> ## Documentation Index
> Fetch the complete documentation index at: https://docs.swiftaiboilerplate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration Reference

> Config keys, secrets files, and the generated Configuration.swift for SwiftAI Boilerplate Pro v2.2.0

The full config schema is machine-readable in `template.manifest.json` (repo root) under `configSchema`. The keys below are the authoritative source consumed by the app and the `scripts/appstore-43-audit.sh` gate.

## Config Keys

| Key                  | Type   | Required | Secret | Purpose                                               |
| -------------------- | ------ | -------- | ------ | ----------------------------------------------------- |
| `SUPABASE_URL`       | url    | Yes      | No     | Supabase project URL (auth + backend)                 |
| `SUPABASE_ANON_KEY`  | string | Yes      | Yes    | Supabase anonymous key                                |
| `REVENUECAT_API_KEY` | string | No       | Yes    | RevenueCat public SDK key                             |
| `RC_ENTITLEMENT_ID`  | string | No       | No     | RevenueCat entitlement identifier                     |
| `PROXY_BASE_URL`     | url    | No       | No     | AI proxy base URL; `EchoLLMClient` is used when empty |
| `PROXY_PATH`         | string | No       | No     | AI proxy path                                         |
| `ONESIGNAL_APP_ID`   | string | No       | Yes    | OneSignal push app ID                                 |

## Configuration Files

### Config/Secrets.xcconfig

**Location:** `Config/Secrets.xcconfig` (gitignored)

<Warning>
  This file contains API keys and secrets. Never commit it to git.
</Warning>

**Template:** `Config/Secrets.example.xcconfig` (copy this to get started)

```bash theme={null}
# Supabase Configuration
SUPABASE_URL = https://YOUR-PROJECT-REF.supabase.co
SUPABASE_ANON_KEY = YOUR_PUBLIC_ANON_KEY

# Google Sign In (Optional)
# GOOGLE_CLIENT_ID = YOUR_IOS_CLIENT_ID.apps.googleusercontent.com

# RevenueCat Configuration
REVENUECAT_API_KEY = YOUR_RC_KEY
RC_ENTITLEMENT_ID = pro

# AI Proxy Configuration (Supabase Edge Function)
PROXY_BASE_URL = https://YOUR-PROJECT-REF.supabase.co/functions/v1
PROXY_PATH = /ai

# OneSignal Push Notifications (Optional)
ONESIGNAL_APP_ID = YOUR_ONESIGNAL_APP_ID
```

**Generate Configuration.swift:**

```bash theme={null}
# After filling in Config/Secrets.xcconfig, run:
bash scripts/update-config.sh
```

This regenerates `SwiftAIBoilerplatePro/Generated/Configuration.swift` (the `AppConfiguration` enum) with your values. Keys whose value still contains a `YOUR` placeholder are written as a comment and treated as "not configured" at runtime.

### Config/App.xcconfig

**Location:** `Config/App.xcconfig`

General app-level build settings, including the single source of truth for app identity. Every target's bundle ID and the app-group entitlements derive from `APP_BUNDLE_ID`.

```bash theme={null}
PRODUCT_NAME = $(TARGET_NAME)

# Single source of truth for app identity. Test, UI-test, and extension
# bundle IDs plus the app-group entitlements all derive from this value.
APP_BUNDLE_ID = com.berkin.SwiftAIBoilerplatePro
SWIFT_VERSION = 6.0
IPHONEOS_DEPLOYMENT_TARGET = 17.0
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor

# Include secrets if present (local only)
#include? "Config/Secrets.xcconfig"
```

## Environment Variables

Set in **Xcode Scheme** → Edit Scheme → Run → Arguments → Environment Variables.

| Variable                | Reads in   | Default behavior            | Purpose                                                                                                        |
| ----------------------- | ---------- | --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `AUTH_BYPASS`           | DEBUG only | Mock used unless set to `0` | Set `0` to use the real Supabase auth client (and `MockPaymentsClient` is also swapped for `RevenueCatClient`) |
| `DIAGNOSTICS_ENABLED`   | RELEASE    | `false`                     | Set `true` to enable diagnostics in Release                                                                    |
| `CRASHLYTICS_ENABLED`   | RELEASE    | `false`                     | Set `true` to enable Firebase Crashlytics in Release                                                           |
| `CHAT_SYNC_ENABLED`     | RELEASE    | `false`                     | Set `true` to enable Supabase chat history sync in Release                                                     |
| `PROXY_DEFAULT_HEADERS` | Any        | none                        | Optional JSON string of extra headers for the AI proxy                                                         |

The mock-vs-real auth decision is made in `CompositionRoot.swift` from `AUTH_BYPASS`, not in `FeatureFlags`. In RELEASE the app always uses real auth; the `MockAuthClient` is DEBUG-only.

## Feature Flags

**Location:** `SwiftAIBoilerplatePro/Composition/FeatureFlags.swift`

