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.