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
- What you'll need (accounts, tools, cost)
- Get it running locally
- Make it yours (rebrand, icon, theme)
- Backend + database (Supabase + Vercel)
- Auth (Sign in with Apple + Google)
- AI providers + spend caps
- Payments (Apple IAP + RevenueCat)
- Apple Developer + App Store Connect setup
- Build with EAS
- Preflight + TestFlight
- Submit for App Review
- Over-the-air (OTA) updates
- Android / Google Play (optional)
- Troubleshooting
Deep-dive references that this guide links to: (build/OTA), apps/mobile/EAS.md (theme tokens), DESIGN.md (Android background-kill).docs/oem-quirks.md
1What you'll need
Tools on your machine
| Tool | Version | Notes |
|---|---|---|
| Node.js | 20 LTS or newer | Required by the EAS CLI (20.x) and the toolchain. node -v |
| pnpm | 11+ | Package manager (the repo pins it). npm i -g pnpm |
| Git | any recent | To clone + version your app |
| Xcode | latest | Required to run the iOS Simulator and for local iOS tooling (macOS only) |
| Watchman | optional | Speeds 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
| Service | What it's for | Cost |
|---|---|---|
| GitHub | Receive the ShipMobile repo (collaborator invite) | Free |
| Apple Developer Program | App Store, in-app purchases, Sign in with Apple | $99 / year — the only mandatory cost |
| Expo (EAS) | Cloud builds, submit, OTA updates | Free — 15 iOS builds / mo ¹ |
| Supabase | Postgres database + user auth | Free — 500 MB DB, 50K users ² |
| Vercel | Host the backend (Hono API) | Free to build → Pro (~$20/mo) once commercial ³ |
| An AI provider (pick one or more) | Your app's AI features | Pay per use — OpenAI · Anthropic · Replicate · fal.ai |
| RevenueCat | In-app purchases + subscriptions | Free up to $2,500 / mo revenue, then 1% |
Optional — add when you need them
| Service | What it's for | Cost |
|---|---|---|
| Upstash Redis | Per-user AI spend caps | Free — 256 MB, 500K commands / mo |
| Sentry | Crash reporting | Free — 5K errors / mo |
| PostHog | Product analytics | Free — 1M events / mo |
| Superwall | Remote paywall A/B tests | Free up to $10K / mo revenue, then 1% |
| Google Cloud | Google 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.localfiles (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):
| File | What it is | Size |
|---|---|---|
icon.png | App icon (iOS + fallback) | 1024×1024, no transparency, no rounded corners (Apple rounds it) |
splash-icon.png | Splash screen logo | ~1024×1024 transparent PNG |
android-icon-foreground.png / -background.png / -monochrome.png | Android adaptive icon layers | 1024×1024 |
favicon.png | Web favicon | 48×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 for the full spec. The app's default theme tokens live in DESIGN.mdpackages/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
-
Go to supabase.com/dashboard/new → create a project (name, a strong DB password — save it, region near your users).
-
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 secret — Settings → JWT Keys.
Key naming changed in late 2025. Supabase now issues
sb_publishable_…/sb_secret_…instead of the legacyanon/service_roleJWTs. ShipMobile expects the new names. If your project is older and still showsanon/service_role, either migrate to new keys or paste theanonkey where a publishable key is asked for andservice_rolewhere a secret key is asked for. Ref: Supabase API keys. - Project URL — Dashboard → Settings → API (also under Integrations → Data API):
-
Paste them into your env files:
apps/backend/.env.local→SUPABASE_URL,SUPABASE_PUBLISHABLE_KEY,SUPABASE_SECRET_KEY,SUPABASE_JWT_SECRETapps/mobile/.env.local→EXPO_PUBLIC_SUPABASE_URL,EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY.env.local(root) →SUPABASE_PROJECT_REF(the<project-ref>subdomain) andSUPABASE_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:remoterunssupabase 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
runtimecasually.
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
- 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
.p8and note the Key ID and your Team ID.
- 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
.p8key 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
- 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
-
Add the keys for the providers your app uses to
apps/backend/.env.local(and to Vercel viavercel env add):OPENAI_API_KEY— platform.openai.com/api-keysANTHROPIC_API_KEY— console.anthropic.com/settings/keysREPLICATE_API_TOKEN— replicate.com/account/api-tokensFAL_API_KEY— fal.ai/dashboard/keys
Keys live only on the backend — the mobile app calls your Vercel API, which calls the providers. Nothing sensitive ships in the bundle.
-
Per-user spend caps (recommended): set
UPSTASH_REDIS_REST_URL+UPSTASH_REDIS_REST_TOKEN(free at console.upstash.com, Global region). ShipMobile uses atomic RedisINCRBYpre-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). -
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:
- Sign the Paid Applications Agreement,
- Enter banking information, and
- 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
- app.revenuecat.com → create a Project → Add app → platform App Store → enter your Bundle ID (exact match). This generates your public SDK key (
appl_…). - 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 Purchase → Generate → download 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.
- App Store Connect → Users and Access → Integrations → In-App Purchase → Generate → download the
- 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.
- 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:
| Check | Guideline |
|---|---|
| No Stripe SDK in the mobile bundle | 3.1.1 |
| Sign in with Apple present if any other social provider is enabled | 4.8 |
| AI consent gate mounted before any AI call | 5.1.2(i) |
| Delete-account control reachable from Settings | 5.1.1(v) |
Apple privacy manifest declared in app.json | — |
ITSAppUsesNonExemptEncryption set in Info.plist | export 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-in | Play 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/ascApiKeyIssuerIdineas.json) rather than an app-specific password. Manage it viaeas 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.jsonhassupportsTablet: 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
| Field | Limit |
|---|---|
| Name | 30 chars |
| Subtitle | 30 chars |
| Keywords | 100 bytes, comma-separated, no spaces |
| Description | 4,000 chars |
| Promotional Text | 170 chars |
| What's New | 4,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):
| Requirement | Current guideline | ShipMobile handles it via |
|---|---|---|
| Disclose + get consent before sending data to third-party AI | 5.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 logins | 4.8 | Apple sign-in enforced (and rendered first per Apple's HIG) |
| Use Apple IAP for digital goods; no external payment SDK | 3.1.1 | RevenueCat over StoreKit; no Stripe SDK in bundle |
Note: ShipMobile's internal preflight labels still read
apple-5.1.1-ai-consentandapple-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 . Refs: EAS Update · Runtime versions.apps/mobile/EAS.md
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 for Android background-kill behavior across OEMs.docs/oem-quirks.md
14Troubleshooting
| Symptom | Fix |
|---|---|
Yellow "packages should be updated" on start | cd apps/mobile && npx expo install --fix |
| Production build boots to a blank/crash, env values empty | Env 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 exist | Run vercel from the repo root, not from apps/backend (Root Directory is set in the dashboard) |
| Supabase calls return 401 / no rows | You're using the wrong key (publishable vs secret) or a table has RLS on with no policy |
| EAS build fails at credentials | Ensure your Apple Developer Program membership is active; re-run eas credentials --platform ios |
| OTA update "won't apply" / fingerprint mismatch | A native change happened — ship a new store build; don't force the OTA |
pnpm shipmobile:preflight fails bundle-id-customized | You haven't run pnpm shipmobile:init yet (still on the template bundle ID) |
| Sentry warning on first run | Expected until you set EXPO_PUBLIC_SENTRY_DSN + SENTRY_AUTH_TOKEN |
Official documentation (bookmark these)
- Expo / EAS: Build · eas.json · Submit iOS · Credentials · EAS Update · Workflows
- Supabase: API keys · Migrations · Sign in with Apple · RLS
- Vercel: CLI deploy · Monorepos · Env vars
- Apple: App Store Connect Help · App Review Guidelines
- RevenueCat: Docs
Stuck? The Full and Founder tiers include community + direct support. This guide is versioned in your repo at docs/LAUNCH.md — PRs welcome.