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.

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

    View Slide

  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

    View Slide

  3. Traditional asynchronous code
    3 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  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

    View Slide

  5. Traditional asynchronous code
    5 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  15. NSError Alternative
    enum NoError: ErrorType { }
    let error = NoError(?)
    15 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  16. Result
    enum Result {
    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

    View Slide

  17. Swift's throws
    17 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  23. Swift's throws
    func readFile(path: String) -> Result
    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

    View Slide

  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

    View Slide

  25. Error handling in
    Asynchronous APIs
    25 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

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

    View Slide

  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

    View Slide

  28. Future
    struct Future {
    typealias ResultType = Result
    typealias Completion = ResultType -> ()
    typealias AsyncOperation = Completion -> ()
    private let operation: AsyncOperation
    }
    28 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  29. Future
    struct Future {
    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

    View Slide

  30. Future.map(): transforming the computed value
    30 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

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

    View Slide

  32. Future.map(): transforming the computed value
    struct Future {
    func map(f: T -> U) -> Future
    }
    32 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  33. map() in other types
    struct Array {
    func map(f: T -> U) -> [U]
    }
    enum Optional {
    func map(f: T -> U) -> U?
    }
    struct Future {
    func map(f: T -> U) -> Future
    }
    33 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

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

    View Slide

  35. Future.map(): transforming the computed value
    func map(f: T -> U) -> Future {
    return Future(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

    View Slide

  36. Future.map(): transforming the computed value
    func map(f: T -> U) -> Future {
    return Future(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

    View Slide

  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

    View Slide

  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

    View Slide

  39. Future.map(): transforming the computed value
    func map(f: T -> U) -> Future {
    return Future(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

    View Slide

  40. Future.andThen(): concatenating async work
    40 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. Future.andThen(): concatenating async work
    func andThen(f: T -> Future) -> Future {
    return Future(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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  49. Future.andThen(): concatenating async work
    func andThen(f: T -> Future) -> Future {
    return Future(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

    View Slide

  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

    View Slide

  51. ... To this
    func requestUserInfo(userID: String) -> Future
    func downloadImage(URL: NSURL) -> Future
    func loadAvatar(userID: String) -> Future {
    return requestUserInfo(userID)
    .map { $0.avatarURL }
    .andThen(downloadImage)
    }
    51 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  52. Mixing it all up
    Result and throws
    52 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  53. Result 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

    View Slide

  54. Result 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

    View Slide

  55. Result 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

    View Slide

  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

    View Slide

  57. ReactiveCocoa
    Signals > Futures
    57 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide

  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

    View Slide

  59. Thanks!
    Questions?
    59 — "Swift Sync and Async Error Handling" - Javier Soto. iOS Conf Singapore - October 2015

    View Slide