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.
✏️ Usage
Basic chaining operations are:
.thenarranges blocks one after another, passing along any values.recoverrecovers from a thrown error with a backup value (or block to run).catchcatches 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.
💾 Installation
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.
📘 Documentation
Using chains from async or throwing contexts
Motivation and common patterns
Frequently asked questions (FAQ)
🚀 Feedback and Contributing
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.
