What's New In Swift 2.0

I recently presented at VanSwift about what is new in Swift 2.0. Here are my slides:

What’s New

There are 3 major features in Swift 2.0 that are new:

  1. Protocol Improvements
  2. Error Handling
  3. Control Flow

What Are Protocols?

A protocol defines an interface of methods, properties, and other requirements for a piece of functionality.

protocol SomeType {
  var someVar: Int { get set }
  static var someTypeVar: Int 
  
  func someFunc()
  static func someTypeFunc()
}

The protocol doesn’t actually provide an implementation for any of these requirements—it only describes what an implementation will look like. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

Protocols can require that conforming types have specific instance properties, instance methods, type methods, operators, and subscripts.

That is the basics of protocols, but protocols have additional features:

The mutating keywords signifies that the inner state of the type may change. That means the type must be instantiated with a var rather than a let to call that function.

A protocol can define how a type must be instantiated by declaring an init method or a required init method.

The last major thing to note that methods in pure Swift protocols must always be implemented. To get optional methods, the protocol must be marked as @objc. This is for compatability with optional methods in Objective-C protocols.

What’s New in Protocols

Protocol Extensions allow protocols to contain method implementations, not just declarations. Protocol Extensions may also have type constraints:

extension CollectionType where Self.Generator.Element: IntegerArithmeticType {
  var average: Self.Generator.Element {
    var accumulate = 0
    var count = 0
    for c in self {
      accumulate += c
      count++
    }
    return count == 0 ? 0 : accumulate/count
  }
}

The above code snippet implements an average function when the conforming type is of type IntegerArithmeticType. This saves you from implementing average for integers, unsigned integers, integers of different size, etc.

Swift 2.0 Error Model

The new error handling mechanism in Swift 2.0 looks like exception handling, but it can really be thought of as syntactic sugar around return values.

The old Objective-C/Cocoa way to return an error looked like:

NSError *error;
BOOL success = [object functionMayFailWithError:&error];
if (success == NO) {
  // Check the error variable
}

Error handling was optional. How many times have you seen a nil passed in as the NSError object?

In Swift, it is mandatory to handle errors:

do {
  try object.firstFunctionThatMayFail()  // the try is mandatory
  try object.secondFunctionThatMayFail() 
} catch {

}

Functions that may throw must be wrapped in a do block. And the function call must be prepended with a try. This is NOT optional. The compiler will complain if you do not do this.

Representing Errors

The way to represent an error is to conform to the empty ErrorType protocol.

enum HTTPError : ErrorType {
    case BadRequest = 400
    case Unauthorized = 401
    case Forbidden = 403
    case NotFound = 404
}

And when you “throw” an error, you must mark your function as throws. I’ve put scare quotes around “throw” because it can be thought of as syntactic sugar around returning an NSError.

struct WeirdError : ErrorType {
  let error: String
}

func functionThatMayThrow() throws -> Int {
  if someStateIsWeird {
    throw WeirdError
  }
  return 0
}

And the way you catch an error looks like this:

do {
  try functionThatMayThrow()
} catch WeirdError {
  print("Weird Error")
}

Catching Errors

There are a few things to note about catching errors. catch patterns are very much like patterns for the switch statement.

Either a catch must be exhaustive, it must catch all possible errors, or the catching function must be marked as throws to propogate the error up the call stack.

An empty catch pattern catches all errors.

Error propogation can be disabled with a force try:

try! functionThatMayThrow()

But note that this will result in an runtime exception if the function throws.

Control Flow: defer and guard

The new defer keyword guarantees that a piece of code will be executed at the end of a scope block:

func readFile(filename: String) throws {
  let file = open(filename)
  defer {
    close(file)
  }
  while let line = readline(file) {
    ...
  }
  // defer is executed here
}

If there are multiple defer blocks in a scope, they will be executed in reverse order. Also, a defer block may NOT call break or return. This is because it is ambiguous where the defer block would be returning to.

The new guard keyword is like an if not:

func printName(name: String?) {
  guard let name = name else {
    return
  }
  print("Hello \(name)")
}

The optional bindings in a guard are available for the rest of the scope block. And guard works the opposite of defer. A guard block MUST call return, break, or continue.

defer and guard are kind of like chocolate and peanut butter. When you see one, you will probably see the other, too:

func readFile(filename: String) {
  guard let file = open(filename) else {
    return
  }
  defer {
    close(file)
  }
  
  while let line = readline(file) {
    ...
  }
}

Conclusion

Those are the major features that were added to Swift 2.0. There are a bunch of other features that I have not covered. And of course there were also bug fixes to the language and to the tools.