How to use a Global Reducer without view

Hi, I'm starting working with The Composable Architecture and so far so good, but I find some parts hard to understand. I found myself in a situation where I'm migrating part of a traditional UIKit app to SwiftUI+TCA approach. I managed to create a SwiftUI view and reducer for the first screen of a flow but I want to handle flow navigation with a global reducer, using pullback.

This method creates the first screen with its own reducer

    public func createWelcomeScreen() -> some View {
        WelcomeView(
            store: .init(
                initialState: .init(),
                reducer: createWelcomeReducer(),
                environment: createWelcomeEnvironment()
            )
        )
    }

Also I created a method to create the global reducer and combine it with the Welcome reducer

    private func createGlobalReducer(
        welcomeReducer: WelcomeReducer
    ) -> GlobalReducer {
        let globalWelcomeReducer: GlobalReducer = welcomeReducer
            .optional()
            .pullback(
                state: \.welcomeState,
                action: /GlobalReducerDefinition.Action.welcome,
                environment: { _ in
                    self.createWelcomeEnvironment()
                })
        return globalWelcomeReducer
            .combined(with: GlobalReducerDefinition.create())
    }

So, my problem now is, how can I inject the GlobalReducer into the WelcomeView since the later is expecting a WelcomeReducer ? Maybe what I want can't be achieved and need to handle navigation from each individual local reducer?

Thanks for your time.

Hello!
You scope the stores at the view level:

store.scope(state: \.welcomeState, action: GlobalReducerDefinition.Action.welcome)

is a Store<WelcomeState, WelcomeAction> that you can pass to your view. You don't need to inject reducers, this is realized automatically when you scope the store using the pullback information you specified in the global reducer (this is not exactly what's happening under the hood, but it behaves like it is, and you can use this model to reason about your app's domain). You pass this scoped store to your view, and the view will consume it as any store of its local domain.

You will probably need a IfLetStore here if your welcome state is optional, but the idea is the same.

I think I understand your point but is not what I meant. So my idea is to do this.

  • I have a flow of screens, each one with its own, View, Reducer, Actions and State
  • In order to handle some common code for all those screen I want to use a global reducer for the complete flow. But there is no view for this reducer, since the UI will be handled by each individual screen.
  • So what I want to do is trigger an action on one of the individual screens, then if needed pass some params to the global reducer and process them there. Once on the globar reducer, and effect can be triggered to jump to another view or display an error on the current view.

This is why I think maybe the global reducer can not be the proper approach and should and use a different shared global type to handle all that common login and then inject it on each individual view in the flow.