Nested Reducers not propagating actions in TCA (Composable Architecture)

64 Views Asked by At

I have the following code, heavily inspired by the TicTacToe example project:

import ComposableArchitecture

struct Session: Equatable {
    let token: String
}

@Reducer(state: .equatable)
enum AppRootFeature {
    case loggedIn(AuthenticatedFeature)
    case loggedOut(AuthenticationFeature)

    public static var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case let .loggedOut(.authenticatedSuccessfully(token)):
                state = .loggedIn(AuthenticatedFeature.State(token: token))
                return .none
            case .loggedOut:
                return .none // Ignore the rest
            case let .loggedIn(state):
                print(state)
                return .none // Ignore unless we would have log out functionality
            }
        }
        .ifCaseLet(\.loggedOut, action: \.loggedOut) {
            AuthenticationFeature()
        }
        .ifCaseLet(\.loggedIn, action: \.loggedIn) {
            AuthenticatedFeature()
        }
    }
}

@Reducer
struct AuthenticatedFeature {
    @ObservableState
    struct State: Equatable {
        let token: String
    }

    enum Action {
        case showUser
        case showOrder
    }

    var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case .showUser:
                return .none
            case .showOrder:
                return .none
            }
        }
    }
}

@Reducer
struct AuthenticationFeature {
    @Dependency(\.authentication) var authentication

    @ObservableState
    struct State: Equatable { }

    enum Action {
        case logIn
        case authenticationError(Error)
        case authenticatedSuccessfully(token: String)
    }

    var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case .logIn:
                return .run { send in
                    do {
                        let token = try await authentication.authenticate()
                        await send(.authenticatedSuccessfully(token: token))
                    } catch let error {
                        await send(.authenticationError(error))
                    }
                }
            case let .authenticationError(error):
                print(error)
                return .none // Not handled ATM
            case .authenticatedSuccessfully:
                return .none // Only interesting for root feature
            }
        }
    }
}

When I trigger the .logIn Action in the AuthenticationFeature, it will trigger the right actions (.authenticatedSuccessfully is received) but the parent feature AppRootFeature does not receive the Action.

I cannot find any significant difference between the TicTacToe example and my code anymore, yet when I run the TicTacToe example it will propagate the actions of the Login feature where mine doesn't

0

There are 0 best solutions below