Share
Embed
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
Transcript
57 slides
-
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 -
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 -
Traditional asynchronous code "Swift Sync and Async Error Handling" -
Javier Soto. NSMeetup San Francisco - July 2015 3 -
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 -
Traditional asynchronous code "Swift Sync and Async Error Handling" -
Javier Soto. NSMeetup San Francisco - July 2015 5 -
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 -
(.Some, .Some) (.None, .None) "Swift Sync and Async Error Handling"
- Javier Soto. NSMeetup San Francisco - July 2015 7 -
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 -
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 -
"Swift Sync and Async Error Handling" - Javier Soto. NSMeetup
San Francisco - July 2015 10 -
"Swift Sync and Async Error Handling" - Javier Soto. NSMeetup
San Francisco - July 2015 11 -
"Swift Sync and Async Error Handling" - Javier Soto. NSMeetup
San Francisco - July 2015 12 -
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 -
NSError Alternative enum NoError: ErrorType { } let error =
NoError(?) "Swift Sync and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 14 -
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 -
Swift's throws "Swift Sync and Async Error Handling" - Javier
Soto. NSMeetup San Francisco - July 2015 16 -
Swift's throws func readFile(path: String) throws -> NSData "Swift Sync
and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 17 -
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 -
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 -
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 -
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 -
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 -
Error handling in Asynchronous APIs "Swift Sync and Async Error
Handling" - Javier Soto. NSMeetup San Francisco - July 2015 23 -
Future<T, E> "Swift Sync and Async Error Handling" - Javier
Soto. NSMeetup San Francisco - July 2015 24 -
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 -
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 -
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 -
Future.map(): transforming the computed value "Swift Sync and Async Error
Handling" - Javier Soto. NSMeetup San Francisco - July 2015 28 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Future.andThen(): concatenating async work "Swift Sync and Async Error Handling"
- Javier Soto. NSMeetup San Francisco - July 2015 38 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
... 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 -
Mixing it all up Result<T, E> and throws "Swift Sync
and Async Error Handling" - Javier Soto. NSMeetup San Francisco - July 2015 50 -
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 -
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 -
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 -
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 -
ReactiveCocoa Signals > Futures "Swift Sync and Async Error Handling"
- Javier Soto. NSMeetup San Francisco - July 2015 55 -
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 -
Thanks! Questions? "Swift Sync and Async Error Handling" - Javier
Soto. NSMeetup San Francisco - July 2015 57