A small iOS SwiftUI app showing how to use the AsyncStream.Continuation → AsyncStream pattern in Swift Concurrency:
- Networking: Bridge
URLSessioncallbacks into AsyncStream usingAsyncStream.Continuation. - Location: Bridge
CLLocationManagerDelegateinto AsyncStream usingAsyncStream.Continuation. - NotificationCenter: Bridge notifications to an AsyncStream via
AsyncStream.Continuation. - CADisplayLink: Bridge frame ticks to an AsyncStream via
AsyncStream.Continuation. - Reachability (NWPathMonitor): Bridge network path updates to an AsyncStream.
- UIControl (UITextField): Bridge target–action
editingChangedto an AsyncStream.
- Open
AsyncStreamPatternDemoApp.xcodeprojin Xcode 16+. - Select the
AsyncStreamPatternDemoAppscheme and a simulator/device, then press Cmd+R. - When prompted, allow Location for the demo.
Notes: Networking uses https://jsonplaceholder.typicode.com/todos/1. DisplayLink runs on the main runloop. Reachability uses NWPathMonitor.
This pattern is the async/await equivalent of the Combine Subject → Publisher pattern:
- Convert delegate-style APIs to AsyncStreams
- Drive SwiftUI UIs with async state using @Observable
- Decouple imperative event sources from async/await subscribers
- Modern Swift Concurrency approach without third-party frameworks
- SwiftUI + MVVM
- Swift Concurrency (async/await + AsyncStream)
- @Observable and @MainActor for ViewModels
- No third-party dependencies
| Combine | Swift Concurrency |
|---|---|
PassthroughSubject |
AsyncStream.Continuation (stateless) |
CurrentValueSubject |
AsyncStream.Continuation (with stored value) |
Publisher |
AsyncStream |
.sink { } / .assign |
for await value in stream |
- AsyncStream.Continuation is the bridge between imperative and async/await programming
- Use
continuation.yield()to send values into the stream - Use
continuation.finish()to complete the stream - Handle cleanup with
continuation.onTermination - Use
@MainActorand@Observablefor SwiftUI integration
💡 Author: Banghua Zhao
⭐️ Please star if you find this useful!
