What's new in Swift 2

What's new in Swift 2

In my study of the Swift language, I've done some research on the development and evolution of the language.
In this series of post, I will recap the innovations in the language starting with Swift 2.

Error Handling

When throwing an error, you can provide an object, that must conform to the ErrorType protocol (it has been renamed to Error in Swift 3, so I will use this last name). The best is to define an Enum that conforms to this protocol, eventually using associated values.

enum CoinWalletError: Error {
  case ExceedsMaximumBalance
  case InsufficientFunds(shortAmount: Int)
}

When calling a function that throws an error, you can choose to handle the error, or propagate the error back to the caller.
You throw an error with:

throw CoinWalletError.ExceedsMaximumBalance
throw CoinWalletError.InsufficientFunds(shortAmount: amount - balance)

In a method, you can choose to handle the error (see later) or let the caller handle it. In this case, you must mark the error with throws:

func deposit(amount: Int) throws {
  ...
  throw CoinWalletError.ExceedsMaximumBalance
  ...
}

func makePurchase(amount: Int) throws {
  ...
  throw CoinWalletError.InsufficientFunds(shortAmount: amount - balance)
  ...
}

In the caller, you can ignore the error if you are sure that it is never thrown:

func starterWallet() -> CoinWallet {
  ...
  try! myWallet.deposit(amount: 300)
  ...
}

Or, you can handle the errors with do / try / catch:

do {
  try wallet.deposit(amount: 500)
  try wallet.makePurchase(amount: 2500)
} catch CoinWalletError.ExceedsMaximumBalance {
  print ("You cannot deposit that many coins!")
} catch CoinWalletError.InsufficientFunds(let shortAmount) {
  print("You cannot buy because you miss \(shortAmount) coins")
}

Here the full example:

enum CoinWalletError: Error {
	case ExceedsMaximumBalance
	case InsufficientFunds(shortAmount: Int)
}

class CoinWallet: CustomStringConvertible {
	private let maximumBalance = 1000
	private(set) var balance: Int = 0
	
	var description: String {
		return "CoinWallet with \(balance) balance"
	}
	
	func deposit(amount: Int) throws {
		if balance + amount > maximumBalance {
			throw CoinWalletError.ExceedsMaximumBalance
		}
		balance += amount
	}
	
	func makePurchase(amount: Int) throws {
		if amount > balance {
			throw CoinWalletError.InsufficientFunds(shortAmount: amount - balance)
		}
		balance -= amount
	}
}

func starterWallet() -> CoinWallet {
	let myWallet = CoinWallet()
	try! myWallet.deposit(amount: 300)
	return myWallet
}

let wallet = starterWallet()
do {
	try wallet.deposit(amount: 500)
	try wallet.makePurchase(amount: 2500)
} catch CoinWalletError.ExceedsMaximumBalance {
	print ("You cannot deposit that many coins!")
} catch CoinWalletError.InsufficientFunds(let shortAmount) {
	print("You cannot buy because you miss \(shortAmount) coins")
}

Scope: defer and guard

Defer is a keyword used to free resources at the end of the current scope.
In case there are multiple defers, they are executed in the opposite order how how they are added. So you should use defer as close as possible to the action you want to finalize.
For example:

func openFile() {
	print ("Opening file")
}
func closeFile() {
	print("Closing file")
}
func startPortListener(_ port: Int)
{
	print ("Starting listening on port \(port)")
}
func stopPortListener(_ port: Int)
{
	print ("Stopping listening on port \(port)")
}

func myFunc() {
	print ("Beginning of myFunc")
	
	openFile()
	defer {
		closeFile()
	}
	
	startPortListener(42)
	defer {
		stopPortListener(42)
	}
	
	print("Ending of myFunc")
}

myFunc()
// >> Beginning of myFunc
// >> Opening file
// >> Starting listening on port 42
// >> Ending of myFunc
// >> Stopping listening on port 42
// >> Closing file

Guard is an if written in the opposite way: you want to express the condition you want to satisfy, otherwise you deal with the error immediately.
This is even more convenient with optionals, because you can deal immediately with the nil case, and deal with the good case in the continuation of the method.
For example:

func testNoChild(age: Int) {
	if age < 13 {
		print("Children not allowed here!")
		return
	}
	guard age >= 13 else {
		print("Children not allowed here!")
		return
	}
}

func testOptional(count: Int?) {
	if let count = count {
		// count is now unwrapped!
	}
    // Handle the error here...
	
	guard let count = count else {
        // Handle the error here...
		return
	}
	// count is now unwrapped!
}

Option Sets

To perform bitwise operations, you can use the OptionSetType (later renamed to OptionSet), that allows to write efficient and at the same time readable code:

struct Inventory: OptionSet {
	let rawValue: Int
	static let Sword = Inventory(rawValue: 1)
	static let Helmet = Inventory(rawValue: 1 << 1)
	static let Shield = Inventory(rawValue: 1 << 2)
}

var inventory: Inventory = [.Sword, .Shield]
if inventory.contains(.Shield) {
	print("has shield")
	inventory.subtract(.Shield)
} else {
	inventory.formUnion(.Helmet)
}

Pattern Matching

Pattern matching gets extended in Swift 2 to simplify tests where we need only a case (if) instead of multiple cases (switch).
If also combines this with associated value and relative tests:

if case .iOS(let language) = platform where language == "Swift" {
	// Swift on iOS!
}

This can be also combined with for loops, avoiding to write a for, then a switch and finally a if:

for case .Example(let value) in collection where value > 10 {
	...
}

Protocol Extensions

With protocol extensions, you can write the extension for a protocol once and reuse it multiple times, thanks to default implementations.
For example, instead of extending the Array, it's more convenient to extend Collection, so all the types conforming to it will benefit from the new method (so also Sets and Dictionaries).

extension Collection {
	func shuffle() -> [Iterator.Element] {
		var result: [Iterator.Element] = []
		for currItem in self {
			if result.count == 0 {
				result.append(currItem)
			} else {
				let random = arc4random_uniform(UInt32(result.count+1))+0
				result.insert(currItem, at:Int(random))
			}
		}
		return result
	}
}

var monsters = ["Godzilla", "Rodan", "Space Godzilla", "Gigan", "Jet Jaguar"]
monsters.shuffle()

var set = Set(["Tokyo", "New York City", "Paris"])
set.shuffle()

When defining your extensions, it's also possible to specify constraints on what types the extension is valid:

extension Collection where Self.Iterator.Element: Comparable {
	func largest() -> Self.Iterator.Element {
		...
	}
}

The biggest advantage of protocol extensions is that it allows you to extend structs, that don't support inheritance natively.