Skip to content

Instantly share code, notes, and snippets.

@darrarski
Created March 24, 2025 19:10
Show Gist options
  • Save darrarski/7fa35c74823f39841a2c081edd634dcc to your computer and use it in GitHub Desktop.
Save darrarski/7fa35c74823f39841a2c081edd634dcc to your computer and use it in GitHub Desktop.
import ComposableArchitecture
import Foundation
@Reducer
public struct AsyncSequenceReducer<Element, Failure>: Sendable
where Element: Sendable,
Element: Equatable,
Failure: Sendable,
Failure: Error
{
public struct State: Sendable, Equatable {
public init() {
self.id = UUID()
self.element = nil
}
public let id: UUID
public var element: Element?
}
@CasePathable
public enum Action: Sendable {
case subscribe
case received(Element)
case finished(failure: Failure? = nil)
case cancel
}
struct CancelID: Hashable {
var stateID: UUID
}
public init(
_ sequence: @escaping @Sendable () -> any AsyncSequence<Element, Failure>
) {
self.sequence = sequence
}
let sequence: @Sendable () -> any AsyncSequence<Element, Failure>
public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .subscribe:
return .run { send in
for try await element in self.sequence() {
await send(.received(element))
}
await send(.finished())
} catch: { error, send in
await send(.finished(failure: (error as! Failure)))
}.cancellable(
id: CancelID(stateID: state.id),
cancelInFlight: true
)
case .received(let element):
state.element = element
return .none
case .finished(_):
return .none
case .cancel:
return .cancel(id: CancelID(stateID: state.id))
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment