How does NavigationStack work, and how do NavigationPath and navigationDestination fit together?

8.6.2 · L3 · Interview

Predict first

What is the path’s count after pushing two values of different types and popping one?

import SwiftUI

var path = NavigationPath()
path.append(1)
path.append("a")
print(path.count)
path.removeLast()
print(path.count)
  • 2, then 1
  • 1, then 0
  • 2, then 2

Mental model. The path is the browser’s history stack, in your hands: you can add a page, drop the last one, or replace the whole history from code.

NavigationStack shows a root view and a stack of pushed destinations, and, unlike the older NavigationView, its stack is state you can read and write. You give it a path: either a typed array ([Route], for one kind of destination) or a NavigationPath (a type-erased list that can hold values of different Hashable types). Pushing means appending a value to the path; popping means removing; going home means emptying it. Values are turned into screens by .navigationDestination(for: Route.self) { route in … } modifiers declared inside the stack, and the stack looks up the matching destination by the value’s type. A NavigationLink(value:) appends a value for you.

That design makes deep links and state restoration straightforward: set the path from a URL, a push notification or saved data (NavigationPath is Codable if its elements are), and keep routing logic in a router or model object that owns the path. Define destinations at the stack’s root level (not inside lazy rows, where they may not be found), keep route values small and hashable (ids, not whole objects), and fetch the data in the destination. NavigationSplitView gives the two- or three-column version for iPad and Mac, which collapses to a stack on compact width, and each tab in a TabView usually has its own NavigationStack. Remember that a pushed view’s @State is created on push and destroyed on pop, and that for UIKit interop the stack is backed by a UIKit navigation controller on iOS.

The path is just a list of values; appending pushes and removing the last pops.

Common traps

  • Declaring navigationDestination inside a lazy row or a conditional. It may not be registered when needed; declare it on the stack’s content.
  • Putting whole model objects in the path. Prefer ids so the path stays small, hashable and restorable.

They might follow up with

  • How do you handle a deep link? Parse the URL into route values and assign them to the path.
  • Can the path hold mixed types? Yes with NavigationPath; a typed array holds one type.

Related questions

  • 8.6.1 How do List, ForEach and the lazy stacks differ, and what do they need from your data?
  • 8.4.1 When do onAppear, task and onChange run, and which one do you use for async work?
  • 7.4.1 How does UINavigationController manage its stack, and where do the bar items and back button come from?

← How do List, ForEach and the lazy stacks differ, and what do they…