NavigationSplitView and NavigationStack persistence

Let's say you have a NavigationModel that contains three NavigationPaths, one for each option in the sidebar for a NavigationSplitView.

This NavigationModel is created by the App and passed down to the root view for the scene in its environment.

Each option has a separate NavigationStack and is passed a binding to the appropriate NavigationPath from the NavigationModel.

Is it expected that when the user changes the selection in the sidebar, the NavigationPath for the newly selected view should be erased?

This is what's currently happening on macOS 26. It seems like the default action when creating a NavigationStack and passing it a binding to a NavigationPath is to clear that path and start from the root view.

Is this normal, intended behaviour or is it a bug? Or, perhaps, an option or modifier I am missing?

@gravity_gunner

Each option has a separate NavigationStack and is passed a binding to the appropriate NavigationPath from the NavigationModel.

Why do you multiple stacks as the detailView of your split view?

To understand the issue better please share a sample code that implements what you've described, that way we're on the same page and please review Understanding the navigation stack

You would have one NavigationStack per sidebar item.

struct RootView: View {
    
    @Environment(NavigationModel.self) var navigationModel
    
    var body: some View {
        @Bindable var navigationModel = self.navigationModel

        NavigationSplitView {
            List(selection: $navigationModel.selectedItem) {
                Label("One", systemImage: "folder")
                    .tag(SidebarItem.one)
                Label("Two", systemImage: "folder")
                    .tag(SidebarItem.two)
            }
            .listStyle(.sidebar)
        } detail: {
            switch navigationModel.selectedItem {
            case .one:
                NavigationStack(path: $navigationModel.pathOne) {
                    OfficersView()
                }
            case .two:
                NavigationStack(path: $navigationModel.pathTwo) {
                    OfficersView()
                }
            }
        }
    }
}

A good example of this in practice is the Apple Music app on macOS. Note how each item in the (long) sidebar has its own NavigationStack and how you can leave and return to any one of those items and the path is retained?

Further investigation and some help from others has turned up this and this.

If I remove the two Labels in the List in the sidebar and remove the selection parameter, and replace the list items with buttons with actions that directly change the navigationModel.selectedItem property, the problem goes away and both NavigationPaths are retained. Though, obviously, this breaks the appearance of the sidebar.

I'm now convinced that this is a defect. Changing navigationModel.selectedItem via the selection parameter on List is somehow resetting the value of the NavigationPath for the relevant NavigationStack as well. This is unexpected behaviour.

For the record, the Understanding the Navigation Stack article you linked to makes no mention of sidebars, NavigationSplitView or TabViews with the .sidebarAdaptable appearance (which also exhibits the same buggy behaviour).

Is there an answer to this query? Right now, I'm looking at the (lengthy) procedure for reporting a defect here.

It seems that causing the detail view in a NavigationSplitView to change when there is any kind of NavigationStack in that view hierarchy causes the associated NavigationPath (or just any array being used as one) to be emptied when the NavigationStack is reused by the system.

The NavigationPath is subsequently unavoidably rewritten. This cannot be overcome if the NavigationPath is mutable and making it immutable means, basically, reinventing the entire navigation process.

This would seem to defeat the purpose of having a stored NavigationPath that can be programmatically altered and used to recreate the state of the NavigationStack when the owning view is created.

Expected Results: When constructing a NavigationStack with a given NavigationPath, the stack should display the top most view appropriate for the element at the top of the stack. Each individual NavigationPath for each item in the sidebar should be retained separately so that the user can return to a previous item and find it in the same state it was left in.

Actual Results: The NavigationStack from the previous displayed view is reused and the NavigationPath originally passed into it is cleared. Reuse of the NavigationStack continues with the new NavigationPath. Returning to the previous item in the sidebar, therefore, results in the root view for that item being displayed instead of the previous stack.

NavigationSplitView and NavigationStack persistence
 
 
Q