Walking is a mode, not another app
Dad's request was one sentence: "여행가서 도보로도 쓸 거야", I'll use it on foot when I travel. The tempting answer is a second screen, a second engine, a second set of rules somewhere in a WalkingViewController. Pathfinder's architecture says no, in invariant 11: walking is a mode, not another app. TravelMode is a two-case enum, driving and walking, and it rides the values that already flow everywhere. RouteOptions.mode carries it to the provider when a route is asked for; NavRoute.mode carries it back on every route the provider returns. Apple's child turns it into MKDirectionsTransportType.walking; Google's engine turns it into its session's travel mode; Kakao's walk comes through cwkPippa's maps door with mode: walking in the request.
From then on nothing asks the settings what mode Dad is in. Whoever holds the route reads route.mode. NavigationSession builds its engine with GuidanceConfig.forMode(route.mode); FeedGuidedTrip, the child that follows Google's engine, builds its feed with the same call; PromptComposer defaults to it. One value, read at every place it matters.
The numbers, one by one
GuidanceConfig.walking starts from the driving defaults and changes ten of them. Every change has a reason you can say out loud, and most of the reasons are one of three things: a person is slow, GPS error is large next to a person's pace, and a person on a pavement faces wherever they like.
| Setting | Driving | Walking | Why it differs |
|---|---|---|---|
| Stage plan (early / prepare / imminent) | 1000 / 300 / 90 m | 200 / 50 / 20 m | Speed. At about 1.4 m/s, 200 m is over two minutes of walking and 20 m is about fourteen seconds. A car at 50 km/h covers 300 m in about twenty-two. |
| Highway plan from | 19.4 m/s (70 km/h) | .infinity | No pedestrian reaches it, and no GPS spike should switch a walk to 2 km warnings. |
| Off-route distance | 45 m | 35 m | A footpath is narrower than a road, and the wrong street in an old town can be a building's width away. |
| Off-route minimum duration | 4 s | 10 s | At walking pace the fix's error is large next to the distance covered. Three misses in a row still have to happen; they must now also span ten seconds. |
| Wrong-way angle | 120° | 181° | A person may face any way on the pavement. No angle between two headings exceeds 180°, so this rule can never fire. |
| Course trusted from | 4 m/s | 1 m/s | Only used by the wrong-way rule, which 181° has switched off. |
| Arrival distance | 35 m | 20 m | A walk ends at a door, not at a kerb beside a car park. |
| Join distance | 150 m | 100 m | A car starts in a car park; a walker starts in a lobby or a square, usually closer to the path. |
| Minimum look-ahead | 500 m | 200 m | The matching window ahead of progress: a walker cannot have gone far since the last fix. |
| Chain distance | 180 m | 40 m | "이어서 …" joins the next maneuver only when it really is right after this one. |
Two defaults the walk keeps are worth noticing. offRouteConfirmations stays at three, and accuracyMargin stays at 20 m, so the off-route threshold is still max(35, accuracy + 20). On an open street with a good fix the walk is judged at 35 m; under tall buildings, where a phone may report 30 m of accuracy, it widens to 50 m by itself. GPS.gov puts a smartphone's open-sky accuracy at about 4.9 m and says it worsens near buildings, bridges and trees. A car outruns that error in a second. A walker needs several seconds to move as far as the error is wide.
Ten-metre steps
The driving plan never speaks a distance under 75 m: its shortest stage, imminent, is always said as "잠시 후", soon. A walk's prepare stage sits at 50 m, so SpokenDistance grew a rule for it: below 75 m, round to ten metres, never less than ten. A prepare line opened by a fix at 47 m says "50미터 앞에서"; one opened at 44 m says "40미터". Above 75 m nothing changed, which a test pins (90 m is still "100미터"), and the closed vocabulary the voice prefetches gained 10 to 70 m in tens.
What a walk does not hear or see
A walk hears no camera warnings. NavigationSession hands its camera watcher the bundled camera index only when route.mode == .driving, and passes nil otherwise, so no camera is placed on a walking route and no camera chunk is prefetched for it. A walk shows no traffic: Apple's map style is built with showsTraffic: mode == .driving. CarPlay refuses a walk outright ("CarPlay is for driving"). And the guided location segment changes its activity type: .fitness, which Core Location calls its pedestrian model, instead of .automotiveNavigation. A code review caught the first version using .otherNavigation, which is boats, trains and planes.
The bug that made the mode a default
The same review found a quieter failure. PromptComposer.start composes the opening line, "안내 시작할게. 도착까지 …", and then the next maneuver. It took an optional config, and every caller left it at the driving default. On a walk, a first turn 500 m away came out as a driving line, "500미터 앞에서 좌회전해", when the walking plan has nothing to say until 200 m. Now the parameter defaults to .forMode(route.mode), and the line becomes "다음 안내까지 500미터". The fix was not "remember to pass walking". It was making the route's own mode the thing you get when you pass nothing.