Coordinator pattern and TCA

Hello! Could somebody help me with a navigation problem? I'm working on a small project with TCA. The problem is I have a big piece of the app, where every screen is independent of each other, like small building blocks.

I've created the main state, which knows, which screen should be shown at one time

enum Scene  {
    case first(FirstChildState)
    case second(SecondChildState)
}

public struct State: Equatable {
    var currentScene: Scene?
}

In every demo of navigation, there are the only cases when the main view knows about child view, and with a scoped store could open it. I have quite a different situation. I don't have the main view at all. On the initial of the system, the first currentScene is calculated and should be shown. After some actions currentScene changes and I want to show another screen according to this change. Also, I cannot open second from the first because first has only its own small store with FirstChildState in it.

I've also been trying to create an array of Scene, which could represent the navigation stack. However, the main problem remain the same. I need a place, where I could open every screen from every screen, or I need to give this possibility to screens themselves.

So for me, it sounds like a coordinator pattern, where I have one entity, which can open any screen from any place, according to provided data.

Unfortunately, I'm quite new in SwiftUI however, I managed to build something like this.

struct Coordinator<Content: View>: View {

    let store: Store<TrainingState, TrainingAction>

    var body: some View {
        WithViewStore(store) { viewStore in
            NavigationView {
                NavigationLink(
                    destination: self.scene(for: viewStore.currentScene),
                    isActive: viewStore.binding(
                        get: { $0.currentScene != nil },
                        send: TAction.onInitiate
                    ),
                    label: EmptyView.init
                )
            }
        }
    }

    func scene(for type: Scene?) -> some View {
        guard let type = type else { return EmptyView() |> AnyView.init }
        switch type {
        case .first:
            return IfLetStore(
                store.scope(state: {
                    guard let currentScene = $0.currentScene else { return nil }
                    return (/Scene.first).extract(from: currentScene)
                },
                            action: Action.first),
                then: FirstScene.init(store:),
                else: EmptyView()
                )
                |> AnyView.init
        }
    }
}

However, because of the way of SwiftUI working this approach doesn't push any new screen every time when a scene changes it just updates one.

2 Likes

Hi! Have you made any progress on this?

I'm also trying a few things to reproduce a coordinator like pattern. The main thing I want is to create an easy way to deeplink to parts of the app.

Unfortunately no @txaiwieser. I’ve made a workaround that I had posted above. I had a hope on WWDC, while doing other things, but without a successful result as well. Maybe @stephencelis or @mbrandonw have an opinion about this topic?