NavigationStack and deep links

SwiftUI Mid-level Swift 5.7+ iOS 16+ Reviewed By Alok Singh

Pick the fix

myapp://product/42 must open product 42 on top of Home. Which approach?

Options
Hint 1 of 1

What drives NavigationStack?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Keep @State var path: [Route] = []; in .onOpenURL, set path = [.product(42)]; map .navigationDestination(for: Route.self)

Correct. Navigation is data, so a deep link is just a new path.

Why not the others?
  • B NavigationLink(isActive:) with a Bool per screen. Deprecated since iOS 16, and it needs a flag per destination.
  • C Present the product in a sheet. That changes the UX and doesn't build the back stack.
  • D Find the UINavigationController and push a hosting controller. It fights SwiftUI's state and breaks easily.

30-second answer

NavigationStack is driven by a path: an array of Hashable route values (or a NavigationPath). .navigationDestination(for:) maps each route type to a screen. To deep link, parse the URL into routes and set the path. Because the path is data, you can save it, restore it and test it.

Practise saying it · 45 s. No recording, just a timer

Explain NavigationStack and deep linking.

Say your answer out loud, then check which of these points you covered:

  • Path of Hashable routes
  • navigationDestination maps types
  • Deep link = set the path
  • Codable for restoration

Deep dive

Use [Route] with an enum when routes are known; NavigationPath when types vary.

Keep the path in a router model so non-view code can navigate.

Make routes Codable to restore navigation after the app is killed.

NavigationSplitView handles iPad and Mac sidebars.

Router Type-checked · Swift 6.4
enum Route: Hashable, Codable { case product(Int), settings }

struct RootView: View {
    @State private var path: [Route] = []
    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: Route.self) { route in
                    switch route {
                    case .product(let id): ProductView(id: id)
                    case .settings: SettingsView()
                    }
                }
        }
        .onOpenURL { url in
            if let id = Int(url.lastPathComponent), url.host == "product" { path = [.product(id)] }
        }
    }
}

Common mistakes

  • Using the deprecated NavigationLink(isActive:).
  • Putting navigationDestination inside a lazy list.
  • Nesting NavigationStacks.

Senior take & follow-ups

Seniors treat navigation as state, which makes deep links, restoration and tests straightforward.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you pop to root?

    Show hint

    Set the path to empty.

  2. How do you restore state after the app is killed?

    Show hint

    Encode the path's codable representation in SceneStorage.

Suggest a fix if something here is wrong or out of date.