swift-async-algorithms
Swift Async Algorithms is an open-source package of asynchronous sequence and advanced algorithms that involve concurrency, along with their related types.
This package has three main goals:
- First-class integration with
async/await
- Provide a home for time-based algorithms
- Be cross-platform and open source
Motivation
AsyncAlgorithms is a package for algorithms that work with values over time. That includes those primarily about time, like debounce
and throttle
, but also algorithms about order like combineLatest
and merge
. Operations that work with multiple inputs (like zip
does on Sequence
) can be surprisingly complex to implement, with subtle behaviors and many edge cases to consider. A shared package can get these details correct, with extensive testing and documentation, for the benefit of all Swift apps.
The foundation for AsyncAlgorithms was included in Swift 5.5 from AsyncSequence. Swift 5.5 also brings the ability to use a natural for/in
loop with await
to process the values in an AsyncSequence
and Sequence
-equivalent API like map
and filter
. Structured concurrency allows us to write code where intermediate state is simply a local variable, try
can be used directly on functions that throw
, and generally treat the logic for asynchronous code similar to that of synchronous code.
This package is the home for these APIs. Development and API design take place on GitHub and the Swift Forums.
Contents
Combining asynchronous sequences
chain(_:...)
: Concatenates two or more asynchronous sequences with the same element type.combineLatest(_:...)
: Combines two or more asynchronous sequences into an asynchronous sequence producing a tuple of elements from those base asynchronous sequences that updates when any of the base sequences produce a value.merge(_:...)
: Merges two or more asynchronous sequence into a single asynchronous sequence producing the elements of all of the underlying asynchronous sequences.zip(_:...)
: Creates an asynchronous sequence of pairs built out of underlying asynchronous sequences.joined(separator:)
: Concatenated elements of an asynchronous sequence of asynchronous sequences, inserting the given separator between each element.
Creating asynchronous sequences
async
: Create an asynchronous sequence composed from a synchronous sequence.AsyncChannel
: An asynchronous sequence with back pressure sending semantics.AsyncThrowingChannel
: An asynchronous sequence with back pressure sending semantics that can emit failures.
Performance optimized asynchronous iterators
AsyncBufferedByteIterator
: A highly efficient iterator useful for iterating byte sequences derived from asynchronous read functions.
Other useful asynchronous sequences
chunks(...)
andchunked(...)
: Collect values into chunks.compacted()
: Remove nil values from an asynchronous sequence.removeDuplicates()
: Remove sequentially adjacent duplicate values.interspersed(with:)
: Place a value between every two elements of an asynchronous sequence.
Asynchronous Sequences that transact in time
debounce(for:tolerance:clock:)
: Emit values after a quiessence period has been reached.throttle(for:clock:reducing:)
: Ensure a minimum interval has elapsed between events.AsyncTimerSequence
: Emit the value of now at a given interval repeatedly.
Obtaining all values from an asynchronous sequence
RangeReplaceableCollection.init(_:)
: Creates a new instance of a collection containing the elements of an asynchronous sequence.Dictionary.init(uniqueKeysWithValues:)
: Creates a new dictionary from the key-value pairs in the given asynchronous sequence.Dictionary.init(_:uniquingKeysWith:)
: Creates a new dictionary from the key-value pairs in the given asynchronous sequence, using a combining closure to determine the value for any duplicate keys.Dictionary.init(grouping:by:)
: /// Creates a new dictionary whose keys are the groupings returned by the given closure and whose values are arrays of the elements that returned each key.SetAlgebra.init(_:)
: Creates a new set from an asynchronous sequence of items.
Task management
Task.select(_:)
: Determine the first task to complete of a sequence of tasks.
Effects
Each algorithm has specific behavioral effects. For throwing effects these can either be if the sequence throws, does not throw, or rethrows errors. Sendability effects in some asynchronous sequences are conditional whereas others require the composed parts to all be sendable to satisfy a requirement of Sendable
. The effects are listed here.
Adding Swift Algorithms as a Dependency
To use the AsyncAlgorithms
library in a SwiftPM project,
add the following line to the dependencies in your Package.swift
file:
.package(url: "https://github.com/apple/swift-async-algorithms"),
Include "AsyncAlgorithms"
as a dependency for your executable target:
.target(name: "<target>", dependencies: [
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
]),
Finally, add import AsyncAlgorithms
to your source code.
Getting Started
⚠️ Please note that this package currently requires a recent Swift Trunk Development toolchain. More information on how to use custom toolchains with Xcode can be viewed here.
Building/Testing Using Xcode on macOS
- Download the most recent development Xcode toolchain.
- Install the package
- Select the development toolchain in Xcode
- Open the
swift-async-algorithms
package directory in Xcode - Build or Test in Xcode as normal
⚠️ Note: swift test
does not currently work properly with custom toolchains for this package.
Building/Testing on Linux
- Download the most recent development toolchain for your Linux distribution
- Decompress the archive to a path in which the
swift
executable is in the binary search path environment variable ($PATH
) - In the
swift-async-algorithms
directory runswift build
orswift test
accordingly
Building with Swift 5.6
git checkout swift-5.6
- run
swift build
orswift test
accordingly
Source Stability
The Swift Async Algorithms package has a goal of being source stable as soon as possible; version numbers will follow Semantic Versioning. Source breaking changes to public API can only land in a new major version.
The public API of version 1.0 of the swift-async-algorithms
package will consist of non-underscored declarations that are marked public
in the AsyncAlgorithms
module. Interfaces that aren’t part of the public API may continue to change in any release, including patch releases.
Future minor versions of the package may introduce changes to these rules as needed.
We’d like this package to quickly embrace Swift language and toolchain improvements that are relevant to its mandate. Accordingly, from time to time, we expect that new versions of this package will require clients to upgrade to a more recent Swift toolchain release. Requiring a new Swift release will only require a minor version bump.