T22/T24/T26: uploader, integration tests, and release readiness
T22 -- TelemetryUploader on package:http with MockClient standing in for MockWebServer, tested against a real in-memory Drift database rather than a fake DAO. Upload runs on its own timer, injected as a callback so the engine has no opinion about HTTP and tests need no network. Config on shared_preferences with a hand-rolled UUID v4. Still no UI for the endpoint, exactly as in the native app. 7 tests. T24 -- integration_test/app_test.dart, 4 tests passing on the iOS simulator. These cover what widget tests cannot: Drift opening against real platform storage, plugin registration, go_router driving a real Navigator, cold start. T26 -- RELEASE-IOS.md. Usage strings are specific rather than generic, which is the leading Guideline 5.1.1 rejection cause; privacy-label answers decided; a pre-submission list covering the bundle-id switch back to com.rippr, the still default app icon, a release build, and a demo video for review notes. T25 written up as REAL-RIDE-CHECKLIST.md but outstanding by nature. Two items decide real things: force-stopping mid-ride on Android confirms the 111 km crash-gap bug is fixed, and parking 15 minutes mid-recording on iOS decides whether geolocator is sufficient or the paid engine is needed. Two prefer_initializing_formals lints suppressed with a reason: Dart forbids a named parameter beginning with an underscore, so the suggested fix will not compile. 178 tests passing, analyze clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
78
docs/port/RELEASE-IOS.md
Normal file
78
docs/port/RELEASE-IOS.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# T26 — iOS release readiness
|
||||
|
||||
What App Review will look at, and what is already in place. Background-location apps get
|
||||
more scrutiny than most, and the research in `docs/PORT_RESEARCH.md` names unclear
|
||||
privacy disclosure as the leading rejection cause.
|
||||
|
||||
**Status: configured, not submitted.** Nothing here has been through review.
|
||||
|
||||
---
|
||||
|
||||
## Guideline 5.1.1 — data privacy and transparency
|
||||
|
||||
**Rejection cause:** generic usage strings like *"This app needs location"*.
|
||||
|
||||
Already in `ios/Runner/Info.plist`, written specifically:
|
||||
|
||||
| Key | Says |
|
||||
|---|---|
|
||||
| `NSLocationWhenInUseUsageDescription` | Records the GPS track of your ride so you can see route, speed and distance afterwards. **Nothing is recorded until you press Start.** |
|
||||
| `NSLocationAlwaysAndWhenInUseUsageDescription` | Keeps recording while the phone is in your pocket or the screen is off, so a ride you started is captured beginning to end. **Recording stops the moment you press Stop.** |
|
||||
|
||||
Both name what is collected, why, and when it stops. That last clause matters — it is the
|
||||
difference between "an app that wants your location" and "a recorder you control".
|
||||
|
||||
## App Privacy nutrition labels
|
||||
|
||||
Declare, and make sure it stays true:
|
||||
|
||||
- **Location → Precise Location**, linked to the user? **No.** Used for **App
|
||||
Functionality** only.
|
||||
- **No data collected for tracking**, no advertising identifiers, no analytics SDKs.
|
||||
- **Data is not transmitted off-device by default.** The uploader exists but has no UI and
|
||||
no endpoint configured; it is inert unless someone sets one deliberately.
|
||||
|
||||
> If a server ever ships (the v3 group-ride idea), these labels must change **before** it
|
||||
> does. Undisclosed background transmission is a straightforward rejection.
|
||||
|
||||
## Guideline 2.1 — completeness and background stability
|
||||
|
||||
The exposure is crashing on background resume. Mitigations in place:
|
||||
|
||||
- Recording state lives in the database, so resuming after suspension reads real state
|
||||
rather than guessing.
|
||||
- `restoreAfterProcessDeath` runs at startup and is covered by four tests, including the
|
||||
crash-gap guard.
|
||||
- Every upload failure path is swallowed; the network cannot stop a recording.
|
||||
- Database write failures are caught per batch — losing points beats losing the app.
|
||||
|
||||
**Still unproven:** what iOS actually does when the app is suspended while stationary.
|
||||
That is item **I3** in [REAL-RIDE-CHECKLIST.md](REAL-RIDE-CHECKLIST.md) and it must be
|
||||
answered before submission.
|
||||
|
||||
## Guideline 4.2 — minimum functionality
|
||||
|
||||
Not a realistic risk: native Flutter UI, real hardware integration, offline-first storage,
|
||||
no web view anywhere.
|
||||
|
||||
---
|
||||
|
||||
## Before submitting
|
||||
|
||||
- [ ] **Switch the bundle id** from `com.rippr.port` to `com.rippr` (T27). The `.port`
|
||||
suffix exists only so the native app can be installed alongside during the port.
|
||||
- [ ] `CFBundleName` is still the generated lowercase `rippr`; `CFBundleDisplayName` is
|
||||
already `Rippr`. Make them consistent.
|
||||
- [ ] App icon and launch screen — still Flutter defaults. The native app's adaptive icon
|
||||
artwork lives in `~/dojo/rippr/design/` and needs re-exporting at iOS sizes.
|
||||
- [ ] Run [REAL-RIDE-CHECKLIST.md](REAL-RIDE-CHECKLIST.md) on a real iPhone, especially I3.
|
||||
- [ ] Record a demo video of a real ride for the review notes. Background-location apps
|
||||
are frequently asked to justify the entitlement; a video pre-empts a rejection round.
|
||||
- [ ] `flutter build ipa --release` and confirm the release build works — everything so
|
||||
far has been debug.
|
||||
|
||||
## Known parity gap to disclose internally
|
||||
|
||||
The notification cannot carry actions, so the native app's Pause/Resume buttons in the
|
||||
shade are absent on both platforms. Not an App Review issue; it is a feature difference
|
||||
the T27 audit must record.
|
||||
Reference in New Issue
Block a user