Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures. Have the best of both worlds: you can use the async solution built into the language, but keep all the useful features of promises.
Basic chaining operations are:
.then
arranges blocks one after another, passing along any values.recover
recovers from a thrown error with a backup value (or block to run).catch
catches any errors (and allows you to throw new ones for later catch blocks)attempt { ... }
kicks off a chain as in the example below:
attempt {
return try await getThing()
}.recover {
error in
return try await backupGetThing(error)
}.then {
thing in
await thing.doYour()
}.catch {
error in
alert(error)
}
For comparison, if we tried to write the above flow without Async+ we'd get something like this:
Task.init {
do {
let thing: Thing
do {
thing = try await getThing()
} catch {
thing = try await backupGetThing(error)
}
await thing.doYour()
} catch {
error in
alert(error)
}
}
Async+ allows async and/or throwing code to remain unnested, modular, and concise. For a full list of operations see the documentation.
Want to still use chained code within a do
/catch
block, Task.init
, or similar context? Easy: chains are fully interoperable with async and/or throwing contexts via the operations .async()
, and .asyncThrows()
at the end of the chain, for example:
let foo = await attempt{ ... }.then{ ... }.async() // non-throwing chain
let foo = try await attempt{ ... }.then{ ... }.asyncThrows() // throwing chain
If the chain doesn't throw you will not be able to call asyncThrows
on it (it is a Guarantee<T>
type rather than a Promise<T>
type), and vice versa. Similarly, chains with potential for uncaught errors will raise an unused value warning at compilation time.
Async+ can be installed with either SwiftPM or CocoaPods.
For SwiftPM, in Xcode go to <your project> -> <ProjectName> -> Package Dependencies -> "+"
and enter: https://github.com/async-plus/async-plus.git
Or modify your Package.swift
file:
dependencies: [
.Package(url: "https://github.com/async-plus/async-plus.git", majorVersion: 1, minor: 1),
]
For CocoaPods, in your Podfile:
target "Change Me!" do
pod "AsyncPlus", "~> 1.1"
end
To use Async+ in a Swift file you must import AsyncPlus
at the top of the file.
Using chains from async or throwing contexts
Motivation and common patterns
Frequently asked questions (FAQ)
This package is in its initial release: please provide feedback and suggestions in order to help shape the API, either by submitting an issue on Github or sending a message on Discord.
Special thanks to the developers of PromiseKit and mxcl for inspiring this work and promoting its development.