Skip to content

[ios] Release configuration: icon and splash, real legal pages, EAS build profiles - #20

Merged
Harrolee merged 2 commits into
mainfrom
ios/release-configuration
Aug 11, 2026
Merged

Harrolee merged 2 commits into
mainfrom
ios/release-configuration

Conversation

@Harrolee

Copy link
Copy Markdown
Owner

Refs #6. Does not close it — the acceptance criteria need npx expo run:ios on a physical device, which I cannot run. See "What remains" at the bottom.

Rebased onto 9324694, so the real extra.eas.projectId from the owner's eas init is preserved untouched; my app.json diff is only the icon/splash/adaptive-icon lines.

Icon and splash

Generated, not hand-drawn: mobile/assets/generate-assets.py renders all four
assets from one description, so the mark can be adjusted and re-rendered rather
than edited as a binary.

File Size Use
mobile/assets/icon.png 1024×1024, no alpha app icon (the App Store rejects icons with an alpha channel)
mobile/assets/adaptive-icon.png 1024×1024 RGBA Android adaptive foreground, inset for the 25% mask
mobile/assets/splash.png 2048×2048 splash, resizeMode: contain over #0B0B0F so it fits any aspect
mobile/assets/notification-icon.png 192×192 white silhouette Android notification icon, tinted #7C5CFF

The mark is a speech bubble with three rising dots. The product is coaches who
message you, and the roster is no longer fitness-only — drummers, songwriters,
yoga instructors — so there is deliberately nothing gym-shaped in it. The bubble
carries an amber → violet → cyan sweep, the same spread tintForCategory() uses
for coach categories, on the app's #0B0B0F ground.

This needs the owner's eye before submission — it is the one thing here I
cannot verify by running a check.

Real legal URLs

settings.tsx pointed at https://cabofit.app/terms and /privacy, which do
not exist. The webapp now serves both as standalone unauthenticated routes
(/terms, /privacy, via LegalPage), and mobile/src/lib/legal.ts points the
app at them, overridable with EXPO_PUBLIC_LEGAL_BASE_URL when a custom domain
appears.

The two documents were rewritten from the SMS-era text. They are shared with the
signup modals, so there is one version of each. Both cover:

  • Auto-renewing subscriptions (Terms §4), with the disclosure App Review
    expects: what the subscription unlocks and for how long, that the price is
    shown on the coach's page before purchase, that payment is charged to the
    Apple ID at confirmation, that it renews within 24 hours of the period end
    unless auto-renewal is turned off at least 24 hours before, how to manage and
    cancel it in Apple ID Account Settings, that unused free-trial time is
    forfeited on purchase, and that deleting the app does not cancel anything.
  • Likeness and photo handling (Terms §6, Privacy §4), written to match what
    [visualiser] Reference photo upload and likeness consent (V1) #11 describes: the reference photo is used to generate images of you, is
    stored and linked to the profile, is used only while the separate likeness
    consent is on (otherwise generations stay on the faceless scene-only model),
    and withdrawing consent deletes the stored photo and immediately reverts later
    generations. Explicitly not used for training, facial recognition, identity
    verification, or biometric matching.
  • Also updated: what is actually collected now (Apple sign-in, conversations,
    goals, push tokens, IAP receipts) and the real processor list (Supabase, GCP,
    OpenAI, Replicate, Expo/Apple push, Stripe, Twilio for legacy SMS).

Two things to look at before submitting:

  1. The pages use halzinnia@gmail.com as the contact. A policy needs a working
    contact address and there is none in the repo — swap it for a support alias if
    you would rather not publish that one.
  2. mobile/src/lib/legal.ts defaults to the deployed Cloud Run hostname
    (workout-motivation-webapp-3sleoawqya-uc.a.run.app). The webapp has to be
    redeployed for /terms and /privacy to exist
    — right now that host serves
    the SPA fallback for those paths.

EAS build profiles

