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

Swift async tasks: a brief introduction of PromiseKit

Swift async tasks: a brief introduction of PromiseKit

A quick talk at Cocoaheads about PromiseKit: a simple solution to achieve asynchronous processes.

Julien Quéré

January 19, 2018
Tweet

More Decks by Julien Quéré

Other Decks in Technology

Transcript

  1. ▸ Authentication (login + password, then: token) ▸ User profile

    ▸ Friends ▸ Conversations Introducing: Stranger Chat
  2. ▸ Authentication (login + password, then: token) ▸ User profile

    ▸ Friends ▸ Conversations Introducing: Stranger Chat > Hello Dustin Henderson. > You have 3 conversations: - [Lucas Sinclair] Skate with Max ? - [Will Byers] From the Upside Down - [Mike Wheeler] Secret about Eleven
  3. Get conversations Authenticate login password Fetch profile token Fetch friends

    list [Conversation] Fetch conversations User [User] User [Conversation]
  4. Get conversations Authenticate You can choose your friends, but not

    your Webservices. login password Fetch profile token Fetch friends list [Conversation] ! Fetch conversations User [User] User [Conversation]
  5. Fetch methods func fetchSomething(withAnInput input:String, completion:((ReturnType?, Error?)->Void)?) { do {

    let data = try makeNetworkCall() let result = try parseServerResponse(data) completion?(result, nil) } catch { completion?(nil, error) } }
  6. AUTHENTICATE APIManager.shared.fetchToken(withLogin: "dh", password: "dd", completion: { (token, error) in

    guard error == nil else { print("Error:\(error!)") return } guard let token = token else { print("Received empty token ¯\\_(ϑ)_/¯") return } // Here we have the token. Ready for next step }
  7. FETCH PROFILE APIManager.shared.fetchToken(withLogin: "dh", password: "dd", completion: { (token, error)

    in guard error == nil else { print("Error:\(error!)") return } guard let token = token else { print("Received empty token ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchUser(withToken: token, completion:{ (user, error) in guard error == nil else { print("Error:\(error!)") return } guard var user = user else { print("Received nil user ¯\\_(ϑ)_/¯") return } // Here we have the token. Ready for next step }) })
  8. FETCH CONVERSATIONS APIManager.shared.fetchToken(withLogin: "dh", password: "dd", completion: { (token, error)

    in guard error == nil else { print("Error:\(error!)") return } guard let token = token else { print("Received empty token ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchUser(withToken: token, completion:{ (user, error) in guard error == nil else { print("Error:\(error!)") return } guard var user = user else { print("Received nil user ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchConversations(forUserWithId: user.identifier, completion: { (conversations, error) in guard error == nil else { print("Error:\(error!)") return } guard let conversations = conversations else { print("Received nil conversations ¯\\_(ϑ)_/¯") return } // Here we have the conversations. Ready for next step }) }) })
  9. FETCH FRIENDS LIST APIManager.shared.fetchToken(withLogin: "dh", password: "dd", completion: { (token,

    error) in guard error == nil else { print("Error:\(error!)") return } guard let token = token else { print("Received empty token ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchUser(withToken: token, completion:{ (user, error) in guard error == nil else { print("Error:\(error!)") return } guard var user = user else { print("Received nil user ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchConversations(forUserWithId: user.identifier, completion: { (conversations, error) in guard error == nil else { print("Error:\(error!)") return } guard let conversations = conversations else { print("Received nil conversations ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchFriends(forUserWithId: user.identifier, completion: { (friends, error) in guard error == nil else { print("Error:\(error!)") return } guard let friends = friends else { print("Received nil friend list ¯\\_(ϑ)_/¯") return } // Here we have all the friends, conversations and user. // It's OVER ! // PS: If you can read this, raise your hands :) }) }) }) })
  10. ▸ Well known in JS world ▸ An idea of

    1976 … One solution: Promise Pattern A promise is an object that represents an asynchronous task. Pass that object around, and write clean, ordered code; a logical, simple, modular stream of progression from one asynchronous task to another.
  11. Make a promise func fetchSomething(withAnInput input:String, completion:((ReturnType?, Error?)->Void)?) { do

    { let data = try makeNetworkCall() let result = try parseServerResponse(data) completion?(result, nil) } catch { completion?(nil, error) } }
  12. Make a promise func fetchSomething(withAnInput input:String) -> Promise<ReturnType> { do

    { let data = try makeNetworkCall() let result = try parseServerResponse(data) completion?(result, nil) } catch { completion?(nil, error) } }
  13. Make a promise func fetchSomething(withAnInput input:String) -> Promise<ReturnType> { return

    Promise { fullfill, reject in do { let data = try makeNetworkCall() let result = try parseServerResponse(data) completion?(result, nil) } catch { completion?(nil, error) } } }
  14. Make a promise func fetchSomething(withAnInput input:String) -> Promise<ReturnType> { return

    Promise { fullfill, reject in do { let data = try makeNetworkCall() let result = try parseServerResponse(data) completion?(result, nil) } catch { completion?(nil, error) } } } public init(resolvers: (_ fulfill: (T) -> Void, _ reject: (Error) -> Void) throws -> Void)
  15. Make a promise func fetchSomething(withAnInput input:String) -> Promise<ReturnType> { return

    Promise { fullfill, reject in do { let data = try makeNetworkCall() let result = try parseServerResponse(data) fullfill(result) } catch { reject(error) } } }
  16. ▸ then ▸ Call the next promise if the current

    succeed. ▸ when ▸ when(resolved:) - Call the next promise when all the promises have been executed. ▸ when(fullfiled:) - Call the next promise if all the promises succeed. ▸ catch ▸ When any promise of the chain failed. Call it
  17. Call it firstly { APIManager.shared.fetchToken(withLogin: "dh", password: "dd") }.then {

    token in print("Received Token: \(token)") } ▸ firstly ▸ Just a wrapper to make code cleaner
  18. Call it firstly { APIManager.shared.fetchToken(withLogin: "dh", password: "dd") }.then {

    token in APIManager.shared.fetchUser(withToken: token) }.then { user in print("Hello \(user.fullname)") }
  19. Call it firstly { APIManager.shared.fetchToken(withLogin: "dh", password: "dd") }.then {

    token in APIManager.shared.fetchUser(withToken: token) }.then { user -> Promise<([User], [Conversation])> in self.currentUser = user let friendsPromise = APIManager.shared.fetchFriends(forUserWithId: user.identifier) let conversationsPromise = APIManager.shared.fetchConversations(forUserWithId: user.identifier) return when(fulfilled: friendsPromise, conversationsPromise) }
  20. Call it firstly { APIManager.shared.fetchToken(withLogin: "dh", password: "dd") }.then {

    token in APIManager.shared.fetchUser(withToken: token) }.then { user -> Promise<([User], [Conversation])> in self.currentUser = user let friendsPromise = APIManager.shared.fetchFriends(forUserWithId: user.identifier) let conversationsPromise = APIManager.shared.fetchConversations(forUserWithId: user.identifier) return when(fulfilled: friendsPromise, conversationsPromise) }.then { (friends, conversations) -> Void in print("Hello \(self.currentUser!.fullname).") print("You have \(conversations.count) conversations:") for conversation in conversations { let friend = friends.first(where: {$0.identifier == conversation.otherPeerId}) print("[\(friend?.fullname ?? "Unknow")] \(conversation.title)") } }
  21. Call it firstly { APIManager.shared.fetchToken(withLogin: "dh", password: "dd") }.then {

    token in APIManager.shared.fetchUser(withToken: token) }.then { user -> Promise<([User], [Conversation])> in self.currentUser = user let friendsPromise = APIManager.shared.fetchFriends(forUserWithId: user.identifier) let conversationsPromise = APIManager.shared.fetchConversations(forUserWithId: user.identifier) return when(fulfilled: friendsPromise, conversationsPromise) }.then { (friends, conversations) -> Void in print("Hello \(self.currentUser!.fullname).") print("You have \(conversations.count) conversations:") for conversation in conversations { let friend = friends.first(where: {$0.identifier == conversation.otherPeerId}) print("[\(friend?.fullname ?? "Unknow")] \(conversation.title)") } }.catch { (error) in print("We have an error, another failed experiment :( \(error)") }
  22. A GENTLE REMINDER APIManager.shared.fetchToken(withLogin: "dh", password: "dd", completion: { (token,

    error) in guard error == nil else { print("Error:\(error!)") return } guard let token = token else { print("Received empty token ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchUser(withToken: token, completion:{ (user, error) in guard error == nil else { print("Error:\(error!)") return } guard var user = user else { print("Received nil user ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchConversations(forUserWithId: user.identifier, completion: { (conversations, error) in guard error == nil else { print("Error:\(error!)") return } guard let conversations = conversations else { print("Received nil conversations ¯\\_(ϑ)_/¯") return } APIManager.shared.fetchFriends(forUserWithId: user.identifier, completion: { (friends, error) in guard error == nil else { print("Error:\(error!)") return } guard let friends = friends else { print("Received nil friend list ¯\\_(ϑ)_/¯") return } // Here we have all the friends, conversations and user. // It's OVER ! // PS: If you can read this, raise your hands :) }) }) }) })
  23. Frameworks integration AddressBook CloudKit CoreBluetooth QuartzCore Social UIKit AssetsLibrary MessagesUI

    AVFoundation EventKit Photos WatchConnectivity Accounts StoreKit Foundation EventKit SystemConfiguration CoreLocation Alamofire OMGHTTPURLRQ Bolts
  24. Frameworks integration AddressBook CloudKit CoreBluetooth QuartzCore Social UIKit AssetsLibrary MessagesUI

    AVFoundation EventKit Photos WatchConnectivity Accounts StoreKit Foundation EventKit SystemConfiguration CoreLocation Alamofire OMGHTTPURLRQ Bolts
  25. CoreLocation firstly { CLLocationManager.promise() }.then { location in CLGeocoder().reverseGeocode(location:location) }.then

    { placemark in print("I know where you live ... \(placemark.locality)") }.catch { (error) in print("I did not find you due to this error: \(error)") }
  26. ▸ Khanlou Promise - https://github.com/khanlou/Promise ▸ Then - https://github.com/freshOS/then ▸

    Hydra - https://github.com/malcommac/Hydra ▸ BrightFutures - https://github.com/Thomvis/BrightFutures Alternatives