8.2 KiB
Claude iOS Plan — Part 2 (build to parity) + Part 3 (iOS QA)
Program: Part 1 = Android QA (
ClaudeQAPlan.md, in progress) → Part 2 = build the iOS app to Android's CURRENT state → Part 3 = run the same QA passes on iOS. This doc covers Parts 2 & 3.
Decisions (locked)
- Approach: native SwiftUI + Firebase iOS SDK (the
iphone/scaffold already chose this: SwiftPM + XcodeGen, feature dirs mirroring Android,ARCHITECTURE_AUDIT.md). Do NOT switch to KMP/Compose-MP. - E2EE: full Tink-compatible crypto — iOS must byte-match Android's wire formats so an iOS↔Android couple decrypts each other. (Overrides the audit's "skip E2EE for MVP" note — encryption is the cornerstone.)
- Scope: working parity build (Simulator + real device, TestFlight optional). No App Store submission.
⚠️ Hard constraint — macOS required (we are on Linux)
iOS build/run/QA needs macOS + Xcode + iOS Simulator; there is no adb-equivalent from Linux. Therefore:
- On this Linux box I can only: author/refresh Swift source, refresh the audit, write the plan/specs, and reason about the code. I cannot compile, run the Simulator, or do interactive/visual QA here.
- Everything that requires building or running is deferred to a Mac (a Mac, cloud-Mac, or macOS CI runner).
- Part 2 done-on-Linux = "code authored + self-reviewed"; Part 2 truly done = builds green in Xcode and runs on Simulator + device (on a Mac). Part 3 (iOS QA) is entirely macOS-gated.
Current iOS state (as found)
iphone/ = scaffold only: ~19 stub Swift files, Crypto/ empty, most feature dirs empty, one FirestoreService.
ARCHITECTURE_AUDIT.md is thorough but generated from Android v0.2.0 — it predates Messages/conversations,
chat media (photo/GIF/sticker/voice), reactions/typing/read-receipts/pagination, and couple-shared premium (A-001).
So this is a near-full build, not a top-up.
Part 2 — Build iOS to current Android parity
2.0 Refresh the audit to CURRENT Android (do first)
Update iphone/ARCHITECTURE_AUDIT.md from the live Android code: add the Messages tab (conversations inbox +
conversation chat: text/photo/GIF/sticker/voice, reactions, delete/tombstone, typing, read-receipts, pagination,
chat-head bubble), the conversations Firestore model, the new notification types, and couple-shared premium
(CouplePremiumChecker — either partner premium unlocks all features for both). Re-tally screens/models/functions.
2.1 Project + dependencies (Mac)
XcodeGen/project.yml; SPM: Firebase Auth/Firestore/Functions/Messaging/Storage, RevenueCat (purchases-ios),
GoogleSignIn; GoogleService-Info.plist; entitlements (push, keychain); App Check via DeviceCheck/App Attest
(Android uses Play Integrity); APNs setup for FCM.
2.2 Core infrastructure
AppDependencies (manual DI), AuthService + AuthRateLimiter (mirror Android limits), FirestoreService +
per-domain services (User/Couple/Invite/Question/Answer/Game/Date/Conversation), NotificationService
(APNs+FCM + deep-link routing mirroring PartnerNotificationType), navigation (TabView + NavigationStack per tab,
AppRoute mirror, deep-link handler), CloserTheme (light+dark), reusable components.
2.3 E2EE — full Tink-compatible crypto (HIGHEST RISK; cornerstone)
Implement byte-compatible Swift crypto for every Android wire format:
SealedAnswerEncryptor(AES-256-GCM, 96-bit IV, AAD"{coupleId}|{questionId}|{userId}",sealed:v1:),FieldEncryptor(enc:v1:, AAD=coupleId) — used by messages/previews/dates,AnswerCommitment(SHA-256,sha256:),UserKeyManager(ECIES P-256 HKDF-HMAC-SHA256 AES128-CTR-HMAC, keypair in Keychain,pub:v1:),ReleaseKeyEncryptor(keybox:v1:),RecoveryKeyManager(Argon2id m=46MiB, t=3, p=1, BIP39-style wordlist),CoupleEncryptionManager, Keychain-backed key stores.- Interop test harness (the acceptance gate): (a) decrypt Android-produced fixtures (
sealed:v1:/enc:v1:/keybox:v1:) on iOS; (b) have Android decrypt iOS-produced ciphertext; (c) verify an Android-generated recovery phrase unlocks on iOS and vice versa. Wire formats must match byte-for-byte. If a format can't be matched with CryptoKit, use a vetted Swift lib (e.g. swift-sodium/SwiftArgon2) — never a non-interoperable shortcut. - Golden vectors for the deterministic primitives (checked into both repos):
AnswerCommitmentSHA-256 andRecoveryKeyManagerArgon2id (fixed salt/params) must produce identical bytes on both platforms — assert in unit tests on each side. AEAD/ECIES use random IVs so they can't be golden-matched; cover those with the round-trip harness above. Generate the Android fixtures now (on Linux) so iOS has them ready.
2.4 Screens & features to parity (~48 + new messaging)
All routes from the refreshed audit's screen map, including the NEW Messages experience (inbox + conversation
with full media/reactions/typing/read-receipts/pagination) and the games (full, not stubs), dates, wheel, settings,
paywall. Mirror the Android UX + the couple-shared premium gating (route gated features through the iOS
EntitlementChecker couple-shared equivalent).
2.5 Build & smoke (Mac)
Xcode build green; run on Simulator + a real device; smoke each major flow (auth → pair → home/today/play/messages/ settings) with no crashes. This step + 2.1/2.5 runs on a Mac.
Part 3 — iOS QA (reuse ClaudeQAPlan.md passes A–E on iOS)
Same methodology, severity scale (P0–P3), report-only→fix→re-QA-until-flawless loop, continuity (use
ClaudeReport-iOS.md + ClaudeQACoverage-iOS.md + run-state header), and autonomous run-to-completion mode.
macOS-gated. Tooling adaptation from Android:
| Android (Part 1) | iOS (Part 3) |
|---|---|
adb install/tap/screencap |
xcrun simctl install/io screenshot; XCUITest or idb to drive taps |
| 2 emulators (5554/5556) | 2 iOS Simulators (or Simulator + device) for the two partners |
adb logcat FATAL/ANR |
xcrun simctl spawn booted log stream / Console crash logs |
cmd uimode night for theme |
Simulator Appearance (light/dark) per device |
premium toggle via set_premium.js |
same admin script (server-side) |
Run all passes on iOS:
- A couple-shared premium (gates + paywall), B each game one full playthrough on both, C visual light+dark all screens + navigation-from-every-entry + back-stack/double-back, D security/encryption, E notifications (APNs/FCM delivery + tap-to-open every type).
- NEW — cross-platform pass (X): a mixed couple (Android + iOS) — messaging, answers, dates, premium, and notifications work cross-platform, and E2EE decrypts both ways (the Part 2 interop gate, verified live on real paired devices). This is the make-or-break for the cornerstone.
- iOS-native dimensions (add to the passes): Dynamic Type (largest sizes), VoiceOver, safe-area / notch /
Dynamic Island, multiple device sizes (SE → Pro Max; iPad if supported), edge-swipe-back gesture + interactive
pop (the iOS "double-back"/nav-stack analog),
scenePhasebackground/foreground, dark/light. - Real-device / sandbox needs: App Attest/DeviceCheck and APNs push don't fully work on the Simulator —
use a real device (or
xcrun simctl pushwith a payload for local notif routing); RevenueCat IAP needs a StoreKit config file or a sandbox Apple ID. Plan Pass A/E around this.
Definition of done
- Part 2: iOS builds green + runs on Simulator + device at feature parity with current Android; E2EE interop harness passes (Android↔iOS decrypt both ways).
- Part 3: an iOS QA round is flawless (zero P0–P2, D+E clean, every game fully played, nav/back verified) AND the cross-platform pass X is clean.
What can proceed now (Linux) vs needs a Mac
- Now (Linux): 2.0 audit refresh; author Swift for models/services/crypto/screens; write iOS QA tooling scripts; prepare Android-produced crypto fixtures for the interop harness.
- Needs a Mac: 2.1 project/deps, all compiling, 2.5 build+run, and all of Part 3. Surface this as the blocker when execution reaches it (per the autonomous-mode rule: do all non-gated work first, then flag the Mac requirement).