ShipMobile
HomeLaunch guide

Launch your first app with ShipMobile

A complete, step-by-step guide — from cloning the repo to a live app on the App Store.

This is the guide you follow after you buy ShipMobile. It takes you all the way: run it locally, make it yours, wire the backend, add auth + AI + payments, build it, get through Apple review, and ship.

You do not need to be an iOS expert. ShipMobile has already done the hard, rejection-prone parts (Apple-compliance guards, native modules, privacy manifest). Your job is to build your app on top and connect a handful of accounts.

New to this? Read top to bottom, once. Then come back and do it section by section. Realistic time to a TestFlight build: a focused weekend. Time to App Store approval after submitting: ~24–48 h (Apple's review queue).


Table of contents

  1. What you'll need (accounts, tools, cost)
  2. Get it running locally
  3. Make it yours (rebrand, icon, theme)
  4. Backend + database (Supabase + Vercel)
  5. Auth (Sign in with Apple + Google)
  6. AI providers + spend caps
  7. Payments (Apple IAP + RevenueCat)
  8. Apple Developer + App Store Connect setup
  9. Build with EAS
  10. Preflight + TestFlight
  11. Submit for App Review
  12. Over-the-air (OTA) updates
  13. Android / Google Play (optional)
  14. Troubleshooting

Deep-dive references that this guide links to: apps/mobile/EAS.md (build/OTA), DESIGN.md (theme tokens), docs/oem-quirks.md (Android background-kill).


1What you'll need

Tools on your machine

ToolVersionNotes
Node.js20 LTS or newerRequired by the EAS CLI (20.x) and the toolchain. node -v
pnpm11+Package manager (the repo pins it). npm i -g pnpm
Gitany recentTo clone + version your app
XcodelatestRequired to run the iOS Simulator and for local iOS tooling (macOS only)
WatchmanoptionalSpeeds up Metro file watching: brew install watchman

You can develop the whole app on macOS and build/submit iOS in the cloud via EAS. A Mac is required to run the iOS Simulator; the actual App Store build happens on Expo's servers.

Accounts you'll need

You can build and test the entire app on free tiers. Below, accounts are split into what's required to ship and what's optional — and every service name links to its signup/pricing page.

Required to ship

ServiceWhat it's forCost
GitHubReceive the ShipMobile repo (collaborator invite)Free
Apple Developer ProgramApp Store, in-app purchases, Sign in with Apple$99 / year — the only mandatory cost
Expo (EAS)Cloud builds, submit, OTA updatesFree — 15 iOS builds / mo ¹
SupabasePostgres database + user authFree — 500 MB DB, 50K users ²
VercelHost the backend (Hono API)Free to build → Pro (~$20/mo) once commercial ³
An AI provider (pick one or more)Your app's AI featuresPay per useOpenAI · Anthropic · Replicate · fal.ai
RevenueCatIn-app purchases + subscriptionsFree up to $2,500 / mo revenue, then 1%

Optional — add when you need them

ServiceWhat it's forCost
Upstash RedisPer-user AI spend capsFree — 256 MB, 500K commands / mo
SentryCrash reportingFree — 5K errors / mo
PostHogProduct analyticsFree — 1M events / mo
SuperwallRemote paywall A/B testsFree up to $10K / mo revenue, then 1%
Google CloudGoogle Sign-In (alongside Apple)Free

¹ EAS free tier includes 15 iOS + 15 Android cloud builds per month; a paid plan is required once your app earns more than $1,500 / mo. ² Supabase pauses free projects after 1 week of inactivity — harmless while developing, just open the dashboard to wake it before a demo. ³ Vercel's Hobby plan is non-commercial only (fair-use terms). Building and testing is free; once your app makes money, the backend needs Pro (~$20 / user / mo).

Bottom line: the only cost to get into the App Store is Apple's $99 / year. Everything else is free while you build — once your app is live and earning, budget ~$20/mo for Vercel Pro plus usage-based AI spend. Both scale with success, not up front.


2Get it running locally

You'll have a real, bootable app in about 4 commands.

# 1. Accept the GitHub invite (email from GitHub after purchase), then clone YOUR copy:
git clone https://github.com/<you>/<your-app>.git my-app
cd my-app

# 2. Install dependencies (first run downloads packages — a few minutes)
pnpm install

# 3. Rebrand the template to your app (interactive — only the app name is required)
pnpm shipmobile:init

# 4. Boot the app in the iOS Simulator
pnpm --filter @shipmobile/mobile start
# then press `i` to open iOS (or `a` for Android)

pnpm shipmobile:init is forgiving and re-runnable. From just your app name it:

  • rebrands apps/mobile/app.json (name, slug, scheme, iOS bundle ID + Android package),
  • generates the three .env.local files (root, backend, mobile) with the right keys scaffolded,
  • prints your exact next steps.

Pass --quick to accept all defaults. It never overwrites an existing .env.local.

The app boots before you wire any services. You'll see the working sample app immediately — proof the boilerplate produces a real app. AI, auth, and payments light up as you connect accounts below.

First-launch warnings you can ignore

On the very first start you may see yellow lines like "packages should be updated for best compatibility" or "Sentry: Missing config for organization". These are informational — the app still runs. To clear the Expo version notices: cd apps/mobile && npx expo install --fix.


3Make it yours

Rename (done by init)

pnpm shipmobile:init already set your app name and bundle ID. To change them later, re-run it, or edit apps/mobile/app.json (expo.name, expo.ios.bundleIdentifier, expo.android.package).

App icon + splash

Replace these files in apps/mobile/assets/images/ with your own art (keep the filenames):

FileWhat it isSize
icon.pngApp icon (iOS + fallback)1024×1024, no transparency, no rounded corners (Apple rounds it)
splash-icon.pngSplash screen logo~1024×1024 transparent PNG
android-icon-foreground.png / -background.png / -monochrome.pngAndroid adaptive icon layers1024×1024
favicon.pngWeb favicon48×48+

Icons are wired through app.json (expo.icon, expo-splash-screen plugin, expo.android.adaptiveIcon) — just swap the files.

Theme + colors

ShipMobile ships a token-based design system. See DESIGN.md for the full spec. The app's default theme tokens live in packages/ui; edit colors, typography, and spacing there and every screen updates. Don't hand-tweak individual screens for global changes — change the tokens.

Build your actual app

The screens under apps/mobile/app/ are working templates (AI chat, image gen, dashboard, paywall, settings). This is where AI coding shines: point Claude Code / Cursor at a screen and describe your product — it edits a working, compliant screen instead of scaffolding from a blank file. The auth, paywall, and rejection guards stay intact underneath.


4Backend + database

ShipMobile's backend is a Hono API on Vercel, backed by Supabase (Postgres + Auth).

4a. Create the Supabase project

  1. Go to supabase.com/dashboard/new → create a project (name, a strong DB password — save it, region near your users).

  2. Grab these four values:

    • Project URL — Dashboard → Settings → API (also under Integrations → Data API): https://<project-ref>.supabase.co
    • Publishable key (sb_publishable_…) — Settings → API Keys. Safe to ship in the mobile app.
    • Secret key (sb_secret_…) — Settings → API Keys. Backend only — never in the mobile bundle.
    • JWT secretSettings → JWT Keys.

    Key naming changed in late 2025. Supabase now issues sb_publishable_… / sb_secret_… instead of the legacy anon / service_role JWTs. ShipMobile expects the new names. If your project is older and still shows anon/service_role, either migrate to new keys or paste the anon key where a publishable key is asked for and service_role where a secret key is asked for. Ref: Supabase API keys.

  3. Paste them into your env files:

    • apps/backend/.env.localSUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY, SUPABASE_JWT_SECRET
    • apps/mobile/.env.localEXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY
    • .env.local (root) → SUPABASE_PROJECT_REF (the <project-ref> subdomain) and SUPABASE_DB_PASSWORD

4b. Run the database migrations

ShipMobile ships its schema as migration files in packages/db/supabase/migrations/. Apply them to your new project:

cd packages/db
pnpm db:link              # supabase link --project-ref $SUPABASE_PROJECT_REF (prompts for DB password)
pnpm db:migrate:up:remote # supabase db push — applies all migrations to your REMOTE database
pnpm db:types:remote      # regenerate typed client from your schema

pnpm db:migrate:up:remote runs supabase db push (deploys local migration files to your live DB). pnpm db:migrate:up:local (supabase db push --local) targets a local dev DB instead. Rule of thumb: db push = remote/production. Ref: Supabase migrations.

Every table ShipMobile ships already has Row Level Security policies. If you add your own tables, you must enable RLS and add policies — with RLS on and no policy, the table is deny-all to the app. Ref: Supabase RLS.

4c. Run the backend locally

pnpm --filter @shipmobile/backend dev   # vercel dev on http://localhost:3001

Your mobile app talks to it via EXPO_PUBLIC_BACKEND_URL=http://localhost:3001 (already set by init).

4d. Deploy the backend to Vercel

The repo is a monorepo, so the Vercel project's Root Directory is apps/backend, and you run the CLI from the repo root (Vercel resolves the subdir itself — running vercel from inside apps/backend will fail).

npm i -g vercel        # if you don't have it
vercel login
vercel link            # from the repo root; when prompted, this creates .vercel/
# In the Vercel dashboard for this project: Settings → Build and Deployment →
#   Root Directory = apps/backend

# Add your production env vars (repeat per variable):
vercel env add SUPABASE_URL production
vercel env add SUPABASE_PUBLISHABLE_KEY production
vercel env add SUPABASE_SECRET_KEY production
vercel env add SUPABASE_JWT_SECRET production
vercel env add OPENAI_API_KEY production          # + any AI keys you use
vercel env add CRON_SECRET production             # generate: openssl rand -hex 20
# …plus UPSTASH_*, SENTRY_DSN, ADMIN_USER_IDS as needed

vercel --prod          # deploy to production (from repo root)

Copy the production URL Vercel prints (e.g. https://your-app-backend.vercel.app) and set it as EXPO_PUBLIC_BACKEND_URL in apps/mobile/.env.local (and as an EAS secret — see §9). Refs: Vercel CLI deploy · Vercel monorepos · Vercel env vars.

Note: Vercel now defaults new projects to Fluid Compute and Node 24. ShipMobile's hybrid Edge/Node function affinity (per provider) still applies — don't change a provider's declared runtime casually.


5Auth

ShipMobile uses Supabase Auth. Sign in with Apple is mandatory for App Store approval if you offer any other third-party/social login (Apple guideline 4.8, "Login Services" — it requires an equivalent privacy-preserving option, and Sign in with Apple is the canonical one) — and ShipMobile enforces "Apple first" at build time in packages/auth/src/config.ts.

5a. Sign in with Apple

  1. In the Apple Developer portal:
    • Your App ID (created in §8) must have the Sign in with Apple capability enabled.
    • Create a Services ID (Identifiers → Services IDs) for the web/OAuth client, e.g. com.yourapp.app.web.
    • Create a Key (Keys → +) with Sign in with Apple enabled; download the .p8 and note the Key ID and your Team ID.
  2. In Supabase → Authentication → Providers → Apple, enable it and provide:
    • Client IDs: your app's bundle ID (native) and Services ID (web).
    • For web OAuth: Team ID, Key ID, and the .p8 key contents (Supabase generates the client secret; it rotates ~every 6 months).
    • Register this callback URL in Apple's Services ID config: https://<project-ref>.supabase.co/auth/v1/callback
  3. On iOS, ShipMobile uses native Sign in with Apple (expo-apple-authentication) → passes the identity token to Supabase. The user's name is returned only on first sign-in — ShipMobile captures it then.

Ref: Supabase — Login with Apple.

5b. Google Sign-In (optional)

Create OAuth clients at console.cloud.google.com/apis/credentials (types: iOS, Android, Web) and set EXPO_PUBLIC_GOOGLE_CLIENT_ID_IOS / _ANDROID / _WEB. If any are set, the Google button appears — and because the 4.8 parity rule is enforced, Apple sign-in automatically renders first.


6AI providers + spend caps

  1. Add the keys for the providers your app uses to apps/backend/.env.local (and to Vercel via vercel env add):

    Keys live only on the backend — the mobile app calls your Vercel API, which calls the providers. Nothing sensitive ships in the bundle.

  2. Per-user spend caps (recommended): set UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN (free at console.upstash.com, Global region). ShipMobile uses atomic Redis INCRBY pre-debit + refund so 100 parallel requests still respect the cap — no user can rack up a surprise bill. Leave blank to disable enforcement (all requests allowed).

  3. AI consent (guideline 5.1.2(i)): ShipMobile already mounts a consent sheet before the first AI call and declares AI data use in the privacy manifest. Don't remove it — as of Nov 2025, Apple requires explicit consent before sending personal data to third-party AI. (Verified by pnpm shipmobile:preflight.)


7Payments

ShipMobile monetizes with Apple In-App Purchase, managed through RevenueCat. This is what satisfies guideline 3.1.1 (digital goods must use Apple IAP) — and ShipMobile ships no Stripe SDK in the bundle (enforced by preflight). Do them in this order:

7a. Sign the Paid Apps agreement (do this FIRST)

In App Store Connect → Business (formerly Agreements, Tax, and Banking), the Account Holder must:

  1. Sign the Paid Applications Agreement,
  2. Enter banking information, and
  3. Complete tax forms.

IAP does not function — not even in sandbox — until this shows "Active." This is the #1 reason first purchases silently fail. Consider the Small Business Program (15% commission if under $1M/yr). Refs: agreements · banking · tax.

7b. Create your IAP products in App Store Connect

App Store Connect → your app → Monetization → In-App Purchases (or → Subscriptions for a recurring "premium" tier) → (+). For each:

  • Pick a type: Consumable, Non-Consumable, Auto-Renewable Subscription (the usual premium tier), or Non-Renewing.
  • Set a Reference Name and a Product ID (e.g. com.yourapp.premium.monthly) — write the Product ID down exactly; RevenueCat must match it.
  • Fill metadata until the product reaches "Ready to Submit."

Your first IAP must be attached to and submitted with your first app version (§11) — it won't show "Approved" until the app is. Ref: IAP types.

7c. Set up RevenueCat

  1. app.revenuecat.com → create a ProjectAdd app → platform App Store → enter your Bundle ID (exact match). This generates your public SDK key (appl_…).
  2. Give RevenueCat your Apple In-App Purchase Key (required for StoreKit 2 — without it, transactions don't record):
    • App Store Connect → Users and Access → Integrations → In-App PurchaseGeneratedownload the .p8 (one download only) → copy the Issuer ID.
    • RevenueCat → your app → In-app purchase key configuration → upload the .p8 + Issuer ID → Save.
    • Ref: RevenueCat service credentials.
  3. Server notifications (recommended): RevenueCat → Apple Server-to-Server notification settings → copy the URL → App Store Connect → App Information → App Store Server Notifications → paste into both Production + Sandbox → select Version 2.
  4. Model your entitlements (docs):
    • Products → import/add each App Store product (Product IDs must exactly match §7b).
    • Entitlements → create one (e.g. premium) → attach your products to it.
    • Offerings → create one → add packages ($rc_monthly, $rc_annual). Offerings let you change what the paywall shows without an app update.

7d. Wire the keys into the app

Add your public RevenueCat keys (the appl_… / goog_… from RevenueCat → API keys — never a secret sk_ key):

# apps/mobile/.env.local (and as EAS secrets for builds — see §9)
EXPO_PUBLIC_REVENUECAT_IOS_KEY=appl_xxxxx
EXPO_PUBLIC_REVENUECAT_ANDROID_KEY=goog_xxxxx

ShipMobile already integrates react-native-purchases and reads entitlements in the paywall. RevenueCat needs a native build — it will not run in Expo Go; test on a development/preview EAS build. Optionally add EXPO_PUBLIC_SUPERWALL_KEY for remote paywall A/B config on top of RevenueCat.

Where to draw the line on external links: as of mid-2025 the US storefront allows external-purchase links without an entitlement, but it's still banned in most regions. Keep it simple and global: stay RevenueCat/StoreKit-only — that's what the 3.1.1 preflight expects.


8Apple Developer + App Store Connect

8a. Enroll in the Apple Developer Program

developer.apple.com/programs/enroll$99/yr, same price for Individual or Organization.

  • Individual / sole proprietor: Apple Account with 2FA + government photo ID; you enroll under your legal name (which becomes the public "seller" name). Fastest — often approved same/next day. Can enroll in the Apple Developer app or on the web.
  • Organization: requires a D-U-N-S number (free) and a legal entity; web enrollment only; takes several days.

Apple publishes no approval SLA — enroll early so it's not your blocker on launch day. Ref: enrollment.

8b. App ID + capabilities — mostly automatic via EAS

You normally don't touch the Developer portal here. On your first eas build -p ios, EAS auto-detects your bundle ID, and syncs the capabilities your app declares — including Sign in with Apple (com.apple.developer.applesignin) and Push Notifications (aps-environment) — registering the App ID and generating the push key for you. Ref: EAS iOS capabilities.

Only do it by hand if you disable auto-sync (EXPO_NO_CAPABILITY_SYNC=1): Developer portal → Certificates, Identifiers & Profiles → Identifiers → (+) → App IDs → Explicit (wildcard IDs can't do Push), then Configure → enable Sign in with Apple + Push.

8c. Create the App Store Connect app record

EAS creates credentials, but not the app listing — do this once:

App Store Connect → Apps → (+) → New App:

  • Platform: iOS
  • Name: your public app name (max 30 chars, must be unique across the App Store)
  • Primary Language, Bundle ID (pick the one EAS registered), SKU (any private internal id, e.g. yourapp-ios-001), User Access.

Ref: create an app record.

8d. Point EAS at the app record

Open App Information → General → Apple ID in App Store Connect (a 10-digit number) and put it — plus your Team ID — into apps/mobile/eas.json:

"submit": {
  "production": {
    "ios": {
      "appleTeamId": "YOUR_TEAM_ID",
      "ascAppId": "1234567890"
    }
  }
}

(The repo ships ShipMobile's own values here — replace them with yours.) For hands-off/CI submits, also create an App Store Connect API key and reference it (ascApiKeyPath/ascApiKeyId/ascApiKeyIssuerId) instead of an app-specific password — manage it with eas credentials --platform ios.


9Build with EAS

ShipMobile builds in the cloud with EAS Build. Profiles are already defined in apps/mobile/eas.json (development, preview, production).

9a. One-time EAS setup

npm i -g eas-cli        # v20.5.0+ (requires Node 20+)
eas login

cd apps/mobile
eas build:configure     # links/creates the EAS project; writes expo.extra.eas.projectId — COMMIT this

Set your build-time env as EAS secrets so cloud builds can read them (full list in apps/mobile/EAS.md):

eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_URL --value "https://<ref>.supabase.co"
eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY --value "sb_publishable_..."
eas secret:create --scope project --name EXPO_PUBLIC_BACKEND_URL --value "https://your-backend.vercel.app"
eas secret:create --scope project --name EXPO_PUBLIC_REVENUECAT_IOS_KEY --value "..."
eas secret:create --scope project --name SENTRY_AUTH_TOKEN --value "sntrys_..." --type string
# …plus PostHog/Superwall/Google as needed

9b. Credentials (signing) — automatic

On your first eas build -p ios, EAS asks you to sign in with your Apple Developer account, then generates and stores your distribution certificate, provisioning profile, and push key for you. You don't hand-manage certificates. Inspect them any time with eas credentials. Ref: Managed credentials.

9c. Build for the App Store

cd apps/mobile
eas build --platform ios --profile production

This produces a signed device .ipa on Expo's servers (~15–25 min). Track it at https://expo.dev/accounts/<you>/projects/<app>/builds. Ref: EAS Build setup.


10Preflight + TestFlight

10a. Run the rejection-guard preflight

Before every submission, run:

pnpm shipmobile:preflight

It mechanically checks the things Apple rejects for. All 12 must pass:

CheckGuideline
No Stripe SDK in the mobile bundle3.1.1
Sign in with Apple present if any other social provider is enabled4.8
AI consent gate mounted before any AI call5.1.2(i)
Delete-account control reachable from Settings5.1.1(v)
Apple privacy manifest declared in app.json
ITSAppUsesNonExemptEncryption set in Info.plistexport compliance
Usage-description strings for declared permissions
runtimeVersion.policy: "fingerprint"OTA safety
Bundle ID is not the template default
EAS project initialized
No restricted Android permissions without opt-inPlay policy
App icon present

Fix anything that fails before you submit. (bundle-id-customized fails until you've run shipmobile:init; eas-project-initialized fails until §9a.)

The guideline numbers above are Apple's current ones. ShipMobile's printed check labels still read the older apple-5.1.1-… / apple-5.1.2-… (Apple renumbered AI-consent → 5.1.2(i) and social-login parity → 4.8 in Nov 2025). The checks themselves are current; only the label strings lag — see §11.

10b. Submit to TestFlight

cd apps/mobile
eas submit --platform ios --latest    # or: --profile production

EAS uploads the build to App Store Connect. After ~10 min of Apple processing it appears under TestFlight → iOS Builds. Add internal testers and send your first invite. Ref: EAS Submit (iOS).

For CI/non-interactive submits, use an App Store Connect API key (ascApiKeyPath/ascApiKeyId/ascApiKeyIssuerId in eas.json) rather than an app-specific password. Manage it via eas credentials --platform ios.


11Submit for App Review

Your build is in TestFlight and you've passed preflight — now fill the store listing and submit. In App Store Connect → your app → the version (Prepare for Submission):

11a. Screenshots

Apple simplified the required sizes (2025). For a universal app you need just two sets:

  • iPhone 6.9"1290 × 2796 (required)
  • iPad 13"2064 × 2752 (required, because ShipMobile's app.json has supportsTablet: true)

1–10 per set, PNG or JPEG. Smaller device sizes are now optional (Apple auto-scales). Ref: screenshot specs.

11b. Text metadata + required URLs

FieldLimit
Name30 chars
Subtitle30 chars
Keywords100 bytes, comma-separated, no spaces
Description4,000 chars
Promotional Text170 chars
What's New4,000 chars

Support URL and Privacy Policy URL are required. Generate your legal pages with pnpm gen:legal (produces privacy policy + terms from your actual SDK usage), then host them somewhere public and paste the URLs. Ref: version info.

11c. App Privacy ("nutrition labels")

App Store Connect → App Privacy → Get Started. Declare, per data type: the purpose, whether it's linked to identity, and whether it's used for tracking — including data collected by third-party SDKs (Sentry, PostHog, RevenueCat). ShipMobile's app.json privacy manifest covers the on-device technical declaration, but you still complete this questionnaire. Ref: manage app privacy.

11d. Age rating (mandatory now)

Complete the age-rating questionnaire (bands: 4+, 9+, 13+, 16+, 18+). The 2025/2026 questionnaire asks about parental controls, medical/wellness, violence, and how often your app shows AI-chatbot-generated content — answer honestly for an AI app. This is required before any submission. Ref: Apple news.

11e. Export compliance

ShipMobile sets ITSAppUsesNonExemptEncryption: false in app.json (your app uses only standard HTTPS/TLS), so you can answer "No" to non-exempt encryption. Verify the flag actually landed in the built Info.plist — if it's missing you'll hit "Missing Compliance" (ITMS-90592). Ref: Apple key.

11f. App Review Information + demo account

Provide a contact name/email/phone. If your app requires login, provide a non-expiring demo account (username + password) so the reviewer can get in. Use the Notes field (4,000 chars) to explain anything non-obvious — e.g. how to reach a gated feature, or how a free-tier exit works.

11g. Attach the build + IAP, then Submit

Select your TestFlight build for the version, attach any first-version IAP (§7b), then Add for Review → Submit. Review typically takes ~24–48 hours (no guaranteed SLA).

What preflight already cleared for you

Because pnpm shipmobile:preflight passed, the mechanical rejection reasons are handled. Current Apple guideline numbers (Apple renumbered some in Nov 2025):

RequirementCurrent guidelineShipMobile handles it via
Disclose + get consent before sending data to third-party AI5.1.2(i)<AiProviderConsentSheet /> consent gate
In-app account deletion (not an email link)5.1.1(v)<DeleteAccountButton /> + request_account_deletion RPC
Login-services parity — offer Sign in with Apple alongside other social logins4.8Apple sign-in enforced (and rendered first per Apple's HIG)
Use Apple IAP for digital goods; no external payment SDK3.1.1RevenueCat over StoreKit; no Stripe SDK in bundle

Note: ShipMobile's internal preflight labels still read apple-5.1.1-ai-consent and apple-5.1.2-sign-in-with-apple — those predate Apple's Nov 2025 renumbering (AI → 5.1.2(i), social-login parity → 4.8). The checks are current; only the label strings lag.


12OTA updates

Once your app is live, ship JS-only fixes instantly (no App Store review) with EAS Update:

cd apps/mobile
eas update --channel production --message "fix copy typo"

The catch, by design: app.json uses runtimeVersion.policy: "fingerprint". Any change that touches native code (a new native dependency, a config-plugin change, an SDK bump) changes the fingerprint → the OTA no longer matches installed binaries and won't be delivered to them. That's the safety net that stops you from OTA-ing a JS bundle that needs native code the installed app doesn't have.

When the OTA workflow reports a fingerprint mismatch: don't bypass it. Ship a new binary via eas build --profile production, get it approved, then OTA updates resume. Full details in apps/mobile/EAS.md. Refs: EAS Update · Runtime versions.


13Android / Google Play

The same code ships to Android. eas.json's production profile builds an AAB; submit with eas submit --platform android. You'll need a Google Play Developer account (one-time $25) and a service-account key. Restricted permissions (SMS, Call Log, etc.) are not in the default set — opt-in modules add them with the required declaration. See docs/oem-quirks.md for Android background-kill behavior across OEMs.


14Troubleshooting

SymptomFix
Yellow "packages should be updated" on startcd apps/mobile && npx expo install --fix
Production build boots to a blank/crash, env values emptyEnv vars aren't in the build — set them via eas secret:create (§9a), not just .env.local
vercel deploy errors with …/apps/backend/apps/backend does not existRun vercel from the repo root, not from apps/backend (Root Directory is set in the dashboard)
Supabase calls return 401 / no rowsYou're using the wrong key (publishable vs secret) or a table has RLS on with no policy
EAS build fails at credentialsEnsure your Apple Developer Program membership is active; re-run eas credentials --platform ios
OTA update "won't apply" / fingerprint mismatchA native change happened — ship a new store build; don't force the OTA
pnpm shipmobile:preflight fails bundle-id-customizedYou haven't run pnpm shipmobile:init yet (still on the template bundle ID)
Sentry warning on first runExpected until you set EXPO_PUBLIC_SENTRY_DSN + SENTRY_AUTH_TOKEN

Official documentation (bookmark these)


Stuck? The Full and Founder tiers include community + direct support. This guide is versioned in your repo at docs/LAUNCH.md — PRs welcome.