Can you open Harbor → v1.4.0 → build 418 without three taps?
With a value-typed path you can: one write, three levels. With tap-only NavigationLinks and no path binding, you cannot — and a relaunch will not remember where you were either. This bench puts both next to each other and prints the depth on the cards.
Root — both at zero
| Side | Depth | Detail |
|---|---|---|
| PATH | 0 | [] |
| LINKS | 0 | stuck at root |
PATH is a NavigationStack(path:) over [ShipRoute]. LINKS is a NavigationStack with nested links and no path binding — the control.
Deep — one append, three levels
path = [.app, .version, .build] // Harbor › v1.4.0 › build 418
// Links has no path to write — depth stays 0
| Side | Depth | Detail |
|---|---|---|
| PATH | 3 | Harbor › v1.4.0 › build 418 |
| LINKS | 0 | stuck at root |
Same moment, same screen. The mint card moved; the rose card did not.
NavigationLink(_:value:) does not embed a destination view. navigationDestination(for:destination:) teaches the stack what to push for each type. Programmatic path = … and a user tap share that mapping — that is why one append can equal three taps.
Pop — clear the array
path = [] // removeAll — back to rootObserved: PATH 0, tape [], phase POP, status removeAll. LINKS was already at root.
Save, then relaunch
Seed mode deep-links, then writes the encoded [ShipRoute] to UserDefaults. Observed: PHASE SAVED, PATH 3, LINKS 0, status defaults ← path.
A fresh process with -mode reload reads the array back into path. Observed: PHASE RESTORED, PATH 3, tape Harbor › v1.4.0 › build 418, LINKS 0, status path ← defaults.
Without a path binding there is no array to encode. After kill, the control wakes at root every time — that is the point of the rose card on the relaunch still.
Mini-exercise
Change deepPath to only [.app, .version]. Re-run seed → reload. Confirm PATH settles at 2 and the tape stops at v1.4.0.
Challenges
- Swap
[ShipRoute]for aNavigationPathand deep-link a second type (e.g. a review id) besideShipRoute. - Add a "Pop one" button that does
path.removeLast()and photograph depth 3 → 2 → 1. - Drive the same path from a simulated URL (
harbor/v1.4.0/418) parsed into[ShipRoute]. - Put the bench back inside an outer
NavigationStackand photograph how the destination steals the window — then fix it the way Launch Lab does for-benchlaunches. - XCUITest: after
-mode reload, assert static text containsRESTOREDandPATH 3.
Key Points
NavigationStack(path:)shares stack state with your code.- Observed deep-link: PATH 3 / LINKS 0 for Harbor › v1.4.0 › build 418.
- Observed pop: PATH 0, status removeAll.
- Observed relaunch: PHASE RESTORED, PATH 3, LINKS 0.
navigationDestination(for:)maps values to screens for taps and appends alike.- Nested
NavigationLinks without a path cannot be deep-linked or restored. - An outer
NavigationStackaround a path-bound bench can swallow pushes — direct-benchlaunch avoids that. - Chapter 13's lesson applies: if you want a relaunch to wake deep, persist the path.
Next up: gestures — drag, long-press, and simultaneous/sequenced composition, with a live readout of which gesture won — including scroll-vs-drag as the control.
Read next
Ship your apps faster
When you're ready to publish your Swift app to the App Store, Simple App Shipper handles metadata, screenshots, TestFlight, and submissions — all in one place.
Try Simple App Shipper