How to convert NavigationLink tag and selection to value?

I'm rebuilding the 2021 WWDC tutorial "Build a Workout App for Apple Watch" with the latest version of SwiftUI (August, 2022), and some of the old commands have been deprecated. Can someone help me rewrite the following to be compatible with iOS 16 and WatchOS 9?

List(workoutTypes) { workoutType in
    NavigationLink(
        workoutType.name,
        destination: SessionPagingView(),
        tag: workoutType,
        selection: $workoutManager.selectedWorkout
    )
}

I know that init(_:destination:tag:selection:) was deprecated in iOS 16. Apple recommends using NavigationLink(_:value:) instead inside a List within a NavigationStack or NavigationSplitView, but I cannot seem to get it to work on Xcode 14 beta 4.0.

Post not yet marked as solved Up vote post of kctuskey Down vote post of kctuskey
3.2k views

Replies

By rearranging the way properties are fed into SwiftUI, you can make it fit for the new APIs. Something like this should work:

NavigationStack {
    List(workoutTypes) { workoutType in
        NavigationLink(workoutType.name, value: workoutType)
    }
    .navigationDestination(for: HKWorkoutActivityType.self) { workoutType in
        SessionPagingView()
    }
}
NavigationSplitView {
    List(workoutTypes, selection: $workoutManager.selectedWorkout) { workoutType in
        NavigationLink(workoutType.name, value: workoutType)
    }
} detail: {
    SessionPagingView()
}
  • Thank you! This approach does a good job of storing the workoutType variable (using the 'tag' class that is deprecated), but I'd also like to store the selected workout as an environment object (similar to the deprecated 'selection' class approach).

    What is the best way to pass 2 stored values, one of which is an environment object?

    @EnvironmentObject var workoutManager: WorkoutManager var workoutTypes: [HKWorkoutActivityType] = [.paddleSports, .sailing, .rowing, .swimming]
  • facing this same issue -- would be super grateful for some additional feedback! Unable to set the equivalent to "selection" from deprecated feature.

Add a Comment
@EnvironmentObject var workoutManager: WorkoutManager
@State var presentedWorkout: [HKWorkoutActivityType] = []
var workoutTypes: [HKWorkoutActivityType] = [.cycling, .running, .walking]

var body: some View {

        NavigationStack(path: $presentedWorkout) {

            List(workoutTypes) { workoutType in

                NavigationLink(

                    workoutType.name,

                    value: workoutType

                )

                .padding(EdgeInsets(top: 15, leading: 5, bottom: 15, trailing: 5))

                .navigationDestination(for: HKWorkoutActivityType.self) { workoutType in

                    SessionPagingView()

                }

                .onChange(of: presentedWorkout) { _ in

                    guard let workout = presentedWorkout.last else { return }

                    workoutManager.selectedWorkout = workout

                }

            }

            .listStyle(.elliptical)

            .navigationBarTitle("Workouts")

            .onAppear {

                workoutManager.requestAuthorization()

            }

        }

    }

https://developer.apple.com/documentation/swiftui/navigationstack

When click the list, the stack puts the HKWorkoutActivityType data in the presentedWorkout array. So you can use onChange(of) to catch the workout your selected, and set this value to Workoutmanager.selecteWorkout.

You can also use the simultaneousGesture feature to select your workout type and replace the deprecated tag/selection.

With your code as example, something like this will do the trick.

List(workoutTypes) { workoutType in
    NavigationLink(
        workoutType.name,
        destination: SessionPagingView()
    )
    .simultaneousGesture(TapGesture().onEnded {
        workoutManager.selectedWorkout = workoutType
    }
}