GPS Route Tracking
How Step FWD handles route segmentation, pause detection, accuracy filtering, and the battery trade-offs behind GPS tracking.
Tracking a walk on a map sounds simple until you actually try to build it. GPS signals bounce off buildings, tunnel through your pocket, and occasionally decide you’ve teleported 200 meters. Pauses create gaps that need to look intentional. Battery life fights against accuracy. And all of this needs to work while the app is in the background, with the user’s music playing and their phone in a jacket pocket.
Here’s how we built it.
Two-phase acquisition
When you start a walk, GPS doesn’t instantly know where you are. Consumer GPS takes a few seconds to acquire satellites, and the first readings are often inaccurate.
Step FWD uses a two-phase approach. During warmup, the location manager runs at maximum frequency with no distance filter — we want every update as fast as possible so the user sees their position lock in quickly. Once we have a reliable fix, we switch to tracking mode with a 5-meter distance filter. This means the system only processes a new location when you’ve moved at least 5 meters from the last one.
The 5-meter filter is the key battery optimization. Without it, the GPS radio fires roughly once per second regardless of movement. With it, a stationary user generates zero updates. For a walker covering about 1.4 meters per second, you get an update roughly every 3.5 seconds — more than enough for a smooth route line, at a fraction of the power cost.
Signal quality
Not all GPS readings are equal. We classify signal into four states: searching (no usable data), weak (10–20 meter accuracy), good (5–10 meters), and strong (under 5 meters).
Every incoming location gets checked for freshness. CoreLocation occasionally delivers cached locations from minutes ago — useful for some apps, misleading for ours. We reject any location older than 30 seconds. If no fresh update arrives within that window, a staleness watchdog degrades the signal state back to “searching,” ensuring the UI never shows a confident signal from stale data.
For route recording, we accept locations up to 20-meter accuracy (the “weak” threshold). This is a deliberate trade-off — in urban canyons or dense tree cover, rejecting everything below 10 meters would leave gaps in the route. A slightly wobbly line is better than a broken one.
Route segmentation
When you pause a walk, Step FWD stops the GPS entirely. No background tracking, no phantom updates. When you resume, the location manager restarts fresh.
This creates a natural segmentation problem. If you pause at a park bench, walk to a coffee shop, and resume walking, the route should show two separate segments with a visible gap — not a straight line from the bench to the shop.
Each GPS point is stored as a RoutePoint with latitude, longitude, timestamp, and a segmentStart boolean flag. The first point after a resume gets segmentStart = true. When rendering the route, the system reconstructs an array of segments by splitting on these flags. Each segment becomes its own polyline on the map, with intentional gaps between them.
This approach is minimal and robust. No complex clustering, no heuristic gap detection — just a boolean flag set at the exact moment the user resumes.
Background tracking
Walking is a background activity. The app needs to keep tracking even when the user switches to their podcast app or locks their screen.
We enable allowsBackgroundLocationUpdates and set activityType to .fitness, which tells CoreLocation to optimize for walking patterns. The status bar shows the blue location indicator so the user always knows tracking is active.
One critical setting: pausesLocationUpdatesAutomatically is set to false. CoreLocation has a built-in feature that pauses location updates when it thinks you’ve stopped moving. This sounds helpful, but it’s unpredictable — it might pause because you slowed down, or because the signal degraded temporarily, or for reasons that aren’t documented. For a walking app, we need consistent tracking until the user explicitly pauses. So we disable auto-pause and handle pause/resume ourselves.
Battery reality
GPS is the most power-hungry sensor on a phone. Here’s how the choices stack up:
The 5-meter distance filter is the biggest win. It reduces update frequency by roughly 70% for a walking pace compared to continuous updates, with no visible loss in route quality.
Setting activityType to .fitness hints to the system that movement is pedestrian, which can allow hardware-level optimizations in the GPS chipset.
We don’t use significant location changes or region monitoring — those are too coarse for route tracking. And we don’t use the magnetometer or accelerometer for dead reckoning — the complexity isn’t justified when GPS is already running.
The result is a walk that uses meaningful but not alarming battery. In our testing, a one-hour walk with active GPS tracking consumes roughly 3–5% of battery on modern iPhones, depending on signal conditions.
What we don’t do
We don’t snap routes to roads. The raw GPS trace is what you see. This means your route might wobble slightly on a straight road, but it also means walking through a park or an unmarked trail shows your actual path, not an algorithm’s guess about which road you were on.
We don’t smooth or interpolate between points. Each line segment connects consecutive GPS readings. The 5-meter distance filter naturally produces smooth-looking routes at walking pace without any post-processing.
We don’t upload routes anywhere. Every point is stored locally in SwiftData and stays on your device. There’s no server receiving your walking paths, no heatmap being built from your data, no anonymized aggregation. Your routes are yours.