Broadcasting/Multicasting for Swift's AsyncSequences
Swift's AsyncSequence
protocol backs the for await value in stream { /* ... */ }
syntax.
This is a nice pattern with an annoying constraint: multiple subscribers are unsupported by default.
It's up to the author of the async sequence's data source to ensure each consumer receives its own AsyncIteratorProtocol
to iterate on — and neither the Swift language nor the official AsyncAlgorithms package provide operators which 'broadcast' an existing AsyncSequence.
Async sequences created with AsyncStream.makeStream(of:)
or with any of the operators from AsyncAlgorithms can not emit to multiple subscribers.
Instance of AsyncBroadcaster
, whether created directly or through a call to .broadcast()
, can.
- Broadcast: make an AsyncSequence safe for multiple subscribers.
- Yield: synchronously yield values to an AsyncBroadcaster.
- Replay behavior: Control how new subscribers receive past values
.none
: No replay.latest(n)
: Replay up to n most recent values.unbounded
: Replay all historical values
(As of v 0.0.1
this is a buffering, not a backpressure, based system.)
Add the following to your Package.swift
file:
dependencies: [
.package(url: "https://github.com/adam-zethraeus/AsyncBroadcaster.git", from: "0.0.1")
]
let stream = [1,2,3].async.broadcast()
let one = Task {
var result: [Int] = []
for await i in stream {
result.append(i)
}
return result
}
await #expect(one.value == [1,2,3])
let channel = AsyncBroadcaster.makeAsyncBroadcaster(of: Int.self, replaying: .latest(3))
let task1 = Task {
var results: [Int] = []
for await value in channel.stream {
results.append(value)
}
return results
}
let task2 = Task {
var results: [Int] = []
for await value in channel.stream {
results.append(value)
}
return results
}
try await Task.sleep(for: .milliseconds(100))
channel.continuation.yield(1)
channel.continuation.yield(2)
channel.continuation.yield(3)
channel.continuation.finish()
let results1 = await task1.value
let results2 = await task2.value
#expect(results1 == [1, 2, 3])
#expect(results2 == [1, 2, 3])
MIT