Google Promises is a rather recent player among Swift frameworks for async programming using futures and promises. You can add it as a dependency to your project using Swift Package Manager, CocoaPods, or Carthage. If you use Swift Package Manager, you can add the Promises dependency by including it in your Package.swift file, as in the following example:
let package = Package(
// ...
dependencies: [
.package(url: "https://github.com/google/promises.git", from: "1.2.3"),
],
// ...
)
If you use CocoaPods, just add the following to your Podfile:
pod 'PromisesSwift', '~> 1.2.3'
Then, refresh your dependencies by running the following:
pod install
Similarly, for Carthage, add the following to your Cartfile:
github "google/promises"
Then, run this command:
carthage update
Once you have added the Promises framework dependency to your project, you can import the Promises module into your Swift source files as follows:
import Promises
Let's reimplement the same example as used earlier using Google Promises. Contrary to PromiseKit, since the Google Promises framework does not provide a promise-based async wrapper for Swift Foundation APIs, the first bit of code we'll need is such a wrapper for URLSession.dataTask. Let's call it promisedData:
func promisedData(_ url: URL) -> Promise<Data> {
let urlRequest = URLRequest(url: url)
let defaultSession = URLSession(configuration: .default)
return wrap { (handler: @escaping (Data, Error?) -> Void) in
let task = defaultSession.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
handler(data, error)
} else {
handler(Data(), error)
}
}
task.resume()
}
}
As you can see, all the heavy lifting is done by the Promises' wrap function, and we just have to provide a proper callback adapter. Once that function is available, we can set out to use GitHub REST API to retrieve the most starred repository first, followed by the most forked one:
func testGitHubAPI () {
if var urlComponents = URLComponents(string: "https://api.github.com/search/repositories") {
urlComponents.query = "q=sort=stars;order=desc;per_page=1"
let p : Promise<Data> = promisedData(urlComponents.url!)
p.then { data -> Promise<Data> in
let string = String(data:data, encoding:String.Encoding.utf8)
print("Most starred repo: \(string!)")
urlComponents.query = "q=forks=stars;order=desc;per_page=1"
return promisedData(urlComponents.url!)
}.then { data -> Promise<Data> in
let string = String(data:data, encoding:String.Encoding.utf8)
print("Most forked repo: \(string!)")
return Promise(data)
}.catch { error in
print("This was unexpected: \(error)")
}
}
}
Besides supporting the basic chaining then and catch functions, Google Promises provides a number of other functions that aim to make it easy to write complex asynchronous workflows, including the following:
- all: An all promise is fulfilled when all of the promises passed to its constructor are.
- any: Similar to all, but will be rejected only if all the composing promises are.
- always: An always block that will always be executed in a promise workflow, even if some previous step is rejected.
- await: The await function will wait for a promise to be fulfilled on a different thread. This makes it possible to use promises in a complete sync style.
- recover: Similar to catch, but it will handle an error condition without breaking the workflow.
- retry: Attempts multiple times to fulfill a Promise if it is rejected.
As mentioned, there exist several open source frameworks for promise-based asynchronous operation handling. What you learned in this section will allow you to pick up any of them and get up to speed quickly.