### Combine AsyncSequences with combineLatest Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Combine multiple AsyncSequences into a single AsyncSequence using combineLatest from Swift Async Algorithms. The example shows two AsyncStreams and prints timing-dependent pairs after both have emitted once. ```swift import AsyncAlgorithms import AsyncCombine // Two arbitrary async streams let a = AsyncStream { cont in Task { for i in 1...3 { try? await Task.sleep(nanoseconds: 100_000_000) cont.yield(i) // 1, 2, 3 } cont.finish() } } let b = AsyncStream { cont in Task { for s in ["A", "B"] { try? await Task.sleep(nanoseconds: 150_000_000) cont.yield(s) // "A", "B" } cont.finish() } } // combineLatest-style pairing (from Swift Async Algorithms) var tasks = Set() combineLatest(a, b) .map { i, s in "Pair: \(i) & \(s)" } .sink { print($0) } .store(in: &tasks) // Prints (timing-dependent, after both have emitted once): // "Pair: 2 & A" // "Pair: 3 & A" // "Pair: 3 & B" ``` -------------------------------- ### Generate DocC documentation with swift package Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Generate DocC documentation locally using the Swift DocC Plugin. The --disable-sandbox flag is required for the command to work. ```bash swift package --disable-sandbox generate-documentation --target AsyncCombine ``` -------------------------------- ### Use stream() for deterministic ordering Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Use stream() when ordering against a subsequent send(_:) matters, such as in tests or producer/consumer pairs. The subscription is registered before send(_:) runs, so the stream yields the replayed value then the sent value. ```swift // Deterministic: the subscription is registered before `send(_:)` runs. let stream = await relay.stream() await relay.send(true) // stream yields false (replay), then true ``` -------------------------------- ### Store subscriptions and cancelAll in deinit Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Shows a Combine-style lifecycle pattern: keep subscriptions in a Set and cancel them all in deinit. Use observed(_:) to create an AsyncStream from an @Observable property, then map and sink, storing the task with .store(in:). ```swift final class Monitor { private var subscriptions = Set() private let vm: CounterViewModel init(vm: CounterViewModel) { self.vm = vm vm.observed(\.count) .map(String.init) .sink { print("Count:", $0) } .store(in: &subscriptions) } deinit { subscriptions.cancelAll() } } ``` -------------------------------- ### Observe count changes with async/await Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Shows a traditional async/await approach to observing the view model's count changes. It uses the Observations API to create an AsyncSequence and a Task with a for-await loop to print formatted values. ```swift let viewModel = CounterViewModel() let countChanges = Observations { self.viewModel.count } Task { for await count in countChanges.map({ "Count: \($0)" }) { print("\(count)") } } ``` -------------------------------- ### Use CurrentValueRelay with values() Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Use CurrentValueRelay for hot, replay-1 state. Subscribing yields the current value immediately, then subsequent updates. values() is synchronous and usable from sync code like configureBindings() or viewDidLoad(). ```swift let relay = CurrentValueRelay(false) var subs = Set() relay.values() // no `await` — usable from sync code .map { $0 ? "ON" : "OFF" } .sink { print($0) } // "OFF" replayed, then every later update .store(in: &subs) ``` -------------------------------- ### Find first matching value with first(equalTo:) Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Use the first(equalTo:) helper to assert that a specific value appears in an AsyncSequence without fully consuming it. It suspends until the first matching element arrives, or returns nil if the sequence finishes first. ```swift import AsyncCombine import Testing @Test func testStreamEmitsSpecificValue() async throws { let stream = AsyncStream { cont in cont.yield(1) cont.yield(2) cont.yield(3) cont.finish() } // Wait for the first element equal to 2. let match = await stream.first(equalTo: 2) #expect(match == 2) } ``` -------------------------------- ### Create a feature branch Source: https://github.com/will-lumley/asynccombine/blob/main/CONTRIBUTING.md Creates a new branch for a feature before submitting a pull request. ```bash git checkout -b feature/my-new-feature ``` -------------------------------- ### Debounce observed stream with debounce(for:) Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Apply debounce to an observed property stream to smooth noisy inputs. Requires importing Swift Async Algorithms. You can also merge, zip, removeDuplicates, etc. ```swift import AsyncAlgorithms viewModel.observed(\.count) .debounce(for: .milliseconds(250)) // smooth noisy inputs .map { "Count: \($0)" } .sink { print($0) } .store(in: &subscriptions) ``` -------------------------------- ### Cancel subscriptions with cancelAll() Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Cancel subscription tasks when done (e.g., in deinit). Subscriptions don't end on their own when the relay deallocates; the consumer is responsible for cancellation. ```swift subs.cancelAll() ``` -------------------------------- ### Test AsyncSequence values with Recorder Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Use the Recorder class to capture and assert values emitted by an AsyncSequence. The recorder consumes the sequence on a background task and buffers elements, allowing tests to await them one by one. If a value doesn't arrive within the timeout, it automatically reports a failure via Issue.record or XCTFail. ```swift import AsyncCombine import Testing // or XCTest @Test func testRelayEmitsExpectedValues() async throws { let relay = CurrentValueRelay(0) let recorder = await relay.stream().record() await relay.send(1) await relay.send(2) let first = try await recorder.next() let second = try await recorder.next() #expect(first == 1) #expect(second == 2) } ``` -------------------------------- ### Observe @Observable properties with observed(_:) Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Turn an @Observable property into an AsyncStream that replays the current value and emits on every change. Chain AsyncSequence operators and finish with sink or assign. Uses withObservationTracking under the hood and reads on MainActor to avoid stale reads. ```swift import AsyncCombine import Observation @Observable @MainActor final class CounterViewModel { var count: Int = 0 } let viewModel = CounterViewModel() var subscriptions = Set() // $viewModel.count → viewModel.observed(\.count) viewModel.observed(\.count) .map { "Count: \($0)" } .sink { print($0) } .store(in: &subscriptions) viewModel.count += 1 // prints "Count: 1" ``` -------------------------------- ### Bind observed property to UI with assign(to:on:) Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Bind an observed property to a UI element's property using assign(to:on:). Works for UILabel.textColor, NSTextField.textColor, SKShapeNode.fillColor, and custom class properties. ```swift // UILabel example let label = UILabel() viewModel.observed(\.count) .map { UIColor( hue: CGFloat($0 % 360) / 360, saturation: 1, brightness: 1, alpha: 1 ) } .assign(to: \.textColor, on: label) .store(in: &subscriptions) ``` -------------------------------- ### Observe count changes with AsyncCombine Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Uses AsyncCombine to observe the view model's count property with Combine-style operators. The observed operator creates a stream, map transforms values, sink prints them, and store(in:) manages the subscription lifetime. ```swift var subscriptions = Set() viewModel.observed(\.count) .map { "Count: \($0)" } .sink { print($0) } .store(in: &subscriptions) ``` -------------------------------- ### Handle throwing streams with sink(catching:_:) Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Consumes a throwing AsyncSequence with sink(catching:_:). The catching closure handles errors thrown by the iterator; omit catching: for non-throwing streams like AsyncStream or relay.values(). ```swift someThrowingAsyncSequence // AsyncSequence whose iterator `next()` can throw .map { $0 } // your transforms here .sink(catching: { error in print("Stream error:", error) }) { value in print("Value:", value) } .store(in: &subscriptions) ``` -------------------------------- ### Define an @Observable CounterViewModel Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Defines an observable view model with a single count property. The @Observable macro and @MainActor annotation are prerequisites for using AsyncCombine's observed operator on this model. ```swift @Observable @MainActor final class CounterViewModel { var count: Int = 0 } ``` -------------------------------- ### Add AsyncCombine package dependency Source: https://github.com/will-lumley/asynccombine/blob/main/README.md Add the AsyncCombine package dependency to your Swift package manifest. The package is available from version 2.2.0. ```swift dependencies: [ .package(url: "https://github.com/will-lumley/AsyncCombine.git", from: "2.2.0") ] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.