Implementing Environment Switcher for iOS and Android: Dev, Staging, Prod
We often encounter this situation: a developer tests a feature against the dev server, QA verifies on staging before release, and support reproduces a user bug on production data. Without a mechanism to switch environments, each new build for a different environment takes 20–30 minutes of compilation plus upload and installation time. Our experience—over 50 projects with such a system—shows that proper architecture reduces testing time by up to 30%.
An Environment Switcher is 10 times faster than hardcoded URLs for switching environments. Instead of three different artifacts, you have a single build with a UI selection.
How Runtime Environment Switching Works
The most common mistake is hardcoded URLs in the code. When the base address is stored in Constants.swift or BuildConfig, each environment change requires code modification and a new build. CI then produces three separate artifacts for dev, staging, and prod. Testers wait for a new build to verify the same feature on staging. This does not scale and increases cycle time by 2–3 times.
A proper architecture is built on three levels:
- Build-time configuration – sets the default environment for each build configuration.
- Runtime switching – allows changing the environment without rebuilding for debug/beta builds.
- Instance restart – safely updates the network layer and clears data on environment change.
Example build-time configuration for Android
buildTypes {
debug {
buildConfigField "String", "DEFAULT_ENV", "\"dev\""
buildConfigField "String", "API_URL_DEV", "\"https://api-dev.example.com\""
buildConfigField "String", "API_URL_STAGING", "\"https://api-staging.example.com\""
buildConfigField "String", "API_URL_PROD", "\"https://api.example.com\""
}
release {
buildConfigField "String", "DEFAULT_ENV", "\"prod\""
// staging and dev URLs not needed in release
}
}
Example runtime switching for iOS
enum Environment: String, CaseIterable {
case dev = "dev"
case staging = "staging"
case production = "production"
var baseURL: URL {
switch self {
case .dev: return URL(string: "https://api-dev.example.com")!
case .staging: return URL(string: "https://api-staging.example.com")!
case .production: return URL(string: "https://api.example.com")!
}
}
}
final class EnvironmentManager {
static let shared = EnvironmentManager()
var current: Environment {
get {
let raw = UserDefaults.standard.string(forKey: "app_environment")
?? Environment.dev.rawValue
return Environment(rawValue: raw) ?? .dev
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: "app_environment")
NotificationCenter.default.post(name: .environmentDidChange, object: nil)
}
}
}
After switching environments, you must restart the network layer: log out the user, clear the cache, recreate URLSession or OkHttpClient. The best approach is to use a DI container that recreates dependencies when the environment changes. Apple recommends recreating resource-heavy objects on configuration change.
Environment Parameter Comparison
| Parameter | Dev | Staging | Prod |
|---|---|---|---|
| API base URL | https://api-dev.example.com | https://api-staging.example.com | https://api.example.com |
| Firebase project | dev | staging | production |
| APNs environment | development | development | production |
| Analytics tracking ID | dev-xxxx | staging-xxxx | prod-xxxx |
Hardcoded URLs vs Environment Switcher
| Criterion | Hardcoded URLs | Environment Switcher |
|---|---|---|
| Environment switching | Requires code change and rebuild | UI selection or settings |
| Time to test one version | 30–60 minutes build + verification | 2–3 minutes to switch |
| Risk of error (wrong build) | High (three different artifacts) | Low (single build with selection) |
| Data security | High (hardcoded in release) | Medium (requires #if DEBUG) |
Runtime environment switching improves QA productivity by 10 times compared to hardcoded URLs.
Why Isolating Dev Configurations from Release Is Critical
Production builds must not contain staging or dev server URLs—they are attack surfaces. Use conditional compilation (#if DEBUG / debug build flavor) to ensure the Environment Switcher code does not end up in the release binary. We guarantee that all sensitive data remains protected.
What Is Included in a Turnkey Solution
Our service includes the full cycle of implementing an Environment Switcher:
- Audit current architecture – identify hardcoded URLs and suboptimal configurations.
- Design – choose between build-time vs runtime approach based on your stack.
- Implementation – code in Swift/Kotlin supporting all environments, including a UI switcher.
- Firebase integration – separate projects (analytics, Remote Config, Crashlytics) per environment.
- APNs/FCM configuration – correct push notification environment.
- Testing and documentation – instructions for QA and developers.
Timeline: from 1 to 3 days. A simple switcher with two environments takes one day; full integration takes up to three days. Cost is determined after individual assessment. Implementation reduces CI costs by decreasing the number of build artifacts from three to one. Developer time savings reach up to 40% per testing cycle. Contact us for a consultation—we will assess your project within one day.
Step-by-Step Implementation Guide
- Define the list of environments (dev, staging, prod) and their parameters (URL, Firebase project, WebSocket).
- Implement build-time flags for each configuration (xcconfig / buildConfigField).
- Create an enum Environment with baseURL and other properties.
- Add a manager to store the current environment (UserDefaults/SharedPreferences).
- Implement a UI switcher (Debug Menu) with a warning about logging out.
- Update the network layer — pass Environment via DI, recreate on change.
- Add conditional compilation to remove the switcher from release builds.
- Verify behavior — switching environments should not break session without login.
Use a separate GoogleService-Info.plist or google-services.json per environment. File selection is done at build time via Build Settings / Gradle. You can switch Firebase projects at runtime, but it is more complex and often unnecessary—a single project with different configurations via Remote Config suffices.
We have 5+ years of experience in mobile development and certified iOS and Android engineers. Order a turnkey Environment Switcher implementation—get a consultation within one day.







