The phone navigates; the wrist shows and taps
Dad's ask for the watch was one short line: "도보는 특히 워치도 지원해야 하고", walking especially has to work on the watch too. On a walk the phone lives in a pocket and the wrist is where he looks. What he did not ask for, and what Pathfinder refuses to build, is a second navigator. The phone holds the route, the voice and the link to Pippa. The watch shows what the phone says and sends Dad's taps back. Architecture invariant 17 says it in one line: the phone is the navigator; the watch shows and taps.
That split decides the plumbing, because two different kinds of thing cross the pairing. Going down is state: what the guidance looks like right now. Going up are acts: Dad tapped "end", Dad tapped "walk home". State wants the newest version and nothing else; yesterday's "240 m to the turn" is worthless. An act wants to arrive, once, and soon. One transport cannot be good at both, so Pathfinder uses two.
Which Apple door, and which one it isn't
Everything rides WatchConnectivity's WCSession. Three of its calls matter here:
| Call | What it promises | Used for |
|---|---|---|
updateApplicationContext(_:) | A dictionary that replaces the previous one. Delivered when the system sees an opportunity; readable at any moment from receivedApplicationContext, a cold launch included. | The glance, down |
transferUserInfo(_:) | Queued on the other device, delivered in order, carried on while the app is suspended. | A command's durable record, up |
sendMessage(_:replyHandler:errorHandler:) | Immediate, only while the counterpart is reachable. Sent from the watch, it wakes the iOS app in the background. | The ring that wakes the phone, up |
Apple's own page for the first call says it is "well suited for updating your app's glance", and that is exactly the name the phone's value got.
Two things are absent from the code, and they are worth naming because the family's word for the wrist lane, "handoff", invites the wrong guess. There is no NSUserActivity anywhere in Pathfinder: Apple's Handoff lets a person pick up an activity on another device, and it is not a feed a phone streams turns into. There is no Live Activity either. The architecture's known-limits table lists one (lock screen, Dynamic Island, the watch's Smart Stack) as the next candidate, not as something built.
Down: one NavigationGlance
NavigationGlance lives in PathfinderKit, so its rules run in the Mac suite. One value carries the whole wrist:
- Where the trip stands:
state(idle, guiding, off_route, rerouting, arrived),mode(walking or driving) and the destination's name. - The turn ahead:
maneuverwith its kind, its words and its distance. - How far, how long: remaining distance, remaining time and an ETA.
- On a drive: the next speed camera's limit and distance, and the speed.
- The tap: a
cuewith a serial number (lesson 4). - Pippa: the talk's phase and a talk line cut to 140 characters, and whether the guidance voice is muted.
- Starting points: up to 12 of Dad's saved places, so a walk can start from the wrist.
- The way: up to 160 route points of what is left of the route, and where the phone puts Dad on it.
- A sentence for Dad when something needs him, plus the bookkeeping: an id,
updatedAt, and whether the phone app is on screen.
The route only rides along when its geometry may leave the provider's own map. Apple's routes may. Kakao's and Google's providers declare routeIsPortable: false, so on those trips the wrist shows the turns alone and the map page never appears.
The wire is property-list values with every absent field omitted. That is not tidiness. An Optional.none bridges to CFNull, which is not a property-list value, and a dictionary holding one cannot be sent: updateApplicationContext throws, and transferUserInfo reports it only later, through its finish callback (the warning below). CwkKitWrist's WristEnvelope.seal checks every key and throws instead, naming the field.
The cadence: at once, or every two seconds
A context per GPS fix is traffic the wrist does not need. isSignificantChange(from:) splits the glance in two. A change of state, mode, destination, the maneuver's kind or words, the cue, the talk phase, the mute, the places, the problem sentence, the phone app reaching or leaving the screen, or the camera limit is significant and goes out at once. Everything else (the distances, the time, the speed, the dot and the line) is a moving number and goes out at most every publishEvery = 2 seconds. It is a gate on calls, not a timer: the phone publishes when something happens, and the gate decides whether this one is worth a context.
Up: WristCommand through the capture lane
Dad's taps become a WristCommand: go(placeID, mode), end, toggleVoice, talk, endTalk and pull. They ride CwkKitWrist's capture lane, the family's shared watch plumbing, which does three things in order. It writes the command to the watch's own outbox file first. It hands the same envelope to transferUserInfo, the durable record. Then, if the phone is reachable, it sends it again with sendMessage, the ring, because a transfer alone launches nothing on the phone. The watch lets go of an entry only when the transfer reports it landed. Durable, and able to wake a phone asleep in a pocket: the right shape for an act. Lesson 2 shows what that shape costs on the receiving end.