Slide 1

Slide 1 text

Swi! 2 Error Handling

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

public struct Account { public enum Error : ErrorType { case TransactionExceedsFunds case NonPositiveTransactionNotAllowed(amount: Int) } var fund: Int public mutating func withdraw(amount: Int) throws { guard amount < fund else { throw Error.TransactionExceedsFunds } guard amount > 0 else { throw Error.NonPositiveTransactionNotAllowed(amount: amount) } fund -= amount } }

Slide 5

Slide 5 text

enum Error : ErrorType { case NoArgumentGiven case ArgumentIsNotAnInteger } var arguments = Process.arguments // Remove the executable name. assert(arguments.count >= 1) arguments.removeAtIndex(0) guard let firstArg = arguments.first else { throw Error.NoArgumentGiven } guard let amount = Int(firstArg) else { throw Error.ArgumentIsNotAnInteger }

Slide 6

Slide 6 text

let dict = NSMutableDictionary(contentsOfFile: "account.plist") ?? NSMutableDictionary() let fund = dict["fund"] as? Int ?? 100 var account = Account(fund: fund) do { try account.withdraw(amount) dict["fund"] = account.fund dict.writeToFile("account.plist", atomically: true) } catch Account.Error.NonPositiveTransactionNotAllowed(let amount) { print("Amount (\(amount)) must be positive.") } catch Account.Error.TransactionExceedsFunds { print("Amount exceeds account balance (\(account.fund)).") } catch { print("An unexpected error <\(error)> occured.") }

Slide 7

Slide 7 text

Throwing, NonThrowing and ReThrowing

Slide 8

Slide 8 text

Take a Look into the Book

Slide 9

Slide 9 text

A throwing method can’t override a nonthrowing method, and a throwing method can’t satisfy a protocol requirement for a nonthrowing method. …

Slide 10

Slide 10 text

… That said, a nonthrowing method can override a throwing method, and a nonthrowing method can satisfy a protocol requirement for a throwing.

Slide 11

Slide 11 text

Non Throwing ⊂ Throwing

Slide 12

Slide 12 text

Model protocol ThrowingFunction { typealias ParameterType typealias ReturnType func call(parameter: ParameterType) throws -> ReturnType } protocol NonThrowingFunction : ThrowingFunction { func call(parameter: ParameterType) -> ReturnType }

Slide 13

Slide 13 text

How does rethrows fits in?

Slide 14

Slide 14 text

rethrows is the shapeshifter

Slide 15

Slide 15 text

as throwing let str: String var x = 0 do { try parse(str) { (char) in if char == "+" { x += 1 } else { throw Error.UnexpectedChar } } } catch error { print("Error occured while parsing: \(error)") }

Slide 16

Slide 16 text

as nonthrowing var x = 0 parse(str) { (char) in if char == "+" { x += 1 } }

Slide 17

Slide 17 text

Limits You can just try the closure. Trying something else is forbidden. !

Slide 18

Slide 18 text

ErrorType demystified

Slide 19

Slide 19 text

// from SwiftExP public enum Error : ErrorType { case UnexpectedEOS case IllegalNumberFormat(numberString: String) case IllegalEscapeSequence(escapeSequence: String) case NonTerminatedList case NonTerminatedQuotedString case IllegalHexCharacter(char: Character) }

Slide 20

Slide 20 text

protocol ErrorType { }

Slide 21

Slide 21 text

(lldb) type lookup ErrorType protocol ErrorType { var _domain: Swift.String { get } var _code: Swift.Int { get } }

Slide 22

Slide 22 text

Implements some Protocols by Default · Hashable · Equatable

Slide 23

Slide 23 text

struct Error : ErrorType { // Not necessary since Xcode 7b5, // but let you avoid to write underscores. var code: Int var domain: String var _code: Int { return code } var _domain: String { return domain } }

Slide 24

Slide 24 text

Swift's Error Throwing under the hood

Slide 25

Slide 25 text

Swift's Error Throwing under the hood · Breaks the control flow · Syntax sugar around NSError pointers · Isn't as expensive as Objective-C exceptions

Slide 26

Slide 26 text

class Object { } enum Error : ErrorType { case HelloWorld } func foo(x: Int) throws -> Object { let obj = Object() if x == 0 { throw Error.HelloWorld } return obj } try foo(0)

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Let's speak about Tests

Slide 29

Slide 29 text

XCTest brings no support.

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

! CatchingFire github.com/mrackwitz/CatchingFire

Slide 32

Slide 32 text

enum BombError: ErrorType { case WrongWire case TimeElapsed } let ! = BombError.TimeElapsed let " = 11 let # = 12 func $(time: Int) throws { if time >= # { throw ! } } class ExampleTests : XCTestCase { func testDoNotDisarm() { AssertNoThrow(try $(")) AssertThrows(!, try $(#)) } }

Slide 33

Slide 33 text

Swift's Builtin Error Throwing VS. Result type

Slide 34

Slide 34 text

Integration with Standard Libraries 1 : 0 NSErrorPointer is vanished from the stdlib.

Slide 35

Slide 35 text

Async 1 : 1 The try-catch model actively breaks the current control flow. But Result values can be passed around arbitrarily.

Slide 36

Slide 36 text

Protocol-oriented Programming 2 : 2 A non-throwing method can fulfill a throwing protocol requirement. A Result type can be generically parameterized with a stub type NoError.

Slide 37

Slide 37 text

Code Smells

Slide 38

Slide 38 text

do { let object = try NSJSONSerialization.JSONObjectWithData(data, options: []) } catch { // should never happen }

Slide 39

Slide 39 text

Better use try! let object : AnyObject = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])

Slide 40

Slide 40 text

But: try! is as bad as force unwrapping optionals

Slide 41

Slide 41 text

Takeaways · Avoid try!, but prefer it instead of empty catch blocks. · Catch Foundation errors by pattern matches with NSCocoaError constructors · File a Radar if the error is not exposed / there is no obvious adequate

Slide 42

Slide 42 text

Give it a try

Slide 43

Slide 43 text

for question in questions { question.ask()! }

Slide 44

Slide 44 text

Thanks for your attention! @mrackwitz [email protected]