Using NavigationSplitView with NavigationStack in it with variable navigation depth (2 and 3 levels) — unexpected back navigation on iPhone

I’m building a cross-platform app targeting macOS, iPad, and iPhone. My app currently uses both 2-level and 3-level navigation workflows:

3-level navigation:

  • First level: Categories
  • Second level: List of items in the selected category
  • Third level: Detail view for a specific item

2-level navigation:

  • First level: Category
  • Second level: A singleton detail view (for example, StatusView). It does not have concept of List.

After watching a couple of WWDC videos about multi-platform navigation, I decided to go with NavigationSplitView.

However, on macOS, a 3-column NavigationSplitView felt a bit overwhelming to my eyes when the third column was empty—especially for the occasional 2-level navigation case. So I removed the third column and instead embedded a NavigationStack in the second column. According to the official Apple documentation, this is supported:

You can also embed a NavigationStack in a column.

The code with NavigationStack in NavigationSplitView works fine on macOS.

But on iPhone, for the same code I’m seeing unexpected behavior:

The first time I tap on the “Actions” category, it briefly shows the “Select an item” view, and then automatically pops back to the all-categories view. If I tap the same "Actions" category again, it shows the list of actions correctly, and everything works fine until I terminate and relaunch the app. Here is a minimal reproducible example:

import SwiftUI

struct StatusView: View {
    var body: some View {
        NavigationStack {
            List {
                Text("Heartbeat: OK")
                Text("Connected to backend: OK")
            }
        }
    }
}

struct ActionListView: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink(value: "Action 1 value") {
                    Text("Action 1 label")
                }
                NavigationLink(value: "Action 2 value") {
                    Text("Action 2 label")
                }
            }
        }
        .navigationDestination(for: String.self) { action in
            Text(action)
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            List {
                NavigationLink(value: "Actions") {
                    Text("Actions (3 level)")
                }
                NavigationLink(value: "Modes") {
                    Text("Modes (3 level)")
                }
                NavigationLink(value: "State") {
                    Text("Status (2 level)")
                }
            }
            .navigationDestination(for: String.self) { category in
                switch category {
                case "Actions":
                    ActionListView()
                case "Modes":
                    Text("Modes View")
                case "State":
                    StatusView()
                default:
                    Text("Unknown Category")
                }
            }
        } detail: {
            Text("Select an item")
            
        }
    }
}

Questions and considerations:

  1. How can I prevent this unexpected automatic pop back to the root view on iPhone the first time I select a category?

  2. Future-proofing for more than 3 level navigation: In the future, I may allow users to navigate beyond three levels (e.g., an item in one category can reference another item in a different category). Is it correct to assume that to support this with back navigation, I’d need to keep using NavigationStack inside NavigationSplitView?

  3. Is embedding NavigationStack in a 2 column NavigationSplitView the only practical approach to handle mixed 2 and 3 navigation depth if I don't want the third column to be ever empty?

  4. On macOS, NavigationStack alone doesn’t feel appropriate for sidebar-based navigation. Does it mean everyone on macOS pretty much always use NavigationSplitView?

Any advice or examples would be appreciated. Thanks!

Using NavigationSplitView with NavigationStack in it with variable navigation depth (2 and 3 levels) — unexpected back navigation on iPhone
 
 
Q