DEV Community

Sebastien Lato
Sebastien Lato

Posted on

SwiftUI App Configuration System (Remote Config, Secrets, Overrides)

Most apps treat configuration like a junk drawer.

Symptoms:

  • constants scattered everywhere
  • magic numbers in views
  • flags mixed with secrets
  • “just change it in prod” panic
  • different behavior no one can explain

Configuration is not data.
It is control plane architecture.

This post shows how to design a clean, layered configuration system in SwiftUI that supports:

  • remote config
  • environment overrides
  • safe defaults
  • secret isolation
  • predictable behavior across builds

🧠 The Core Principle

Configuration decides what the app is allowed to do —

not how it does it.

If config logic leaks into views, the system is already broken.


🧱 1. Configuration Is a First-Class Domain

Never do:

if UserDefaults.standard.bool(forKey: "new_ui") { ... }
Enter fullscreen mode Exit fullscreen mode

Instead, define a domain model:

struct AppConfig {
    let apiBaseURL: URL
    let featureFlags: FeatureFlags
    let experiments: Experiments
    let limits: Limits
}
Enter fullscreen mode Exit fullscreen mode

The app reads one config object.


📦 2. Configuration Sources (Layered)

A real app has multiple sources:

  1. Hardcoded defaults (safe baseline)
  2. Build-time config (Debug / Staging / Prod)
  3. Remote config (server-controlled)
  4. Local overrides (dev / QA only)

Each layer overrides the previous one.


🧭 3. Config Provider Architecture

protocol ConfigProvider {
    func load() async throws -> AppConfig
}
Enter fullscreen mode Exit fullscreen mode

Implementations:

  • DefaultConfigProvider
  • RemoteConfigProvider
  • OverrideConfigProvider

Compose them:

final class AppConfigLoader {
    func load() async -> AppConfig {
        let base = DefaultConfig()
        let remote = await RemoteConfig().merge(into: base)
        let overridden = Overrides().merge(into: remote)
        return overridden
    }
}
Enter fullscreen mode Exit fullscreen mode

One pipeline. Predictable behavior.


🔐 4. Secrets Are NOT Configuration

Never put secrets in:

  • remote config
  • plist files
  • feature flags
  • JSON

Secrets belong in:

  • Keychain
  • secure backend
  • injected credentials

Config may reference capabilities, not secrets.


🧬 5. Typed Access Only

Bad:

config["max_items"]
Enter fullscreen mode Exit fullscreen mode

Good:

config.limits.maxItems
Enter fullscreen mode Exit fullscreen mode

This gives:

  • compile-time safety
  • refactorability
  • documentation

🧪 6. Environment Overrides (Dev & QA)

Allow overrides only outside production:

#if DEBUG
config = config.withOverrides(localOverrides)
#endif
Enter fullscreen mode Exit fullscreen mode

Rules:

  • overrides never ship to prod
  • overrides are visible in UI
  • overrides are resettable

No hidden behavior.


⚠️ 7. Safe Defaults & Fallbacks

Remote config will fail sometimes.

Rules:

  • defaults must be safe
  • app must boot without network
  • missing values must not crash
  • unknown keys must be ignored

If config failure bricks the app, architecture failed.


🧠 8. Config Change Handling

Config changes should:

  • apply at safe boundaries
  • not rewire running features
  • avoid mid-session flips

Correct:

  • apply on next launch
  • apply on feature entry
  • apply on refresh boundaries

Never mutate live state arbitrarily.


❌ 9. Common Configuration Anti-Patterns

Avoid:

  • reading config in views
  • mixing config with business logic
  • using config for authorization
  • shipping debug toggles
  • stringly-typed access
  • no ownership or documentation

Config rot is real.


🧠 Mental Model

Think:

Defaults
 → Build Config
   → Remote Config
     → Local Overrides
       → App Behavior
Enter fullscreen mode Exit fullscreen mode

Not:

“Just check this flag here”


🚀 Final Thoughts

A proper configuration system gives you:

  • safe rollouts
  • predictable environments
  • faster debugging
  • fewer hotfixes
  • calmer releases

Configuration is infrastructure, not glue code.

Top comments (0)