```swift theme={null}
import Foundation

/// Feature flags for optional capabilities
public struct FeatureFlags {
    /// Master switch for diagnostics (MetricKit always enabled)
    public static var diagnosticsEnabled: Bool {
        #if DEBUG
        return true
        #else
        return ProcessInfo.processInfo.environment["DIAGNOSTICS_ENABLED"] == "true"
        #endif
    }

    /// Enable Firebase Crashlytics
    public static var crashlyticsEnabled: Bool {
        #if DEBUG
        return false // Off by default in debug
        #else
        return ProcessInfo.processInfo.environment["CRASHLYTICS_ENABLED"] == "true"
        #endif
    }

    /// Enable Supabase chat history sync
    /// When false (default), chat data stays local-only in SwiftData.
    /// See docs/CHAT_SYNC_SETUP.md for setup instructions.
    public static var chatSyncEnabled: Bool {
        #if DEBUG
        return false // Off by default in debug
        #else
        return ProcessInfo.processInfo.environment["CHAT_SYNC_ENABLED"] == "true"
        #endif
    }
}
```

## Backend Configuration

### Supabase

<Tabs>
  <Tab title="Development">
    ```bash theme={null}
    SUPABASE_URL = https://dev-project.supabase.co
    SUPABASE_ANON_KEY = dev_anon_key
    PROXY_BASE_URL = https://dev-project.supabase.co/functions/v1
    ```
  </Tab>

  <Tab title="Production">
    ```bash theme={null}
    SUPABASE_URL = https://prod-project.supabase.co
    SUPABASE_ANON_KEY = prod_anon_key
    PROXY_BASE_URL = https://prod-project.supabase.co/functions/v1
    ```
  </Tab>
</Tabs>

**Get credentials:** [Supabase Dashboard](https://app.supabase.com) → Project Settings → API

### OpenRouter

**Set as Supabase secret:**

```bash theme={null}
supabase secrets set OPENROUTER_API_KEY=sk-or-v1-YOUR_KEY
```

**Get key:** [OpenRouter Dashboard](https://openrouter.ai/keys)

### RevenueCat

```bash theme={null}
REVENUECAT_API_KEY = appl_YOUR_PUBLIC_KEY
RC_ENTITLEMENT_ID = pro
```

**Get key:** [RevenueCat Dashboard](https://app.revenuecat.com) → Settings → API Keys

## CompositionRoot Configuration

**Location:** `SwiftAIBoilerplatePro/Composition/CompositionRoot.swift`

The DI container reads configuration from the generated `AppConfiguration` enum and chooses real or mock implementations. The LLM client is selected by `createLLMClient(...)` in `LLMClientFactory.swift`:

```swift theme={null}
// Auth: real Supabase + Apple + Email, or MockAuthClient in DEBUG (CompositionRoot.swift)
#if DEBUG
let shouldUseMock = ProcessInfo.processInfo.environment["AUTH_BYPASS"] != "0"
if shouldUseMock {
    self.sessionManager = Auth.MockAuthClient()
} else {
    self.sessionManager = SessionManagerWrapper(/* Supabase SessionManager */)
}
#endif

// LLM: ProxyLLMClient when PROXY_BASE_URL is real, EchoLLMClient otherwise (LLMClientFactory.swift)
guard let baseURL = URL(string: AppConfiguration.PROXY_BASE_URL),
      !AppConfiguration.PROXY_BASE_URL.contains("YOUR"),
      !AppConfiguration.PROXY_BASE_URL.contains("placeholder") else {
    return EchoLLMClient()
}
return ProxyLLMClient(baseURL: baseURL, httpClient: httpClient, path: AppConfiguration.PROXY_PATH)
```

When `PROXY_BASE_URL` is unconfigured the app also wires an `UnconfiguredHTTPClient` instead of a real `URLSessionHTTPClient`, so unconfigured backends fail explicitly rather than calling a placeholder host.

## Xcode Build Configuration

### Debug vs Release

**Debug (default):**

* Mock auth enabled by default
* Detailed logging
* Crashlytics disabled
* No optimization

**Release:**

* Real auth required
* Production logging
* Crashlytics opt-in (requires `CRASHLYTICS_ENABLED=true` and a `GoogleService-Info.plist`)
* Full optimization

**Switch configuration:**

```text theme={null}
Product → Scheme → Edit Scheme → Run → Build Configuration
```

## Quick Reference

<AccordionGroup>
  <Accordion title="Enable Real Auth in DEBUG">
    1. Edit Scheme → Run → Environment Variables
    2. Set `AUTH_BYPASS` = `0`
    3. Fill in `Config/Secrets.xcconfig` with real Supabase credentials
    4. Run `bash scripts/update-config.sh`
    5. Clean and rebuild
  </Accordion>

  <Accordion title="Enable Chat Sync">
    1. Run SQL migration
    2. Set `chatSyncEnabled = true` in FeatureFlags
    3. Uncomment Supabase repositories
    4. Wire up in CompositionRoot
  </Accordion>

  <Accordion title="Switch to Production Backend">
    1. Update `Config/Secrets.xcconfig` with production URLs/keys
    2. Run `bash scripts/update-config.sh` to regenerate Configuration.swift
    3. Verify in RELEASE build configuration
    4. Archive and test
  </Accordion>
</AccordionGroup>

## Related Resources

<CardGroup cols={2}>
  <Card title="Installation" href="/pages/installation">
    Initial configuration
  </Card>

  <Card title="Supabase Setup" href="/pages/guides/supabase-setup">
    Backend configuration
  </Card>

  <Card title="Building Guide" href="/pages/guides/building-your-app">
    Production configuration
  </Card>

  <Card title="Architecture" href="/pages/architecture">
    How config flows through app
  </Card>
</CardGroup>
