Top Related Projects
Commonly used data structures for Swift
Reactive Programming in Swift
Network abstraction layer written in Swift.
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
Elegant HTTP Networking in Swift
A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Quick Overview
Swift Async Algorithms is a Swift package that provides a suite of asynchronous sequence and stream processing algorithms. It extends the Swift standard library's async/await capabilities with powerful tools for working with asynchronous data streams, making it easier to write efficient and expressive asynchronous code.
Pros
- Enhances Swift's async/await ecosystem with advanced algorithms
- Provides a consistent API for working with asynchronous sequences
- Improves code readability and maintainability for complex async operations
- Thoroughly tested and well-documented
Cons
- Requires Swift 5.7 or later, limiting compatibility with older projects
- May have a learning curve for developers new to async programming concepts
- Some algorithms might have performance overhead compared to custom implementations
- Still in development, so API may change in future versions
Code Examples
- Using
zip
to combine two async sequences:
let numbers = AsyncStream { continuation in
for i in 1...5 {
continuation.yield(i)
sleep(1)
}
continuation.finish()
}
let letters = "abcde".async
for try await (number, letter) in zip(numbers, letters) {
print("Number: \(number), Letter: \(letter)")
}
- Debouncing an async sequence:
let taps = AsyncStream { continuation in
// Simulate user taps
continuation.yield(())
continuation.yield(())
sleep(1)
continuation.yield(())
continuation.finish()
}
for try await _ in taps.debounce(for: .seconds(0.5)) {
print("Debounced tap received")
}
- Using
chunks(ofCount:)
to process elements in groups:
let sequence = AsyncStream { continuation in
for i in 1...10 {
continuation.yield(i)
}
continuation.finish()
}
for try await chunk in sequence.chunks(ofCount: 3) {
print("Processing chunk: \(chunk)")
}
Getting Started
To use Swift Async Algorithms in your project:
- Add the package to your
Package.swift
file:
dependencies: [
.package(url: "https://github.com/apple/swift-async-algorithms", from: "0.1.0")
]
- Import the module in your Swift file:
import AsyncAlgorithms
- Start using the async algorithms in your code:
let sequence = AsyncStream { ... }
for try await item in sequence.debounce(for: .seconds(1)) {
// Process debounced items
}
Competitor Comparisons
Commonly used data structures for Swift
Pros of swift-collections
- Focuses on general-purpose data structures, offering a wider range of collection types
- More mature and stable, with a longer development history
- Provides optimized implementations for common data structures like deques and ordered sets
Cons of swift-collections
- Lacks specific support for asynchronous programming patterns
- Does not include algorithms tailored for working with asynchronous sequences
- May require additional code to integrate with async/await workflows
Code Comparison
swift-collections:
import Collections
var deque = Deque<Int>()
deque.append(1)
deque.prepend(0)
let first = deque.popFirst()
swift-async-algorithms:
import AsyncAlgorithms
let numbers = AsyncStream { continuation in
for i in 1...5 {
continuation.yield(i)
try? await Task.sleep(nanoseconds: 1_000_000_000)
}
continuation.finish()
}
let buffered = numbers.buffered(2)
swift-collections provides traditional data structures, while swift-async-algorithms focuses on asynchronous sequence operations. The former is better suited for general-purpose programming, while the latter excels in scenarios involving asynchronous data processing and event streams.
Reactive Programming in Swift
Pros of RxSwift
- Mature ecosystem with extensive documentation and community support
- Rich set of operators for complex data transformations and event handling
- Cross-platform compatibility (iOS, macOS, tvOS, watchOS)
Cons of RxSwift
- Steeper learning curve due to its comprehensive nature
- Potential for memory leaks if not managed properly
- Third-party dependency, not native to Swift
Code Comparison
RxSwift:
Observable.combineLatest(observable1, observable2)
.map { value1, value2 in value1 + value2 }
.subscribe(onNext: { result in
print(result)
})
.disposed(by: disposeBag)
Swift Async Algorithms:
for await (value1, value2) in zip(asyncSequence1, asyncSequence2) {
let result = value1 + value2
print(result)
}
Key Differences
- RxSwift uses a reactive programming paradigm, while Swift Async Algorithms leverages Swift's native concurrency model
- Swift Async Algorithms is part of the Swift standard library, ensuring long-term support and integration
- RxSwift offers more complex operations out-of-the-box, while Swift Async Algorithms focuses on fundamental async operations
Use Cases
- RxSwift: Complex event-driven applications, UI bindings, advanced data streams
- Swift Async Algorithms: Swift-native async programming, simpler async sequences, integration with Swift concurrency
Network abstraction layer written in Swift.
Pros of Moya
- Simplifies network layer abstraction and API integration
- Provides a robust testing framework for network requests
- Offers a plugin architecture for easy customization and extension
Cons of Moya
- Limited to network-related tasks, unlike Swift Async Algorithms' broader scope
- May introduce additional complexity for simple API integrations
- Requires learning Moya-specific concepts and patterns
Code Comparison
Moya example:
let provider = MoyaProvider<MyAPI>()
provider.request(.userProfile) { result in
switch result {
case let .success(response):
let data = response.data
// Handle the response
case let .failure(error):
// Handle the error
}
}
Swift Async Algorithms example:
let numbers = AsyncStream { continuation in
for i in 1...5 {
continuation.yield(i)
sleep(1)
}
continuation.finish()
}
for await number in numbers {
print(number)
}
While Moya focuses on network abstraction, Swift Async Algorithms provides tools for working with asynchronous sequences and streams in a more general context. The choice between the two depends on the specific needs of your project and whether you require network-specific functionality or more general async programming tools.
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
Pros of ReactiveCocoa
- Mature and battle-tested framework with a large community
- Supports multiple programming paradigms (functional, reactive, imperative)
- Extensive documentation and learning resources available
Cons of ReactiveCocoa
- Steeper learning curve for developers new to reactive programming
- Can lead to complex code if not used judiciously
- Requires more setup and boilerplate code compared to Swift Async Algorithms
Code Comparison
ReactiveCocoa:
let searchResults = searchTextField.reactive.continuousTextValues
.throttle(0.3, on: QueueScheduler.main)
.flatMap(.latest) { query in
return API.search(query)
}
Swift Async Algorithms:
for await query in searchTextField.textValues.debounce(for: .seconds(0.3)) {
let results = await API.search(query)
updateUI(with: results)
}
ReactiveCocoa offers a more declarative approach with powerful operators, while Swift Async Algorithms provides a more familiar, imperative style using async/await syntax. The choice between the two depends on project requirements, team expertise, and personal preference.
Elegant HTTP Networking in Swift
Pros of Alamofire
- Comprehensive networking library with a wide range of features
- Well-established and mature project with extensive community support
- Simplified syntax for common networking tasks
Cons of Alamofire
- Larger dependency footprint compared to Swift Async Algorithms
- May introduce unnecessary complexity for simple networking tasks
- Less focused on asynchronous programming patterns
Code Comparison
Swift Async Algorithms:
let numbers = AsyncStream { continuation in
Task {
for i in 1...5 {
continuation.yield(i)
try await Task.sleep(nanoseconds: 1_000_000_000)
}
continuation.finish()
}
}
Alamofire:
AF.request("https://api.example.com/data")
.responseDecodable(of: [DataModel].self) { response in
switch response.result {
case .success(let data):
print("Received data: \(data)")
case .failure(let error):
print("Error: \(error)")
}
}
Summary
Swift Async Algorithms focuses on asynchronous sequence processing and is part of Apple's Swift ecosystem. It provides tools for working with asynchronous data streams and is more specialized in its scope.
Alamofire is a comprehensive networking library that simplifies HTTP networking tasks. It offers a wide range of features but may be overkill for simple use cases.
Choose Swift Async Algorithms for async sequence processing or when working closely with Swift's native async/await pattern. Opt for Alamofire when you need a full-featured networking solution with extensive community support.
A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Pros of swift-composable-architecture
- Provides a comprehensive framework for building applications with a unidirectional data flow
- Offers powerful testing capabilities, including time travel debugging
- Includes built-in support for side effects and dependencies management
Cons of swift-composable-architecture
- Steeper learning curve due to its more complex architecture
- May introduce overhead for smaller projects or simpler use cases
- Requires adherence to a specific architectural pattern, which might not fit all project needs
Code Comparison
swift-async-algorithms:
let numbers = AsyncStream { continuation in
for i in 1...5 {
continuation.yield(i)
try? await Task.sleep(nanoseconds: 1_000_000_000)
}
continuation.finish()
}
swift-composable-architecture:
struct CounterState: Equatable {
var count = 0
}
enum CounterAction: Equatable {
case increment
case decrement
}
let counterReducer = Reducer<CounterState, CounterAction, Void> { state, action, _ in
switch action {
case .increment:
state.count += 1
return .none
case .decrement:
state.count -= 1
return .none
}
}
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
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
adjacentPairs()
: Collects tuples of adjacent elements.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 quiescence 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.
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 Async 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", from: "1.0.0"),
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 requires Xcode 14 on macOS hosts. Previous versions of Xcode do not contain the required Swift version.
Building/Testing Using Xcode on macOS
- In the
swift-async-algorithms
directory runswift build
orswift test
accordingly
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
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.
Top Related Projects
Commonly used data structures for Swift
Reactive Programming in Swift
Network abstraction layer written in Swift.
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
Elegant HTTP Networking in Swift
A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot