Closer/ClaudeiOSPlan.md

121 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 **and now profile metadata
(`displayName` + `sex` in `users/{uid}`)**,
`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): `AnswerCommitment` SHA-256 and
`RecoveryKeyManager` Argon2id (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.
- **⛔ Profile-metadata decrypt-on-read (REQUIRED before iOS launch — R22).** Android now encrypts `displayName` +
`sex` under the couple key. iOS reads these raw today ([HomeViews](iphone/Closer/Home/HomeViews.swift),
[SettingsViews](iphone/Closer/Settings/SettingsViews.swift)) and will show `enc:v1:…` until it decrypts them on
read (tolerant of legacy plaintext; show a 🔒 placeholder when the key is missing) — mirror Android's
`FirestoreUserDataSource` chokepoint + the pairing/legacy **migration** and **unpair-revert**. Until this ships,
iOS shows the locked placeholder for name/gender (acceptable in dev; **not** for release).
### 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 AE on iOS)
Same methodology, severity scale (P0P3), 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), `scenePhase` background/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 push` with 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 P0P2, 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).