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.
Links & legal
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.
