import ComposableArchitecture extension Reducer { /// A higher order reducer that sends a receive action on a trigger action. /// /// ## Example /// ```swift /// /// @Reducer /// struct MyFeature { /// ... /// /// enum Action { /// case receive(TaskResult) /// case task /// } /// /// @Dependency(\.numberClient) var numberClient /// /// var body: some ReducerOf { /// Reduce { state, action in /// ... /// } /// .receive(on: \.task, with: \.receive) { /// try await numberClient.fetchCurrentNumber() /// } /// } /// } /// ``` /// /// - Parameters: /// - triggerAction: The action that triggers calling the result handler. /// - receiveAction: The action that receives the task result. /// - resultHandler: The operations that is called that returns the value to be received. /// @inlinable public func receive( on triggerAction: CaseKeyPath, with receiveAction: CaseKeyPath>, result resultHandler: @escaping @Sendable () async throws -> Value ) -> _ReceiveOnTriggerReducer { .init( parent: self, triggerAction: { AnyCasePath(triggerAction).extract(from: $0) }, receiveOperation: .case(AnyCasePath(receiveAction), resultHandler) ) } } extension Reducer where Action: ReceiveAction { /// A higher order reducer that sends a receive action on a trigger action. /// /// ## Example /// ```swift /// /// @Reducer /// struct MyFeature { /// ... /// /// enum Action: ReceiveAction { /// case receive(TaskResult) /// case task /// /// @CasePathable /// enum ReceiveAction { /// case currentNumber(Int) /// ... /// } /// } /// /// @Dependency(\.numberClient) var numberClient /// /// var body: some ReducerOf { /// Reduce { state, action in /// ... /// } /// .receive(on: \.task, case: \.currentNumber) { /// try await numberClient.fetchCurrentNumber() /// } /// } /// } /// ``` /// /// - Parameters: /// - triggerAction: The action that triggers calling the result handler. /// - embedCasePath: The case path to embed the result into. /// - resultHandler: The operations that is called that returns the value to be received. @inlinable public func receive( on triggerAction: CaseKeyPath, case embedCasePath: CaseKeyPath, result resultHandler: @escaping @Sendable () async throws -> Value ) -> _ReceiveOnTriggerReducer { .init( parent: self, triggerAction: { AnyCasePath(triggerAction).extract(from: $0) }, receiveOperation: .case(AnyCasePath(embedCasePath), resultHandler) ) } } public struct _ReceiveOnTriggerReducer< Parent: Reducer, TriggerAction, Value, Result >: Reducer { @usableFromInline let parent: Parent @usableFromInline let triggerAction: @Sendable (Parent.Action) -> TriggerAction? @usableFromInline let receiveOperation: ReceiveOperation @usableFromInline init( parent: Parent, triggerAction: @escaping @Sendable (Parent.Action) -> TriggerAction?, receiveOperation: ReceiveOperation ) { self.parent = parent self.triggerAction = triggerAction self.receiveOperation = receiveOperation } @inlinable public func reduce( into state: inout Parent.State, action: Parent.Action) -> Effect { let baseEffects = parent.reduce(into: &state, action: action) guard triggerAction(action) != nil else { return baseEffects } return .merge( baseEffects, .receive(operation: receiveOperation) ) } }