# iPhone verification — what a Mac can prove, and what needs a phone Scoped and then executed. Everything in the first table has been **run**; everything in the second cannot be done without hardware and a rider. Reproduce with: ```bash xcrun simctl location booted start --speed=15 --interval=1 \ 51.0447,-114.0719 51.0530,-114.0719 51.0610,-114.0800 51.0700,-114.0900 & ( for i in $(seq 1 90); do xcrun simctl privacy booted grant location-always com.rippr.port; sleep 1; done ) & flutter test integration_test/ride_simulation_test.dart -d ``` --- ## The finding that matters most **The iOS simulator moves, but still reports zero speed.** `simctl location start --speed=15` genuinely interpolates between waypoints over time — unlike Android's `adb emu geo fix`, which teleports. That looked like it might finally make speed testable locally. It does not: ``` RIPPR-PROBE fixes=8 speeds=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ``` Eight fixes, every one zero. Measured, not assumed — the assumption was worth attacking and it survived. The consequence is visible in a real recorded ride: ``` trip=1 points=18 distanceM=245.5 maxSpeedKmh=0.0 movingMs=0 segments=1 ``` **245 metres travelled, zero moving time.** Distance comes from consecutive positions, so it works. Moving time comes from `speedKmh >= 1.5`, so with speed pinned at zero every sample falls under the noise floor. That is correct behaviour on bad input, and it is exactly why `REAL-RIDE-CHECKLIST.md` cannot be skipped. --- ## ✅ Verified locally, on a Mac | What | How | Evidence | |---|---|---| | App builds and launches on iOS | `flutter build ios --simulator`, `simctl launch` | Runs | | The real UI renders | Screenshot of the record screen | Wordmark, SPEED headline, Ready state, 72dp button all correct | | **The permission dialog and its usage string** | Screenshot | *"Rippr records the GPS track of your ride… Nothing is recorded until you press Start."* — the Guideline 5.1.1 item, confirmed rendering | | Drift opens against real iOS storage | Integration test | `rippr_db.sqlite` + WAL created in the app container | | Plugin registration resolves | Integration test | geolocator, path_provider, shared_preferences all load | | CoreLocation delivers fixes | Integration test | 18 fixes in ~15 s | | **Distance accumulates from real movement** | Integration test | 245.5 m, latSpan 0.00197° — consistent | | A trip persists and is listed | Integration test | `completedTrips=1`, Card present | | Segments are created | Integration test | `segments=1` | | Trip detail renders, including the map | Integration test | `DISTANCE` found on screen | | Navigation record → rides → detail | Integration test | All transitions land | | Cold start with no ride | Integration test | Lands idle, no crash | | App icon under the iOS mask | Rendered preview | Ring and orange accent survive the superellipse | ## ⛔ Needs a real iPhone | What | Why a simulator cannot | |---|---| | **Max speed, average moving speed** | CoreLocation reports 0.0 — measured above | | **Moving time** | Derived from speed; reads 00:00:00 locally even after 245 m | | **Speed colouring on the map** | Every path renders in one colour | | **Elevation gain** | Simulated altitude has no correlated GPS error; the ~30 m drift question is unanswerable here | | **Background suspension when stationary (I3)** | The simulator does not model iOS's power-management suspension. **This decides whether `geolocator` is sufficient or the paid engine is needed.** | | **Screen-lock continuation** | Real background lifecycle only | | **Phone call / interruption handling** | No telephony | | **Battery drain** | No meaningful power model | | **The blue background-location indicator** | Real background mode only | | **Real GPS accuracy and dropouts** | Simulated fixes are perfect; the accuracy gate never rejects anything | | **App Store review** | Requires submission | ## 🟡 Gap in local coverage, worth naming **No screenshot of the iOS trips list or detail screen.** They are verified *functionally* — the integration test asserts the Card and the `DISTANCE` readout are present — but not *visually*, because tapping the iOS simulator is not scriptable the way `adb shell input tap` is, and every fresh install re-prompts for location, which sits over the UI during the capture window. Android's equivalents were checked visually. On iOS this is worth a manual look next time the app is built for a device: launch it, record a short ride by hand, and eyeball the list, the detail stats and the map.