Soar API Setup

Connect Swift to a conforming Unified Platform host: client build, host environment, sessions, and verification.

This is the Unified Mode counterpart to Supabase Setup: everything needed to run the iOS app against a conforming Web host instead of a Supabase project. If you haven't picked a mode yet, start at Backend Modes.

No host is privileged by framework name — Next.js, Nuxt, and TanStack Start all work; conformance to the frozen contracts is the compatibility boundary.

1. Deploy a conforming host

Deploy one of the Web templates as a Unified Platform Host and complete its host-side setup (database, Better Auth URL and secret, App API):

Sign up on the Web and verify /api/auth/get-session works before touching the iOS side. For the cross-template picture, see the platform Unified Quick Start.

2. Configure the client build

Unified Mode needs two keys in Secrets.xcconfig:

// Secrets.xcconfig
SOAR_BACKEND_MODE = soar_api
SOAR_API_BASE_URL = https:/$()/your-app.example.com

SUPABASE_URL/SUPABASE_PUBLISHABLE_KEY can stay unset — the Supabase client is never built in this mode.

Google Sign-In still needs GOOGLE_IOS_CLIENT_ID/GOOGLE_REVERSED_CLIENT_ID in the client xcconfig in both modes (the native Google SDK runs on device); Apple Sign-In uses the app capability and needs no client key.

Configuration errors stop this build. If SOAR_API_BASE_URL is missing, every request surfaces a diagnosable error — there is no silent Supabase fallback.

3. Configure the host for this app

On the server (the selected host's deployment environment), the app expects the native support described in Native Authentication:

Server variableWhy the iOS app needs it
APP_SCHEME=soar://The app sends Origin: soar:// on every request; Better Auth rejects untrusted origins with 403. Must match the scheme registered in the app's Info.plist.
GOOGLE_IOS_CLIENT_IDAudience allow-list entry for native Google ID-token sign-in.
APPLE_APP_BUNDLE_IDENTIFIERAudience for native Apple ID-token sign-in — no Services ID needed.
REVENUECAT_WEBHOOK_AUTHORIZATION + REVENUECAT_PRODUCT_*Syncs App Store purchases into the shared subscription row.
APNS_*Server-side APNs credentials for the test-push endpoint.
STORAGE_PROVIDER (+ R2_* in production)Presigned-upload object storage backing UploadsRepository.

4. Sessions live in the Keychain

In Unified Mode the app authenticates with Better Auth Bearer tokens (the set-auth-token response header), not cookies:

  • The token plus a minimal user snapshot (userId, email, name, image, emailVerified, expiry) is persisted only in the Keychain (KeychainSessionStore) — never UserDefaults, never logs.
  • On relaunch the stored session is restored and validated against the server; sign-out clears both the Keychain entry and in-memory state.
  • Whenever a response carries a new set-auth-token, the stored token is replaced.
  • Sessions are database-backed and revocable: revoking a session on the web invalidates the iOS token immediately, and the app treats 401 as "sign in again" — it never retry-loops and never falls back to Supabase auth.

5. RevenueCat appUserID

PurchasesController always logs in to RevenueCat with the current user's UserID.rawValue — in Unified Mode that is the Better Auth user ID. This is what lets the server's RevenueCat webhook resolve app_user_id back to the same Better Auth user and update the one shared subscription row that the web reads too.

Keep RevenueCat's Restore Behavior set to Transfer disabled: a receipt already attached to another account should surface a support-facing error, not silently move entitlements between users.

Verify the setup

Run the shared smoke test across both surfaces:

  • Sign up or sign in on iOS, then open the same account on the Web.
  • Update the profile on one surface and read it from the other.
  • Create, edit, reorder, and delete a todo across both surfaces.
  • Upload an avatar and confirm its completion URL is readable.
  • Revoke the iOS session from the Web and verify the app returns to sign-in.
  • After a sandbox purchase, confirm the same entitlement appears on both surfaces.
  • Register the device token and trigger the APNs test push.

For a concrete end-to-end walkthrough of one host-and-client pair, follow Next.js + Swift Unified Setup.

On this page