4.5 KiB
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:
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 <sim-id>
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.