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

NSMeetup San Francisco July 2015

NSMeetup San Francisco July 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

July 01, 2015
Tweet

More Decks by Javier Soto

Other Decks in Programming

Transcript

  1. Swift Sync and Async Error Handling NSMeetup San Francisco -

    July 2015 — @Javi "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 1
  2. Agenda 4 Traditional Asynchronous Code 4 Problems with Traditional Error

    Handling 4 Result vs throws 4 Future 4 Future.map 4 Future.andThen 4 Making Result and throws play together "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 2
  3. Traditional asynchronous code "Swift Sync and Async Error Handling" -

    Javier Soto. NSMeetup San Francisco - July 2015 3
  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!) } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 4
  5. Traditional asynchronous code "Swift Sync and Async Error Handling" -

    Javier Soto. NSMeetup San Francisco - July 2015 5
  6. Traditional asynchronous code func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) ->

    ()) (.Some, .None) (.None, .Some) (.Some, .Some) (.None, .None) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 6
  7. (.Some, .Some) (.None, .None) "Swift Sync and Async Error Handling"

    - Javier Soto. NSMeetup San Francisco - July 2015 7
  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 { } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 8
  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 ... } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 9
  10. NSError Alternative protocol ErrorType { } enum UserInfoErrorDomain: ErrorType {

    case UserDoesNotExist case UserRequestFailure(reason: String) case NetworkRequestFailure(reason: String) } extension NSError: ErrorType { } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 13
  11. NSError Alternative enum NoError: ErrorType { } let error =

    NoError(?) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 14
  12. Result enum Result<T, E: ErrorType> { case Success(T) case Failure(E)

    } Result by Rob Rix: https://github.com/antitypical/Result "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 15
  13. Swift's throws "Swift Sync and Async Error Handling" - Javier

    Soto. NSMeetup San Francisco - July 2015 16
  14. Swift's throws func readFile(path: String) throws -> NSData "Swift Sync

    and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 17
  15. Swift's throws func readFile(path: String) throws -> NSData // throws...

    what exactly? "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 18
  16. Swift's throws func readFile(path: String) throws -> NSData Functionally equivalent

    to func readFile(path: String) -> Result<NSData, ErrorType> "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 19
  17. 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. } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 20
  18. Swift's throws func readFile(path: String) -> Result<NSData, FoundationError> let file

    = readFile("path/to/my/file") switch file { case let .Succcess(value): // ... case let .Failure(error): // This `error` variable has type information! } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 21
  19. Swift's throws Hopefully in a future beta... func readFile(path: String)

    throws FileIOError -> NSData "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 22
  20. Error handling in Asynchronous APIs "Swift Sync and Async Error

    Handling" - Javier Soto. NSMeetup San Francisco - July 2015 23
  21. Future<T, E> "Swift Sync and Async Error Handling" - Javier

    Soto. NSMeetup San Francisco - July 2015 24
  22. Futures 4 Encapsulate a deferred computation. 4 Treat values that

    incur a delay to be retrieved as if they were regular values. 4 Allow us to treat errors as first class citizens. 4 Easily composable. "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 25
  23. Future struct Future<T, E: ErrorType> { typealias ResultType = Result<T,

    E> typealias Completion = ResultType -> () typealias AsyncOperation = Completion -> () private let operation: AsyncOperation } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 26
  24. Future struct Future<T, E: ErrorType> { init(operation: AsyncOperation) { self.operation

    = operation } func start(completion: Completion) { self.operation() { result in completion(result) } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 27
  25. Future.map(): transforming the computed value "Swift Sync and Async Error

    Handling" - Javier Soto. NSMeetup San Francisco - July 2015 28
  26. 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 } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 29
  27. Future.map(): transforming the computed value struct Future<T, E: ErrorType> {

    func map<U>(f: T -> U) -> Future<U, E> } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 30
  28. 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> } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 31
  29. 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 } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 32
  30. 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 } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 33
  31. 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 { } } }) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 34
  32. Future.map(): transforming the computed value case let .Success(value): // Call

    completion with the transformed value completion(Result.Success(f(value))) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 35
  33. Future.map(): transforming the computed value case let .Failure(error): // We

    didn't get a value: no transformation completion(Result.Failure(error)) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 36
  34. 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)) } } }) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 37
  35. Future.andThen(): concatenating async work "Swift Sync and Async Error Handling"

    - Javier Soto. NSMeetup San Francisco - July 2015 38
  36. 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) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 39
  37. Future.andThen(): concatenating async work struct Future<T, E: ErrorType> { func

    andThen<U>(f: T -> Future<U, E>) -> Future<U, E> } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 40
  38. Future.andThen(): concatenating async work func andThen<U>(f: T -> Future<U, E>)

    -> Future<U, E> { return Future<U, E>(operation: { completion in } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 41
  39. 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 } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 42
  40. 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): // ... } } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 43
  41. Future.andThen(): concatenating async work case let .Success(value): let nextFuture =

    f(value) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 44
  42. Future.andThen(): concatenating async work case let .Success(value): let nextFuture =

    f(value) nextFuture.start { finalResult in completion(finalResult) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 45
  43. Future.andThen(): concatenating async work case let .Failure(error): completion(Result.Failure(error)) "Swift Sync

    and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 46
  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 switch firstFutureResult { case let .Success(value): f(value).start(completion) case let .Failure(error): completion(Result.Failure(error)) } } }) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 47
  45. 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) } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 48
  46. ... 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) } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 49
  47. Mixing it all up Result<T, E> and throws "Swift Sync

    and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 50
  48. 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) } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 51
  49. 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 } } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 52
  50. Result<T, E> and throws Future => throws let avatarFuture =

    loadAvatar("4815162342") avatarFuture.start { result in do { let avatar = result.materialize() } catch { // handle `error` } } "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 53
  51. Limitations of Futures 4 Only represent computation to retrieve one

    value. 4 Can't encapsulate streams of values. 4 Only consumer (pull) driven, not producer (push) driven. "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 54
  52. ReactiveCocoa Signals > Futures "Swift Sync and Async Error Handling"

    - Javier Soto. NSMeetup San Francisco - July 2015 55
  53. Follow-up Resources 4 Playground: https://github.com/JaviSoto/Talks 4 ReactiveCocoa: https://github.com/ReactiveCocoa/ ReactiveCocoa 4

    Railway Oriented Programming: http:// fsharpforfunandprofit.com/posts/recipe-part2/ "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 56
  54. Thanks! Questions? "Swift Sync and Async Error Handling" - Javier

    Soto. NSMeetup San Francisco - July 2015 57