NB: I just saw your edit before I was about to post the below
. I'll still post it for posterity because I think some of it will help you out, but what you found is also another navigation "trick" we've had to employ to work around strange popping behavior.
Hey @GrafHubertus, when I run your application in Xcode 12.5.1 I don't see the behavior you are describing, but I know exactly what you are talking about. I have witnessed it many times myself, and there have been a few discussions on this forum and in the GitHub discussions about this issue.
It appears to be a SwiftUI bug. If you observe too much state in a screen it seems that NavigationLinks can get confused and pop their content. It's reproducible in even vanilla SwiftUI.
It's hard to know if this is the problem you are seeing because I can't reproduce locally, but I can tell you how chisel away the state that you are observing.
For example, right now your RootView is observing all of RootState even though it doesn't actually need any of that state in the body:
struct RootView: View {
let store: Store<RootState, RootAction>
var body: some View {
WithViewStore(store) { viewStore in
...
}
}
}
This means every single little state change in the application, even if it's just in the 10th drill down screen of the folder structure, is going to cause this view to recompute its body.
One easy thing you can do here is just transform the store to be "stateless", which means it will not observe any state:
WithViewStore(store.stateless) { viewStore in
There is a similar problem in FolderView. You are observing all of FolderState even though all you need is content and selection.value?.item.id. In particular that means you are observing all state changes to every child FolderView, not just this one.
To fix this you can introduce a little struct to hold just the state the view cares about:
struct FolderView: View {
let store: Store<FolderState, FolderAction>
struct ViewState: Equatable {
let content: IdentifiedArrayOf<Item>?
let selectionItemId: Int?
init(state: FolderState) {
self.content = state.content
self.selectionItemId = state.selection.value?.item.id
}
}
...
}
And then .scope the store before hitting it with WithViewStore:
WithViewStore(store.scope(state: ViewState.init)) { viewStore in
...
}
And then the only change you need to make in the view's body is to use selectionItemId rather than reaching into the selection directly:
- selection: viewStore.binding(get: \.selection.value?.item.id, send: FolderAction.navigateTo)
+ selection: viewStore.binding(get: \.selectionItemId, send: FolderAction.navigateTo),
I'd be curious if any of that helps. If not, then I'd also be curious what version of Xcode/iOS you are testing on where you see this behavior.