A Convention Nobody Writes Down
Every routing provider hands back a list of steps, and every step has two obvious fields: an instruction and a distance. What the documentation rarely says is how the two relate. Is "turn right onto X" the thing you do at the start of the step's distance, or at its end? Both readings are self-consistent. Only one is true for a given provider, and if you guess wrong every turn is announced at the wrong place.
Pathfinder did not guess; it measured. On its first day (2026-10-01) it asked Apple's Korean routing through the Maps Server API, which serves the same data MapKit does, and read the steps against the geometry. The step "장항IC에서 … 왼쪽 차선 유지" carries the 1.5 km that end at 장항IC, and the final "목적지에 도착" carries the last 109 m. Then MapKit itself, run in the simulator with a drive replayed along Apple's own route geometry, confirmed it: "백마로로 우회전" was shown 150 m before the actual turn onto 백마로. The step's 150 m is the road leading up to the turn.
Read that step the tempting way, maneuver at the start, and Pippa says "우회전해" a full 150 m early, at an intersection that is not the one. On the interchange step the error would be 1.5 km.
Pin It Once: maneuverAlong
The architecture doc turns the measurement into invariant 3: a step's instruction is the maneuver at the END of the step. The code makes the invariant impossible to forget by giving NavStep a field that already holds the answer: maneuverAlong, the distance along the whole route where the maneuver happens. The measured example lives in that type's doc comment, so anyone editing it meets the evidence first.
The engine never looks at a step's distance to decide when to speak. It reads maneuverAlong - progress and nothing else. That means every provider must translate its own convention into this one number, and none of that translation leaks into guidance:
- Apple (
AppleMapsProvider.navRoute): take the last point of each step's polyline, project it onto the whole route inside a window that starts where the previous step ended, accept the match if it lies within 60 m of the line, never move backwards, and clamp to the route's length. If the projection fails, fall back to the running total. - Kakao has two conventions, and cwkPippa translates both before the phone sees them. A driving guide sits on the first vertex of the road it names (measured as 0.0 m on every guide of a real route). A walking step describes its stretch, so the maneuver at its start is read from Kakao's words and, where they are silent, from the line's own turn.
- Google guides itself: its trips never run through this engine, and its maneuvers arrive through its own feed.
The test fixture obeys the same rule: "East 3 km, left turn, north 2 km, arrive" carries the left turn at 3,000 m and the arrival at 5,000 m, and the first step, a departure placeholder with no words, is never spoken.
The Map Faces Where Dad Faces
The same discipline (decide once, as a value in the kit, test it on a Mac) also governs where the map looks. Dad's ruling was blunt: 3D alone is confusing. So the map is flat by default, with 3D one button away; it is heading-up; and Dad sits low on the screen, the way Kakao Navi does it.
FollowCamera is a pure value that answers two questions. Which way should the map face? Driving: the GPS course while moving at 2.5 m/s or more, otherwise the road's direction on the route at the current progress, otherwise the previous heading. Walking: course at a brisk 1.8 m/s, otherwise the compass, because a person turns on the spot and the phone knows. Never north by default; the old code fell back to north and spun the map at every red light. Where should the camera sit? Walking at 350 m; driving at speed × 40 m, clamped between 600 and 1,800 m so the highway shows more road ahead. The centre is pushed ahead of Dad by 0.12 × that distance in 2D (0.05 in the tilted view), and since the simulator showed the screen's height spanning about 0.55 × the camera distance, that puts him about 22 % below centre. Pitch is 0 flat and 55° in 3D.
While the drive is on route, Dad's marker sits on the snapshot's matched point, the fix snapped onto the line, so the arrow the map follows is the same position the voice is reasoning about.