PromisedFuture is a lightweight implementation of Futures/Promises.
PromisedFuture
helps to write readable and comprehensible asynchronous code.
Usually the callback mechanism is used when working with asynchronous tasks. It should get the job done for some use cases, but usually we need to perform more that one asynchronous operation, so we have to nest the second operation inside the completion block of the first one, but when we have nested callbacks it starts to get messy, the code is not friendly in term of maintainability, readability and control, and this leads to the Pyramid of doom, Callback hell and error handling issues.
PromisedFuture is here to rescue, the code will go from this:
APIClient.login(email: "test@gmail.com", password: "myPassword", completion: { result in
switch result {
case .success(let user):
APIClient.userArticles(userID: user.id, completion: { result in
switch result {
case .success(let articles):
APIClient.getArticle(id: articles.last!.id, completion: { result in
switch result {
case .success(let article):
print(article)
case .failure(let error):
print(error)
}
})
case .failure(let error):
print(error)
}
})
case .failure(let error):
print(error)
}
})
to this:
APIClient.login(email: "test@gmail.com", password: "myPassword")
.map({$0.id})
.andThen(APIClient.userArticles)
.map({$0.last!.id})
.andThen(APIClient.getArticle)
.execute(onSuccess: { article in
print(article)
}, onFailure: {error in
print(error)
})
- Chainable asynchronous operations.
- Lightweight and simple to use (just ≈ 40 lines of code).
- Fully unit tested.
- Fully Documented.
- iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
- Xcode 9.0+
- Swift 4.0+
To run the example project, clone the repo, then open the workspace PromisedFuture.xcworkspace
run using iOS Example
scheme.
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate PromisedFuture into your Xcode project using CocoaPods, specify it in your Podfile
:
use_frameworks!
pod 'PromisedFuture'
Then, run the following command:
$ pod install
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following command:
$ brew update
$ brew install carthage
To integrate PromisedFuture into your Xcode project using Carthage, specify it in your Cartfile
:
github "aladinway/PromisedFuture"
Run carthage update
to build the framework and on your application targets’ “General” settings tab, in the “Embedded Binaries” section, drag and drop the built PromisedFuture.framework
from the Carthage/Build folder on disk.
Create a Future
:
to create a Future
using an operation (network call for example) we use:
init(operation: @escaping (_ completion:@escaping Completion) -> Void)
- Parameters:
operation
: the operation that should be performed by the Future. This is usually the asynchronous operation.completion
: the completion block of the operation. It has theResult
of the operation as parameter.
- Example usage:
let future = Future(operation: { completion in
// Your operation here to retrieve the value
Alamofire.request("https://httpbin.org/get")
.responseData { response in
switch response.result {
case .success(let value):
// Then in case of success you call the completion
// with the Result passing the value
completion(.success(data))
case .failure(let error):
// or in case of error call the completion
// with the Result passing the error like :
//completion(.failure(error))
}
}
})
You can also create a Future
by Result
, Value
or Error
.
Initialize a new Future
with the provided Result
:
init(result: Result<Value>)
-
Parameters:
result
: The result of theFuture
. It can be aResult
of success with a value or failure with anError
.
-
Example usage:
let future = Future(result: Result.success(12))
Initialize a new Future
with the provided value:
init(value: Value)
-
Parameters:
value
: The value of theFuture
.
-
Example usage:
let future = Future(value: "Hello")
Initialize a new Future
with the provided Error
:
init(value: Value)
-
Parameters:
value
: The value of theFuture
.
-
Example usage:
let f: Future<Int>= Future(error: NSError(domain: "E", code: 4, userInfo: nil))
Execute a Future
:
To execute the operation of the Future we can use one these methods:
-
func execute(completion: @escaping Completion)
- Parameters:
completion
: the completion block of the operation. It has theResult
of the operation as parameter.
- Parameters:
-
Example usage:
let future = Future(value: 14) future.execute(completion: { result in switch result { case .success(let value): print(value) // it will print 14 case .failure(let error): print(error) } })
-
func execute(completion: @escaping Completion)
- Parameters:
onSuccess
: the success completion block of the operation. It has the value of the operation as parameter.onFailure
: the failure completion block of the operation. It has the error of the operation as parameter.
- Parameters:
-
Example usage:
let future = Future(value: 14) future.execute(onSuccess: { value in print(value) // it will print 14 }, onFailure: { error in print(error) })
Chaining multiple Future
:
The powerful part of the Future is the ability to chain asynchronous operations. We can use andThen
method to chain two depending futures.
-
func andThen<U>(_ f: @escaping (_ value: Value) -> Future<U>) -> Future<U>
- Parameters:
f
: function that will generate a newFuture
by passing the value of this Future.
- Parameters:
-
Example usage:
struct User { id: Int } // Let's assume we need to perform two network operations // The first one to get the user id // And the second one to get the user information // we can use `andThen` to chain them let userIdFuture = Future(value: 14) func userFuture(by userId: Int) -> Future<User> { return Future(value: User(id: userId)) } userIdFuture.andThen(userFuture).execute { user in print(user) }
We can also map the result of the
Future
usingmap
function:func map<T>(_ f: @escaping (_ value: Value) -> T) -> Future<T>
-
Parameters:
f
: function that will generate a newFuture
by passing the value of this Future
-
Example usage:
let stringFuture = Future(value: "http://www.google.com") let urlFuture = stringFuture.map({URL(string: $0)})
I highly recommend reading my article below, If you want to learn more about Futures
and how we can use PromisedFuture in the networking layer with Alamofire
:
Write a Networking Layer in Swift 4 using Alamofire 5 and Codable Part 3: Using Futures/Promises
Alaeddine Messaoudi itechnodev@gmail.com
PromisedFuture is available under the MIT license. See the LICENSE file for more info.