eas init did not write an eas.json, so this adds one: development (dev
client, internal distribution, device rather than simulator — the profile a
push-capable device build needs), preview, and production with an
auto-incrementing build number.

resolveProjectId() in notifications.ts reads
Constants.expoConfig.extra.eas.projectId first and falls back to
Constants.easConfig.projectId; with the id now in app.json, the first path is
the one that resolves for expo run:ios, the second covers a build made by EAS
itself. It now warns when it resolves to nothing rather than quietly requesting a
token with no project.

Bundle id check (read-only)

Queried the App Store Connect API with the owner's key. com.cabo.coaches is
not registered
— the account holds 15 bundle ids and 6 apps, none under
com.cabo.*, and no app named Coaches. There is a wildcard * App ID, which
cannot be used for push or for App Store distribution. Registering it (with the
Push Notifications capability) belongs to #7; I created and modified nothing.

Checks

  • cd mobile && npx tsc --noEmit — clean.
  • cd _infra && terraform validate — Success (after terraform init -backend=false).
  • cd webapp && npx vite build — builds, including the new routes.
  • Probes against a fresh local stack (Supabase from this branch's migrations +
    example_roster.sql, the function gateway, the mock model):
    viz-realtime-probe 17/17, flow-probe 31/34, rls-probe 37/41.
    The 7 failures are in code this branch does not touch — git diff origin/main
    covers no file under functions/, supabase/, or mobile/e2e/, and the
    probes import nothing from the app source — so they are pre-existing here, not
    regressions. For whoever chases them:
    • rls-probe ×4: anonymous coach-detail join fails with permission denied for table creator_profiles. …120100_creators_and_coach_subscriptions.sql grants
      SELECT on that table to authenticated but never to anon, so an
      unauthenticated browse of a coach cannot embed the creator.
    • flow-probe ×3: the nudge-dispatcher section. Its user_profiles setup
      (nudge_hour, quiet_hours_start/end) does not land, so due_coach_nudges
      returns nothing and the sweep considers 0.
    • Note several agents are running local stacks on this machine at once and
      ports 8790/8791 were already taken; I had to run my gateway on another port,
      which is why a first pass looked far worse than it was.

What remains

  • Device verification. The acceptance criteria — expo run:ios on a physical
    device reaching the roster, ensurePush() returning 'granted', and a real
    ExponentPushToken[...] row in push_devices — is still outstanding. The
    original blocker (no project id) is gone; what is left is that I have no
    device. npx expo run:ios --device, or
    eas build --profile development --platform ios, is the run that closes it.
  • Register com.cabo.coaches on the Apple Developer account ([iap] App Store Connect subscriptions, product mapping, and notification webhook (I2) #7). A device
    build with push will not provision until it exists.
  • Redeploy the webapp so /terms and /privacy resolve before review.
  • Look over the icon and splash.

Release configuration for the App Store build (refs #6).

Icon and splash: generated from assets/generate-assets.py rather than
hand-drawn, so the mark can be adjusted and re-rendered. The mark is a
speech bubble with three rising dots — the product is coaches who message
you, and the roster now spans drummers, songwriters and yoga instructors,
so nothing about it is gym-specific. icon.png is written without an alpha
channel because the App Store rejects icons that have one.

Legal: mobile/app/(tabs)/settings.tsx pointed at https://cabofit.app/terms
and /privacy, which do not exist. The webapp now serves /terms and /privacy
as standalone unauthenticated routes, and the two documents were rewritten
from the SMS-era text to cover what the app actually does: auto-renewing
subscriptions billed per coach through the Apple ID (the disclosure App
Review requires), and the reference photo the visualiser can use — stored,
used only under an explicit likeness consent, deleted when that consent is
withdrawn.

EAS: eas.json adds development/preview/production build profiles, which
`eas init` does not create. resolveProjectId() is documented and hardened
to warn instead of silently minting no token.
@Harrolee
Harrolee merged commit ce948ee into main Aug 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant