Reason - Example - Installation
fetchUserId().then { id in
print("UserID : \(id)")
}.onError { e in
print("An error occured : \(e)")
}.finally {
print("Everything is Done :)")
}
Swift 2 -> version 1.4.2
Swift 3 -> version 2.0.2
Because async code is hard to write, hard to read, hard to reason about.
A pain to maintain
By using a then keyword that enables you to write aSync code that reads like an English sentence
Async code is now concise, flexible and maintainable β€οΈ
- Based on the popular Promise/Future concept
- Lightweight (1 file ~100lines)
- Pure Swift
- No magic involved
- Strongly Typed
- Chainable
fetchUserId({ id in
fetchUserNameFromId(id, success: { name in
fetchUserFollowStatusFromName(name, success: { isFollowed in
// The three calls in a row succeeded YAY!
reloadList()
}, failure: { error in
// Fetching user ID failed
reloadList()
})
}, failure: { error in
// Fetching user name failed
reloadList()
})
}) { error in
// Fetching user follow status failed
reloadList()
}
πππ#callbackHell
fetchUserId()
.then(fetchUserNameFromId)
.then(fetchUserFollowStatusFromName)
.then(updateFollowStatus)
.onError(showErrorPopup)
.finally(reloadList)
fetchUserId().then { id in
print("UserID : \(id)")
}.onError { e in
print("An error occured : \(e)")
}.finally {
print("Everything is Done :)")
}
If we want this to be maintainable, it should read like an English sentence
We can do this by extracting our blocks into separate functions:
fetchUserId()
.then(printUserID)
.onError(showErrorPopup)
.finally(reloadList)
This is now concise, flexible, maintainable, and it reads like an English sentence <3
Mental sanity saved
// #goodbyeCallbackHell
Wondering what fetchUserId() is?
It is a simple function that returns a strongly typed promise :
func fetchUserId() -> Promise<Int> {
return Promise { resolve, reject in
print("fetching user Id ...")
wait { resolve(1234) }
}
}
Here you would typically replace the dummy wait function by your network request <3
As for then
and onError
, you can also call a progress
block for things like uploading an avatar for example.
uploadAvatar().progress { p in
// Here update progressView for example
}
.then(doSomething)
.onError(showErrorPopup)
.finally(doSomething)
Calling then
starts a promise if it is not already started.
In some cases, we only want to register some code for later.
For instance, in the case of JSON to Swift model parsing, we often want to attach parsing blocks to JSON promises, but without starting them.
In order to do that we need to use registerThen
instead. It's the exact same thing as then
without starting the promise right away.
let fetchUsers:Promise<[User]> = fetchUsersJSON().registerThen(parseUsersJSON)
// Here promise is not launched yet \o/
// later...
fetchUsers.then { users in
// YAY
}
whenAll
calls you back when all the promises passed are fullfiled :
whenAll(fetchUsersA(),fetchUsersB(), fetchUsersC()).then { allUsers in
// All the promises came back
}
Oftetimes we need to return a rejecting promise as such :
return Promise { _, reject in
reject(anError)
}
This can be written with the following shortcut :
return Promise.reject(error:anError)
pod 'thenPromise'
use_frameworks!
github "freshOS/then"
Simply Copy and Paste Promise.swift in your Xcode Project :) https://github.com/freshOS/then/blob/master/Code/then/Promise.swift
Grab this repository and build the Framework target on the example project. Then Link against this framework.
S4cha, YannickDot, Damien, piterlouis
then is part of a series of lightweight libraries aiming to make developing iOS Apps a breeze: