Search code examples
iosswiftswiftuiswiftui-navigationlinkswiftui-navigationstack

NavigationStack(path: ) - Is path meant to update the Binding<Value> passed in?


I'm a little confused by the syntax of NavigationStack(path: Binding), as mentioned above, it looks like NavigationStack should automatically handle the updating of path when a NavigationLink is pressed, is this not the case?

I've implemented a basic view as below:

import SwiftUI

class PathObj: ObservableObject {
    @State var path = [Int]()
}

struct Workout1: View {
@StateObject var pathObj = PathObj()

var body: some View {
    NavigationStack(path: $pathObj.path) {
        Text("Workout 1")

        NavigationLink {
            Workout2()
        } label: {
            VStack {
                Text("Workout 2")
                    .font(.headline)
                    .foregroundStyle(.black)
                Image(systemName: "arrow.right")
            }
        }
    }
    .environmentObject(pathObj)
}
}

The NavigationLink works correctly, but if I observe the environment object I can see that pathObj.paths is empty. Am I mis-understanding how this works? Below is Workout2's implementation:

import SwiftUI

struct Workout2: View {
@EnvironmentObject var pathObj: PathObj

var body: some View {
    Text("Workout 2")

    Button {
        pathObj.path.removeAll()
    } label: {
        Text("Pop")
    }

    NavigationLink {
        Workout3()
    } label: {
        VStack {
            Text("Workout 3")
                .font(.headline)
                .foregroundStyle(.black)
            Image(systemName: "arrow.right")
        }
    }
}
}

Where I try to add a pop function (remove all paths and navigate to root). I'm just playing with a few different implementations of handling multiple NavgationStacks on a Tab bar but feel like I'm missing the basic of passing a binding var to path?

Any help is much appreciated.


Solution

  • it looks like NavigationStack should automatically handle the updating of path

    Yes.

    but if I observe the environment object I can see that pathObj.paths is empty

    This is because you used a destination-based navigation link. That is, you used one of the NavigationLink initialisers that directly take in a destination: as a view builder closure.

    If you use a value-based navigation link, by calling one of the initialisers that take a value: parameter, that value will be appended to the navigation path when the navigation link is activated. Note that the destination view will be written in a navigationDestination(for:destination:) modifier.

    // by activating this navigation link, 1 will be appended to the 'path' array
    NavigationLink("Go!", value: 1)
        .navigationDestination(for: Int.self) { value in
            SomeDestination()
        }
    

    Destination-based navigation links do not affect the navigation path at all, so I recommend exclusively using value-based navigation links if you decide to use a navigation path, and not to mix the two types of navigation links.

    Side note: in an ObservableObject, @State doesn't make much sense. I assume you meant to use @Published.

    class PathObj: ObservableObject {
        @Published var path = [Int]()
    }