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.

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

    View Slide

  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

    View Slide

  3. Traditional asynchronous code
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 3

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

    View Slide

  5. Traditional asynchronous code
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 5

    View Slide

  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

    View Slide

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

    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 {
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 8

    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 ...
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 9

    View Slide

  10. "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 10

    View Slide

  11. "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 11

    View Slide

  12. "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 12

    View Slide

  13. 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

    View Slide

  14. NSError Alternative
    enum NoError: ErrorType { }
    let error = NoError(?)
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 14

    View Slide

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

    View Slide

  16. Swift's throws
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 16

    View Slide

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

    View Slide

  18. 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

    View Slide

  19. Swift's throws
    func readFile(path: String) throws -> NSData
    Functionally equivalent to
    func readFile(path: String) -> Result
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 19

    View Slide

  20. 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

    View Slide

  21. 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!
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 21

    View Slide

  22. 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

    View Slide

  23. Error handling in
    Asynchronous APIs
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 23

    View Slide

  24. Future
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 24

    View Slide

  25. 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

    View Slide

  26. Future
    struct Future {
    typealias ResultType = Result
    typealias Completion = ResultType -> ()
    typealias AsyncOperation = Completion -> ()
    private let operation: AsyncOperation
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 26

    View Slide

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

    View Slide

  28. Future.map(): transforming the computed value
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 28

    View Slide

  29. 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 }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 29

    View Slide

  30. Future.map(): transforming the computed value
    struct Future {
    func map(f: T -> U) -> Future
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 30

    View Slide

  31. 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
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 31

    View Slide

  32. 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
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 32

    View Slide

  33. 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
    }
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 33

    View Slide

  34. 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 {
    }
    }
    })
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 34

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. 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))
    }
    }
    })
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 37

    View Slide

  38. Future.andThen(): concatenating async work
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 38

    View Slide

  39. 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)
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 39

    View Slide

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

    View Slide

  41. Future.andThen(): concatenating async work
    func andThen(f: T -> Future) -> Future {
    return Future(operation: { completion in
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 41

    View Slide

  42. Future.andThen(): concatenating async work
    func andThen(f: T -> Future) -> Future {
    return Future(operation: { completion in
    self.start { firstFutureResult in
    }
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 42

    View Slide

  43. 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): // ...
    }
    }
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 43

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

  47. 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))
    }
    }
    })
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 47

    View Slide

  48. 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

    View Slide

  49. ... 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)
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 49

    View Slide

  50. Mixing it all up
    Result and throws
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 50

    View Slide

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

    View Slide

  52. 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
    }
    }
    }
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 52

    View Slide

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

    View Slide

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

    View Slide

  55. ReactiveCocoa
    Signals > Futures
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 55

    View Slide

  56. 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

    View Slide

  57. Thanks!
    Questions?
    "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 57

    View Slide