Repository navigation
iOS round 6: Live Activities, Sign in with Apple, in-app purchase, Siri and Handoff - #270
Conversation
Generate, Attack, Brainstorm and Factoids start a Live Activity while the model writes: Ollie in the step's pose, research, write and check, the parts written and the time since it started. When the app leaves the screen the activity goes stale shortly after and says to open DebateOwl, because iOS suspends the app and the stream with it. A finished run stays on the Lock Screen for a while when the student is away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From iOS 17 the timer's Live Activity has Pause or Resume and Next speech, which act on the clock it shows as the timer screen's buttons do. When the app leaves the screen with a clock running, a time-sensitive local notification is set for the moment it runs out, because the app's own signals stop when iOS suspends it. Notification permission is asked the first time a clock starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apple's own button and sheet, with a hashed nonce and Supabase's id token sign-in; the name Apple gives on the first sign-in is saved to the account. Google runs the website's OAuth flow in an authentication session with PKCE. Each shows only in builds that can finish it: app.config.ts turns Google on for development builds and Apple for builds signed by DebateOwl's own account (DEBATELAB_APPLE_ACCOUNT), which also keep push and add the Time Sensitive and Handoff entitlements. Every other development build drops the Sign in with Apple entitlement the package's plugin adds, as it drops push. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both are in Expo Go. The review prompt is Apple's own, offered a few seconds after the third finished result opens and at most every 120 days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app asks for it on every live run (notifyWhenReady); the ai function pushes once the streamed answer is stored and only for an answer, with fixed copy that names the tool. The app hides it while it is on screen. A tapped notification now opens its screen: a judged Sparring round or the timer. Not deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Additive; the placeholders stay for the database tests. Not applied anywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local StoreKit 2 module (modules/store-kit) and purchases.ts: Apple's sheet with the account's id as appAccountToken, then app-store-purchase, and the transaction is finished only once the server has it. Unfinished ones, Ask to Buy approvals, renewals and redeemed offer codes are delivered at launch, on sign-in, on every update and in the foreground, one at a time. The plans sheet shows StoreKit's prices with the renewal terms, Restore, Redeem a code and Manage subscription; You and the limit sheet open it where the build has StoreKit. Until App Store Connect and the server are set up it says purchases are not available yet. Strings for this round in en, es and hu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/apple-platform.md maps what runs in Expo Go, a development build and a build signed by DebateOwl's own account, and lists what the Apple account, App Store Connect, Supabase and the website need to switch each part on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Six App Intents with App Shortcuts phrases in English, Spanish and Hungarian: start the timer, ask the coach (the question is filled in, not sent), start a debate, write a case for a motion, open the Library, spar. A config plugin compiles them into the app target, where Xcode extracts their metadata. Handoff offers the open screen through the app's own NSUserActivity module: a Mac continues on the matching debateowl.com page, another iPhone or iPad opens the same route. Development builds only; the Expo Go config is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A Transaction.updates version is sent as it comes, even for an id already delivered, so a refund reaches the server; the same version is not sent twice. - Offer codes are not offered: Apple gives a redeemed code no account id, so the server cannot apply it. Restore counts only purchases made out to another account as theirs. - A generating activity whose owls arrived while the app was off screen starts when it comes back, as iOS refuses to start one in the background. - The plans sheet does nothing once closed, so a late purchase cannot pop another screen. - Ask's q and Generate's motion take the first value when a link repeats them. - Handoff opens History for a result only the other device holds, and the website's free tools hand back to the timer, speech time and motions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app sends its install id with notifyWhenReady, and the push goes only to that installation's token rows, so a case written on the iPad does not ring the iPhone. Without an install id no push is sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
|
Local |
… version The timer's round could not tell which clock was on screen once nothing ran, so pausing prep from the Lock Screen ended the activity (or showed the paused speech) and Resume could not restart that prep. The session now keeps the bank last paused while it ran as held; the activity shows it paused and its Resume restarts it, until another clock starts, the bank runs out or the format changes. Stored with the round. Purchases skipped any transaction whose id this run had already delivered, so a new unfinished version of it (a refund, a new expiry) was counted as applied without being sent or finished. The cache is now keyed by the signed version, and updates and pending deliveries share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Local |
The server already has that version, so it is not sent again; an update repeating a version the server took is skipped as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/mobile/AGENTS.md # apps/mobile/src/app/sparring/index.tsx # apps/mobile/src/lib/native-capabilities.test.ts # apps/mobile/src/lib/native-capabilities.ts # docs/development.md
Round 6 of the iOS app: the Apple platform features. It builds on #266 (rounds 3 and 4), so the base is
native-ios/parity-r3r4-combined. Round 5 (native-ios/r5-speed-polish) owns quick actions, Spotlight indexing, the share extension and iPad keyboard shortcuts, so none of those are here.Nothing here is deployed. The migration, the
aifunction change and the new push copy only exist in this PR. Every native part needs a new development build, and none was started.What it adds
ai_run_results.aifunction pushesresult_readyonce a streamed answer is stored. It goes only to the phone that started the run, and only for a successful answer. The app hides it while it is on screen.signInWithIdToken, the name saved on first sign-in) and Google (the website's OAuth flow in an authentication session, with PKCE). The Supabase client now usesflowType: 'pkce', as the website's does; email code and demo code verify directly and are unaffected.modules/store-kit, pluspurchases.ts. It buys with the account id asappAccountToken, posts the signed transaction toapp-store-purchase, and finishes it only after a 2xx.Transaction.updatesversion is sent too, so refunds reach the server.com.pistack.debateowl.{basic,pro}.monthly.Build switches
Expo Go's config is byte-identical to before (checked with
npx expo config --json).DEBATELAB_BUILD=devadds the native parts. The newDEBATELAB_APPLE_ACCOUNT=1is for a build signed by DebateOwl's own Apple account. It keeps the push and Sign in with Apple entitlements, whichwithoutAccountEntitlementsstrips from every other development build because the borrowed profile has neither. It also adds Time Sensitive Notifications andactivitycontinuation:debateowl.com. It goes in theeas.jsonprofileenvon build day.What Adam sets up
docs/apple-platform.md has the full checklist. In short:
group.com.pistack.debateowl.com.pistack.debateowl(App Groups, Associated Domains, Push, Sign in with Apple, Time Sensitive Notifications) andcom.pistack.debateowl.ExpoWidgetsTarget(App Groups).com.pistack.debateowl;debatelab://auth/callbackredirect;APP_STORE_*settings;app-store-purchase,app-store-notifications,accountandai.apple-app-site-associationfile, which needs the Team ID.Verification
npm run verifypassed locally at the final headfb2e8035: lint and repo rules, typecheck, 902 unit tests, 30 collaboration tests, 555 edge tests, build, service worker, 47 Playwright smoke tests. This is a local pass, not CI../.superset/db-gate.sh --db-only): 33 files, 655 pgTAP tests, PASS, with the new migration. Stack stopped.not_configured, pending and cancelled, serial delivery, an account switch mid-purchase, refund updates, a new signed version of a delivered transaction, a finish that failed;result_readyscoped to the requesting install;onResultruns after the result is stored and cannot break the stream.StoreKitLogic.swiftandDebateOwlAppIntents.swifttypecheck with Swift 6.4 on the Mac (macOS SDK, plus Catalyst for the UIKit branch).StoreKitModule.swiftandHandoffModule.swifthave not been compiled against ExpoModulesCore. They mirror existing Expo modules.6b8790c4) had seven medium findings, all addressed incd1b24b5. For the offer-code finding, Redeem was removed and the limit documented.71cc82d4,ca0cd554andfb2e8035:Not verified: anything on a device or in Xcode, Live Activities, Siri, Handoff, the StoreKit sandbox, Apple and Google sign-in end to end, web-preview screenshots (
expo exportwas skipped for memory).Risks
app.config.ts,_layout.tsx,apps/mobile/package.json,package-lock.json,apps/mobile/AGENTS.md,docs/development.md, and two lines insparring/index.tsx.🤖 Generated with Claude Code