Configuration

Secrets.xcconfig keys, the xcconfig chain, and graceful degradation.

This is the iOS equivalent of an "environment variables" page. An iOS app has no .env file or server environment — instead, configuration flows through Xcode's xcconfig build settings into Info.plist, and the app reads them back through a typed accessor. This page explains that chain and documents every key.

The xcconfig chain

Configuration moves through five stages:

AppConfig.xcconfig (tracked)

Committed to git with safe placeholder values (e.g. your-supabase-publishable-key). It ends with #include? "Secrets.xcconfig", so your local file overrides these.

Secrets.xcconfig (git-ignored)

Your real values, created with cp Secrets.example.xcconfig Secrets.xcconfig. Never committed. Any key you define here overrides the placeholder above.

Info.plist $() substitution

The build settings are surfaced into Info.plist entries using $(KEY) substitution, so the values ship inside the app bundle as Info dictionary keys.

AppSecrets accessor

Core/Config/AppSecrets.swift reads the Info dictionary and exposes typed, optional properties (supabaseURL: URL?, revenueCatIOSKey: String?, …).

Per-controller isConfigured no-op

Each controller checks its key. When it's absent, the controller sets isConfigured = false and no-ops instead of crashing.

Placeholders count as unset. AppSecrets treats a value as missing if it's empty, still contains the $( substitution marker, or begins with your-. That means you can leave any placeholder in AppConfig.xcconfig untouched and the owning feature simply stays inactive — the app still builds and boots. This is the graceful degradation contract.

Key reference

Every key is optional. The tables below are grouped by service; "When absent" describes the behavior with the key unset.

Supabase

Prop

Type

Required for every Supabase-backed flow (auth, todos, uploads). See Supabase Setup.

RevenueCat

Prop

Type

See RevenueCat Setup and Subscriptions.

PostHog (analytics)

Prop

Type

Analytics is also consent-gated — it only starts after the user accepts the consent gate. See Observability.

Sentry (crash reporting)

Prop

Type

Update gate

Prop

Type

Bump MIN_APP_VERSION / LATEST_APP_VERSION in Secrets.xcconfig to exercise the gate locally. See App Gates.

Prop

Type

See Deep Links for the Universal Links setup.

Google Sign-In

Prop

Type

Both keys ship in Secrets.example.xcconfig. See Authentication for the full Google flow. Apple Sign-In needs no key — it uses the Sign in with Apple capability instead.

Server-side secrets never live in the app

Keys in Secrets.xcconfig are client values that ship inside the app bundle — only put publishable/public values here. Privileged secrets (the APNs signing key, the RevenueCat webhook auth token, the Supabase service-role key) live in Supabase Edge Function secrets and are never bundled with the app. See Supabase Setup.

xcconfig gotcha: // is a comment

In xcconfig files, // starts a comment — which collides with the // in a URL. The template works around this with the $() empty-substitution trick, splitting the // so it isn't parsed as a comment:

SUPABASE_URL = https:/$()/your-project-ref.supabase.co

$() expands to nothing, so the value reads back as https://your-project-ref.supabase.co. Keep this pattern whenever you set a URL-valued key.

On this page