I'm trying to learn navigation using TCA, and want to create a macOS app with a sidebar. This is what I want to achieve:
Except with the text replaced with ProjectView() with the corresponding Blob Jr project.
NavigationView is deprecated and Apple recommends using NavigationSplitView for this it looks like.
Here's the code I have so far:
struct ProjectsView: View {
let store: StoreOf<ProjectsFeature>
var body: some View {
NavigationStackStore(self.store.scope(state: \.path, action: { .path($0) })) {
WithViewStore(self.store, observe: \.projects) { viewStore in
NavigationSplitView {
List {
ForEach(viewStore.state) { project in
NavigationLink(state: ProjectFeature.State(project: project)) {
Text(project.name)
}
}
}
} detail: {
Text("How do I get ProjectView() with Blob Jr to show here?")
}
}
} destination: { store in
ProjectView(store: store)
}
}
}
ProjectFeature is just like this:
(I wan't to be able to mutate the project from this view in the future.)
struct ProjectFeature: Reducer {
struct State: Equatable {
var project: Project
}
enum Action {
case didUpdateNameTextField
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch(action) {
case .didUpdateNameTextField:
return .none
}
}
}
struct ProjectView: View {
let store: StoreOf<ProjectFeature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack {
Text("Project").font(.largeTitle)
Text(viewStore.state.project.name)
}
}
}
}
If I remove the NavigationSplitView, the navigation works, but the display is incorrect.
How can I use this NavigationSplitView with TCA?

I came up with a solution. It seems though, TCA is not (yet) directly supporting a
NavigationSplitView, with having a custom view likeNavigationSplitViewStore. Thus some "manual" coding was necessary. I'm no expert with TCA, though - so bear with me if I have missed something in TCA. ;)TCA denotes this kind of navigation as "Tree based navigation". It's recommended to read the documentation, which is excellent by the way.
First, as already mentioned, we need a way to keep the selection. For this type of navigation TCA provides a property wrapper
@PresentationState:Note that
MasterandDetailare reducers.Note also, we have an array of "items" in the Master State whose titles will be drawn in the sidebar.
This struct is for demoing purpose only. Its "detail" property represents some "detail". Its type is arbitrary for the sake of the demo.
In a SwiftUI
NavigationSplitViewsetup, Master View and Detail View communicate through a@State selectionvariable defined in the Master View. TCA would probably define a Custom NavigationSplitView in order to hide the details and use aStorefor this.Now, in order to let a Store communicate with a selection, we need to add the code for the selection state and call an appropriate
send(action:)when the selection has been changed.The below snippet shows a working example. Please keep mind, that this is a starting point, and could probably improved. It's also not very "TCA" like (it lacks ergonomics), but I'm pretty sure this can be achieved with some custom views.