Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOS Conf Singapore October 2015

iOS Conf Singapore October 2015

Swift Sync and Async Error Handling. Showing some of the problems with traditional synchronous and asynchronous error handling in Objective-C, and how we can do better in Swift with Result, some kind of Future API, and comparing it with the new Swift 2 throws syntax.

Avatar for Javier Soto

Javier Soto

October 24, 2015
Tweet

More Decks by Javier Soto

Other Decks in Programming

Transcript

  1. Swift Sync and Async Error Handling iOS Conf Singapore -

    October 2015 — @Javi 1 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  2. Agenda → Traditional Asynchronous Code → Problems with Traditional Error

    Handling → Result vs throws → Modeling asynchronous computation: Future → Making Result and throws play together 2 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  3. Traditional asynchronous code 3 — "Swift Sync and Async Error

    Handling" - Javier Soto. iOS Conf Singapore - October 2015
  4. Traditional asynchronous code struct User { let avatarURL: NSURL }

    func requestUserInfo(userID: String, completion: (User?, NSError?) -> ()) func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) -> ()) func loadAvatar(userID: String, completion: (UIImage?, NSError?) -> ()) { requestUserInfo(userID) { user, error in if let user = user { downloadImage(user.avatarURL) { avatar, error in if let avatar = avatar { completion(avatar, nil) } else { completion(nil, error!) } } } else { completion(nil, error!) } } } 4 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  5. Traditional asynchronous code 5 — "Swift Sync and Async Error

    Handling" - Javier Soto. iOS Conf Singapore - October 2015
  6. Traditional asynchronous code func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) ->

    ()) (.Some, .None) (.None, .Some) (.Some, .Some) (.None, .None) 6 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  7. (.Some, .Some) (.None, .None) 7 — "Swift Sync and Async

    Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  8. Error Handling func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) -> ())

    downloadImage(url) { imageOrNil, errorOrNil in if error = errorOrNil { // What if image is also not nil?? } else if image = imageOrNil { } } 8 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  9. Error Handling var error: NSError? let string = NSString(contentsOfFile:path encoding:NSUTF8Encoding

    error:&error) if string == nil { // Oops: if error.code == NSFileReadNoSuchFileError { // ... } else if ... } 9 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  10. 10 — "Swift Sync and Async Error Handling" - Javier

    Soto. iOS Conf Singapore - October 2015
  11. 11 — "Swift Sync and Async Error Handling" - Javier

    Soto. iOS Conf Singapore - October 2015
  12. 12 — "Swift Sync and Async Error Handling" - Javier

    Soto. iOS Conf Singapore - October 2015
  13. 13 — "Swift Sync and Async Error Handling" - Javier

    Soto. iOS Conf Singapore - October 2015
  14. NSError Alternative protocol ErrorType { } enum UserInfoErrorDomain: ErrorType {

    case UserDoesNotExist case UserRequestFailure(reason: String) case NetworkRequestFailure(underlyingError: NSError) } extension NSError: ErrorType { } 14 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  15. NSError Alternative enum NoError: ErrorType { } let error =

    NoError(?) 15 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  16. Result enum Result<T, E: ErrorType> { case Success(T) case Failure(E)

    } Result by Rob Rix: https://github.com/antitypical/Result 16 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  17. Swift's throws 17 — "Swift Sync and Async Error Handling"

    - Javier Soto. iOS Conf Singapore - October 2015
  18. Swift's throws func readFile(path: String) throws -> NSData 18 —

    "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  19. Swift's throws func readFile(path: String) throws -> NSData // throws...

    what exactly? 19 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  20. Swift's throws func readFile(path: String) throws -> NSData Functionally equivalent

    to func readFile(path: String) -> Result<NSData, ErrorType> 20 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  21. Swift's throws func readFile(path: String) throws -> NSData do {

    let contents = try readFile("path/to/my/file") } catch(SomeTypeOfError) { // How can I know which errors I should match..? } catch { // catch-all block. } 21 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  22. Swift's throws func readFile(path: String) throws -> NSData let contents:

    NSData? = try? readFile("path/to/my/file") 22 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  23. Swift's throws func readFile(path: String) -> Result<NSData, FileIOError> let file

    = readFile("path/to/my/file") switch file { case let .Succcess(value): // ... case let .Failure(error): // This `error` variable has type information! } 23 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  24. Swift's throws Hopefully in a future version of Swift ...

    func readFile(path: String) throws FileIOError -> NSData 24 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  25. Error handling in Asynchronous APIs 25 — "Swift Sync and

    Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  26. Future<T, E> 26 — "Swift Sync and Async Error Handling"

    - Javier Soto. iOS Conf Singapore - October 2015
  27. Futures → Encapsulate a deferred computation. → Treat values that

    incur a delay to be retrieved as if they were regular values. → Allow us to treat errors as first class citizens. → Easily composable. 27 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  28. Future struct Future<T, E: ErrorType> { typealias ResultType = Result<T,

    E> typealias Completion = ResultType -> () typealias AsyncOperation = Completion -> () private let operation: AsyncOperation } 28 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  29. Future struct Future<T, E: ErrorType> { init(operation: AsyncOperation) { self.operation

    = operation } func start(completion: Completion) { self.operation() { result in completion(result) } } } 29 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  30. Future.map(): transforming the computed value 30 — "Swift Sync and

    Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  31. Future.map(): transforming the computed value struct User { let avatarURL:

    NSURL } func requestUserInfo(userID: String) -> Future<User, ErrorDomain> func requestUserAvatarURL(userID: String) -> Future<NSURL, ErrorDomain> { return requestUserInfo(userID) .map { $0.avatarURL } } 31 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  32. Future.map(): transforming the computed value struct Future<T, E: ErrorType> {

    func map<U>(f: T -> U) -> Future<U, E> } 32 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  33. map() in other types struct Array<T> { func map<U>(f: T

    -> U) -> [U] } enum Optional<T> { func map<U>(f: T -> U) -> U? } struct Future<T, E: ErrorType> { func map<U>(f: T -> U) -> Future<U, E> } 33 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  34. Future.map(): transforming the computed value func map<U>(f: T -> U)

    -> Future<U, E> { // Return a new Future w/ a new operation... return Future<U, E>(operation: { completion in }) } 34 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  35. Future.map(): transforming the computed value func map<U>(f: T -> U)

    -> Future<U, E> { return Future<U, E>(operation: { completion in // Retrieve the value from self... self.start { result in } }) } 35 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  36. Future.map(): transforming the computed value func map<U>(f: T -> U)

    -> Future<U, E> { return Future<U, E>(operation: { completion in self.start { result in // Consider .Success and .Failure... switch result { } } }) } 36 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  37. Future.map(): transforming the computed value case let .Success(value): // Call

    completion with the transformed value completion(Result.Success(f(value))) 37 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  38. Future.map(): transforming the computed value case let .Failure(error): // We

    didn't get a value: no transformation completion(Result.Failure(error)) 38 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  39. Future.map(): transforming the computed value func map<U>(f: T -> U)

    -> Future<U, E> { return Future<U, E>(operation: { completion in self.start { result in switch result { case let .Success(value): completion(Result.Success(f(value))) case let .Failure(error): completion(Result.Failure(error)) } } }) } 39 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  40. Future.andThen(): concatenating async work 40 — "Swift Sync and Async

    Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  41. Future.andThen(): concatenating async work func requestUserAvatarURL(userID: String) -> Future<NSURL, ErrorDomain>

    func downloadImage(URL: NSURL) -> Future<UIImage, ErrorDomain> func downloadUserAvatar(userID: String) -> Future<UIImage, ErrorDomain> { return requestUserAvatarURL(userID) .andThen(downloadImage) } 41 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  42. Future.andThen(): concatenating async work struct Future<T, E: ErrorType> { func

    andThen<U>(f: T -> Future<U, E>) -> Future<U, E> } 42 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  43. Future.andThen(): concatenating async work func andThen<U>(f: T -> Future<U, E>)

    -> Future<U, E> { return Future<U, E>(operation: { completion in } } 43 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  44. Future.andThen(): concatenating async work func andThen<U>(f: T -> Future<U, E>)

    -> Future<U, E> { return Future<U, E>(operation: { completion in self.start { firstFutureResult in } }) } 44 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  45. Future.andThen(): concatenating async work func andThen<U>(f: T -> Future<U, E>)

    -> Future<U, E> { return Future<U, E>(operation: { completion in self.start { firstFutureResult in switch firstFutureResult { case let .Success(value): // ... case let .Failure(error): // ... } } }) } 45 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  46. Future.andThen(): concatenating async work case let .Success(value): let nextFuture =

    f(value) 46 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  47. Future.andThen(): concatenating async work case let .Success(value): let nextFuture =

    f(value) nextFuture.start { finalResult in completion(finalResult) } 47 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  48. Future.andThen(): concatenating async work case let .Failure(error): completion(Result.Failure(error)) 48 —

    "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  49. Future.andThen(): concatenating async work func andThen<U>(f: T -> Future<U, E>)

    -> Future<U, E> { return Future<U, E>(operation: { completion in self.start { firstFutureResult in switch firstFutureResult { case let .Success(value): f(value).start(completion) case let .Failure(error): completion(Result.Failure(error)) } } }) } 49 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  50. We can go from this... func loadAvatar(userID: String, completion: (UIImage?,

    NSError?) -> ()) { requestUserInfo(userID) { user, error in if let user = user { downloadImage(user.avatarURL) { avatar, error in if let avatar = avatar { completion(avatar, nil) } else { completion(nil, error) } } } else { completion(nil, error) } } } 50 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  51. ... To this func requestUserInfo(userID: String) -> Future<User, UserInfoErrorDomain> func

    downloadImage(URL: NSURL) -> Future<UIImage, UserInfoErrorDomain> func loadAvatar(userID: String) -> Future<UIImage, UserInfoErrorDomain> { return requestUserInfo(userID) .map { $0.avatarURL } .andThen(downloadImage) } 51 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  52. Mixing it all up Result<T, E> and throws 52 —

    "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  53. Result<T, E> and throws throws => Result extension Result {

    public init(@autoclosure _ f: () throws -> T) { do { self = .Success(try f()) } catch { self = .Failure(error as! Error) } } } 53 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  54. Result<T, E> and throws Result => throws extension Result {

    public func dematerialize() throws -> T { switch self { case .Success(value): return value case let .Failure(error): throw error } } } 54 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  55. Result<T, E> and throws Future => throws let avatarFuture =

    loadAvatar("4815162342") avatarFuture.start { result in do { let avatar = try result.materialize() } catch { // handle `error` } } 55 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  56. Limitations of Futures → Only represent computation to retrieve one

    value. → Can't encapsulate streams of values. → Only consumer (pull) driven, not producer (push) driven. 56 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  57. ReactiveCocoa Signals > Futures 57 — "Swift Sync and Async

    Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  58. Follow-up Resources → ReactiveCocoa: https://github.com/ ReactiveCocoa/ReactiveCocoa → Railway Oriented Programming:

    http:// fsharpforfunandprofit.com/posts/recipe-part2/ 58 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015
  59. Thanks! Questions? 59 — "Swift Sync and Async Error Handling"

    - Javier Soto. iOS Conf Singapore - October 2015