Firebase Remote Config Integration for Mobile Apps
We often encounter client requests to quickly change app parameters without a full release cycle. For example, changing the color of a promo button or the free delivery threshold. Without Remote Config, this requires a hotfix, a new review, and waiting for moderation in App Store or Google Play — from several hours to days. With Remote Config, you simply change a value in the Firebase console, and within 20–30 seconds the new config applies to all devices. No app version update needed. We use this tool in every project where flexibility in managing user experience matters — over 5 years we have integrated it into 200+ mobile apps serving more than 500,000 active users in total.
How Firebase Remote Config Works Under the Hood
Remote Config is a Key-Value store with a server side in the Firebase Console and client SDKs for iOS/Android/Flutter. The Firebase Remote Config Documentation describes two key actions: fetch() and activate(). fetch downloads the config to a staging cache, activate applies it at runtime. The separation is intentional — to avoid breaking the current user session. The cache lifetime is set via minimumFetchInterval: in production 3600 seconds, in debug you can set 0.
// iOS, Swift let remoteConfig = RemoteConfig.remoteConfig() let settings = RemoteConfigSettings() settings.minimumFetchInterval = 3600 // в проде — 1 час, в дебаге можно 0 remoteConfig.configSettings = settings // Дефолты — обязательны, иначе до первого fetch значения nil remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults") remoteConfig.fetchAndActivate { status, error in if status == .successFetchedFromRemote || status == .successUsingPreFetchedData { let buttonColor = remoteConfig["promo_button_color"].stringValue ?? "#FF5722" DispatchQueue.main.async { self.applyConfig(buttonColor) } } } On Android via Kotlin:
val remoteConfig = Firebase.remoteConfig remoteConfig.setConfigSettingsAsync(remoteConfigSettings { minimumFetchIntervalInSeconds = 3600 }) remoteConfig.setDefaultsAsync(R.xml.remote_config_defaults) remoteConfig.fetchAndActivate().addOnCompleteListener { task -> if (task.isSuccessful) { val threshold = remoteConfig.getLong("free_delivery_threshold") updateCartUI(threshold) } } Why Defaults Are Critical
If you access a key without a default before the first successful fetch (e.g., due to no network), you get nil or an empty string. The app won't crash, but behavior becomes unpredictable. Defaults must cover every key your app uses. We recommend storing them in a separate file (Plist for iOS, XML for Android) and syncing with the keys in the Firebase Console. We guarantee that after setting defaults, you won't face surprises on first launch. In one project, missing defaults caused 15% of users to see a placeholder instead of a banner — fixing it took 2 minutes in the console.
How to Avoid Race Conditions at Startup
fetchAndActivate is asynchronous. If the UI is drawn before the fetch completes, the user sees default values. For critical parameters (e.g., a paywall display flag), it's better to load the config on the splash screen and block navigation until the response is received — with a 2–3 second timeout. We use this approach in every project: the delay is imperceptible, and the logic works reliably.
How to Set Up Conditional Values for A/B Tests
The real power of Remote Config lies in conditions. You can assign different values for:
- specific app versions (
app_version < 2.5.0) - platform (iOS vs Android)
- user country
- any custom
user_propertyset via Firebase Analytics
For example, show a new onboarding only to iOS 16+ users in Russia — without a separate release. This speeds up A/B tests and regional customization. For an A/B test, you create a configuration that assigns different values to control and test groups. Results are tracked via Firebase Analytics. Typical time savings: up to 40 hours per feature flag that previously required a release.
Common mistake: forgot defaults
On first launch without network, fetch doesn't execute, and keys without defaults become null. To avoid this, always set defaults for all keys. We recommend storing them in a separate file and syncing with the Firebase console. In one project, missing defaults caused 15% of users to see a placeholder instead of a banner — fixing it took 2 minutes in the console.Table: Platforms and Initial Setup
| Parameter | iOS (Swift) | Android (Kotlin) | Flutter (Dart) |
|---|---|---|---|
| SDK Integration | Swift Package Manager firebase-ios-sdk |
Gradle com.google.firebase:firebase-config |
firebase_remote_config from pub.dev |
| Defaults | Plist file | XML file (remote_config_defaults.xml) |
Dart map in setDefaults |
| Minimum Fetch Interval | minimumFetchInterval = 3600 |
minimumFetchIntervalInSeconds = 3600 |
setMinimumFetchInterval(3600) |
| Getting a Value | config["key"].stringValue |
config.getLong("key") |
config.getString("key") |
| Typed Access | Helper class with enum | Helper class with sealed class | Helper class with const |
Table: Popular Condition Types
| Condition | Example | Use Case |
|---|---|---|
| App version | app_version < 2.5.0 |
Enable a feature only for old versions |
| Country | country == "RU" |
Localize an offer |
| Custom user property | user_property["premium"] == "true" |
A/B test among paying users |
| Random percentage | random_percent < 10 |
Gradual rollout of a new feature |
What's Included in the Work
- Firebase SDK connection (via SPM for iOS, Gradle for Android,
firebase_remote_configfor Flutter) -
RemoteConfigSettingsconfiguration with correct intervals for debug/release - Defaults file covering all keys
- Typed helper class for accessing values (no string keys in code)
- Integration with the app's initialization point (AppDelegate / Application)
Timelines and Cost
Basic integration with a typed helper takes 1 day. The cost is calculated individually after requirements analysis. Contact us for a consultation: we'll help estimate the scope and configure Remote Config for your needs. Get a consultation — and you'll see how easy it is to manage features without releases. Reach out to us to discuss your project.







