Nested NavigationStack destination

 struct View1: View {
    var category: String
    
    var body: some View {
        NavigationStack {
            List { ...
                NavigationLink(value: category) {
                    Text("Link to View2")
                }
            }
            .navigationDestination(for: String.self) { str in
                View2(item: str)
            }
        }
    }
}

struct View2: View {
    var item: String
    
    var body: some View {
        NavigationStack {
            List { ...
                NavigationLink(value: category) {
                    Text("Link to View3")
                }
            }
            .navigationDestination(for: String.self) { str in
                View3(number: str)
            }
        }
    }
}

struct View3: View {
    var number: String
    
    var body: some View {
        List { ... }
    }
}

In this code View1 when press one of the List item it says " there is no matching navigationDestination declaration visible from the location of the link. " But when I delete the NavigationStack bracket in the View2, then it works. Why is that?

And I want to put the path to the NavigationStack for going to the root. So I think I should leave the NavigationStack in the View2. Is it right? or is there another way?

Accepted Reply

Hi,

Is there a reason you need to nest one NavigationStack inside another? The intended way to use it is to have one top-level NavigationStack that contains multiple navigationDestination - https://developer.apple.com/documentation/swiftui/view/navigationdestination(for:destination:) - modifiers that allow you to navigate to your next view based on the value, or to use other ways of destination like a NavigationLink that contains a destination in it.

For your code, since you already have your items in a List, you don't need another NavigationStack. Also, you don't need two different navigationDestination modifiers that both target Strings. If you want these to have different destinations, you can use different types as well to achieve this.

Here is your code with the above applied:

struct View1: View {
    @State private var path: NavigationPath = NavigationPath()
    
   var body: some View {
       NavigationStack(path: $path) {
           List(0..<3) { num in
               //value is a String here
               NavigationLink(value: num.description) {
                   Text("\(num.description) link to View2")
               }
           }
           .navigationDestination(for: String.self) { str in
               View2()
           }
           .navigationDestination(for: Int.self) { num in
               View3()
               
           }
       }
   }
}

struct View2: View {
    var body: some View {
        List(0..<4) { num in
            //value is an Int here
            NavigationLink(value: num) {
                Text("\(num.description) link to View3")
            }
        }
    }
}

struct View3: View {
   var body: some View {
       List(0..<3) { num in
           Text(num.description)
       }
   }
}

The value presented in View1 is a String, so it will use the navigation destination modifier that targets Strings to go to View2. The value presented in View2 is an Int, so it will use the navigation destination modifier that targets Ints to go to View3. The NavigationPath will be in the top level NavigationStack, which is in View1

Replies

Hi,

Is there a reason you need to nest one NavigationStack inside another? The intended way to use it is to have one top-level NavigationStack that contains multiple navigationDestination - https://developer.apple.com/documentation/swiftui/view/navigationdestination(for:destination:) - modifiers that allow you to navigate to your next view based on the value, or to use other ways of destination like a NavigationLink that contains a destination in it.

For your code, since you already have your items in a List, you don't need another NavigationStack. Also, you don't need two different navigationDestination modifiers that both target Strings. If you want these to have different destinations, you can use different types as well to achieve this.

Here is your code with the above applied:

struct View1: View {
    @State private var path: NavigationPath = NavigationPath()
    
   var body: some View {
       NavigationStack(path: $path) {
           List(0..<3) { num in
               //value is a String here
               NavigationLink(value: num.description) {
                   Text("\(num.description) link to View2")
               }
           }
           .navigationDestination(for: String.self) { str in
               View2()
           }
           .navigationDestination(for: Int.self) { num in
               View3()
               
           }
       }
   }
}

struct View2: View {
    var body: some View {
        List(0..<4) { num in
            //value is an Int here
            NavigationLink(value: num) {
                Text("\(num.description) link to View3")
            }
        }
    }
}

struct View3: View {
   var body: some View {
       List(0..<3) { num in
           Text(num.description)
       }
   }
}

The value presented in View1 is a String, so it will use the navigation destination modifier that targets Strings to go to View2. The value presented in View2 is an Int, so it will use the navigation destination modifier that targets Ints to go to View3. The NavigationPath will be in the top level NavigationStack, which is in View1

Oh now I understand. Thank you!! So we don't need to put navigationDestination to each view but every destination on the first view under NavigationStack