Skip to content

Instantly share code, notes, and snippets.

@NikolaiRuhe
Created August 14, 2025 16:44
Show Gist options
  • Save NikolaiRuhe/36384157d59c392c0a36331886198fbd to your computer and use it in GitHub Desktop.
Save NikolaiRuhe/36384157d59c392c0a36331886198fbd to your computer and use it in GitHub Desktop.
A property wrapper that creates an AsyncStream yielding values when the property is set.
import os
@propertyWrapper
public struct Streaming<Wrapped: Sendable> {
public var wrappedValue: Wrapped {
get { value }
set {
value = newValue
let continuations = protectedContinuations.withLock { $0.values }
for continuation in continuations {
continuation.yield(newValue)
}
}
}
public var projectedValue: Stream {
get {
let id = nextID
let (stream, continuation) = Stream.makeStream(bufferingPolicy: bufferingPolicy)
protectedContinuations.withLock { $0[id] = continuation }
continuation.onTermination = { [protectedContinuations] _ in
protectedContinuations.withLock { $0[id] = nil }
}
return stream
}
}
public init(wrappedValue: Wrapped, bufferingPolicy: Stream.Continuation.BufferingPolicy = .bufferingNewest(1)) {
self.value = wrappedValue
self.bufferingPolicy = bufferingPolicy
}
public typealias Stream = AsyncStream<Wrapped>
private var value: Wrapped
private let bufferingPolicy: Stream.Continuation.BufferingPolicy
private let protectedContinuations: OSAllocatedUnfairLock<[Int: Stream.Continuation]> = .init(initialState: [:])
}
fileprivate var nextID: Int { _nextID.withLock { id in defer { id += 1 }; return id } }
fileprivate let _nextID: OSAllocatedUnfairLock<Int> = .init(initialState: 42)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment