Search code examples
swiftswiftuiswiftui-navigationstack

SwiftUI NavigationStack: Destination does not show up but shows same view again


In the following sample app, why after tapping on "Second Destination" does the same screen show up again instead of actually showing the SecondDestination?

struct ContentView: View {
    var body: some View {
        NavigationStack {
            NavigationLink { 
                FirstDestination()
            } label: { 
                Text("First Destination")
            }

        }
    }
}

struct FirstDestination: View {
    var body: some View {
        NavigationLink("Second Destination", value: "my name").navigationDestination(for: String.self) { name in
            SecondDestination(name: name)
        }
    }
}

struct SecondDestination: View {
    
    let name: String
    
    var body: some View {
        Text(name)
    }
}

Solution

  • The issue is that you are mixing two different approaches two navigation, the "old" one with NavigationLink(destination:) and the "new" one NavigationLink(value:).

    Mixing both approaches does not work. You will have to decide to either only use the links where you directly define the destination or the one where you define a value and then later on define a .navigationDestination(for:) for the value.

    Explanation:

    To understand why you can't mix those approaches, it helps to look at NavigationStack and what it can do when you provide a path as an input like this: NavigationStack(path: $path). [Even if you don't define it explicitly, SwiftUI will do it internally.]

    path is a collection of values that represent your navigation path. It contains all the values that you pushed onto the stack via NavigationLink(value:). Each of the values in that path represents one view, one level of navigation in your NavigationStack and you can interact with the navigation in code by adding or removing elements to/from the path and the UI will adapt accordingly.

    However, if you then also use NavigationLink(destination:) somewhere, what would be the value that gets added to the path when the user taps on that link? The answer is: none. There is no value. That NavigationLink only defines a destination view. So SwiftUI tries to do its best to show the destination view, but then can't append something to the path.

    So if you then try to remove the last element from the path, you will not remove the "last view on the stack", but the second to last. Similarly, if you try to append something to the path, SwiftUI can't really put it above the currently presented view, because that view simply does not exist within path. The result is that the newly added view is shown, but "beneath" the view you navigated to via NavigationLink(destination:).

    I hope this helps you understand why you can't mix the two approaches. Decide on one and things will work.