Firebase Remote Config
Tested with Firebase version 12.2.0 (iOS)
Using Appfigurate remote properties instead of Firebase APIs directly allows for the following:
locally change Firebase remote configuration without affecting your entire customer base.
compile time type safety - Appfigurate remote properties are typed.
avoids hardcoding duplicated flag names throughout your app.
deleting a remote configuration property from Firebase Console won't affect existing apps, they'll continue to use the default value provided in the
resetmethod.
We assume you already have Appfigurate Library and Firebase integrated into your app with the following example remote properties created in the Firebase Console:

Add remote properties into your Configuration subclass
Update your configuration subclass to include your remote configuration properties. Provide default values for the properties in the overridden reset method. See also Supported property types.
Swift Configuration example
import Foundation
import AppfigurateLibrary
@objcMembers class MyConfiguration: APLConfiguration {
...
@RemoteBoolProperty(key: "alwaysDarkMode", description: "Force dark mode to be always set")
var alwaysDarkMode: Bool
@RemoteStringPropertyEdit(key: "appTitle", description: "Title of application")
var appTitle: String
@RemoteIntPropertyEdit(key: "bookingDuration", description: "Duration (days) for reservation bookings")
var bookingDuration: Int
@RemoteDoublePropertyEdit(key: "fontSize", description: "Size of font throughout app")
var fontSize: Double
...
override func reset() {
alwaysDarkMode = false
appTitle = "Holiday finder"
bookingDuration = 30
fontSize = 13.0
}
...
}Objective-C Configuration header example
@import Foundation;
@import AppfigurateLibrary;
@interface MyConfiguration : APLConfiguration
...
@property(nonatomic, assign) BOOL alwaysDarkMode;
@property(nonatomic, strong) NSString* appTitle;
@property(nonatomic, assign) NSInteger bookingDuration;
@property(nonatomic, assign) double fontSize;
...
@end
Objective-C Configuration implementation example
#import "MyConfiguration.h"
@implementation MyConfiguration
...
REMOTE_BOOL_PROPERTY(alwaysDarkMode, @"alwaysDarkMode", @"Force dark mode to be always set");
REMOTE_STRING_PROPERTY_EDIT(appTitle, @"appTitle", @"Title of application");
REMOTE_INT_PROPERTY_EDIT(bookingDuration, @"bookingDuration", @"Duration (days) for reservation bookings");
REMOTE_DOUBLE_PROPERTY_EDIT(fontSize, @"fontSize", @"Size of font throughout app");
...
- (void) reset {
self.alwaysDarkMode = NO;
self.appTitle = @"Holiday finder";
self.bookingDuration = 30;
self.fontSize = 13.0;
}
...
@endKotlin Configuration example
package com.yourcompany.yourapp
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteBooleanProperty
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteDoublePropertyEdit
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteIntPropertyEdit
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteStringPropertyEdit
class MyConfiguration : nz.co.electricbolt.appfiguratelibrary.Configuration() {
...
@RemoteBooleanProperty(propertyKey = "alwaysDarkMode", description = "Force dark mode to be always set")
var alwaysDarkMode: Boolean = false
@RemoteStringPropertyEdit(propertyKey = "appTitle", description = "Title of application")
var appTitle: String? = null
@RemoteDoublePropertyEdit(propertyKey = "fontSize", description = "Size of font throughout app")
var fontSize: Double = 0.0
@RemoteIntPropertyEdit(propertyKey = "bookingDuration", description = "Duration (days) for reservation bookings")
var bookingDuration: Int = 0
...
override fun reset() {
alwaysDarkMode = false
appTitle = "Holiday finder"
bookingDuration = 30
fontSize = 13.0
}
}Java Configuration example
package com.yourcompany.yourapp;
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteBooleanProperty;
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteDoublePropertyEdit;
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteIntPropertyEdit;
import nz.co.electricbolt.appfiguratelibrary.annotations.RemoteStringPropertyEdit;
public class MyConfiguration extends nz.co.electricbolt.appfiguratelibrary.Configuration {
...
@RemoteBooleanProperty(propertyKey = "alwaysDarkMode", description = "Force dark mode to be always set")
public boolean alwaysDarkMode;
@RemoteStringPropertyEdit(propertyKey = "appTitle", description = "Title of application")
public String appTitle;
@RemoteDoublePropertyEdit(propertyKey = "fontSize", description = "Size of font throughout app")
public double fontSize;
@RemoteIntPropertyEdit(propertyKey = "bookingDuration", description = "Duration (days) for reservation bookings")
public int bookingDuration;
...
@Override
public void reset() {
alwaysDarkMode = false;
appTitle = "Holiday finder";
bookingDuration = 30;
fontSize = 13.0;
}
...
}Provide remote configuration values to Appfigurate Library when requested
Appfigurate Library needs to be able to read the current remote configuration values from Firebase. Copy and paste the following code into your app.
The defaultValue parameter in the callback is not used with Firebase. See the Apply default values for Firebase section below.
Swift example
APLFetchRemoteConfiguration { remoteKey, propertyType, defaultValue in
if propertyType == .string {
return self.remoteConfig.configValue(forKey: remoteKey).stringValue as NSObject
} else if propertyType == .bool {
return self.remoteConfig.configValue(forKey: remoteKey).boolValue as NSObject
} else { // .int || .double
return self.remoteConfig.configValue(forKey: remoteKey).numberValue as NSObject
}
}Objective-C example
APLFetchRemoteConfiguration(^NSObject* (NSString* remoteKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
if (propertyType == APLRemotePropertyTypeString) {
return [self.remoteConfig configValueForKey: remoteKey].stringValue;
} else if (propertyType == APLRemotePropertyTypeBool) {
return [NSNumber numberWithBool: [self.remoteConfig configValueForKey: remoteKey].boolValue];
} else { // APLRemotePropertyTypeInt || APLRemotePropertyTypeDouble
return [self.remoteConfig configValueForKey: remoteKey].numberValue;
}
});Kotlin example
Appfigurate.fetchRemoteConfiguration { remoteKey: String, propertyType: RemotePropertyType, defaultValue: Any? ->
when (propertyType) {
RemotePropertyTypeString -> this.remoteConfig.getString(remoteKey)
RemotePropertyTypeBoolean -> this.remoteConfig.getBoolean(remoteKey)
RemotePropertyTypeInt -> this.remoteConfig.getLong(remoteKey)
RemotePropertyTypeDouble -> this.remoteConfig.getDouble(remoteKey)
}
}Java example
Appfigurate.fetchRemoteConfiguration((remoteKey, propertyType, defaultValue) -> {
return switch (propertyType) {
case RemotePropertyTypeString -> this.remoteConfig.getString(remoteKey);
case RemotePropertyTypeBoolean -> this.remoteConfig.getBoolean(remoteKey);
case RemotePropertyTypeInt -> this.remoteConfig.getLong(remoteKey);
case RemotePropertyTypeDouble -> this.remoteConfig.getDouble(remoteKey);
};
});Apply default values to Firebase
Apply default values to Firebase using the values you set in the reset method of your configuration subclass. The default values are used by Firebase when the remote configuration has not yet been received.
The APLConfiguration class has a method remoteDefaults which provides a Dictionary of all the remote configuration property default values that were set in your overridden reset method.
Swift example
FirebaseApp.configure()
self.remoteConfig = RemoteConfig.remoteConfig()
...
// add the following line
self.remoteConfig.setDefaults(APLConfiguration.shared().remoteDefaults())The APLConfiguration class has a method remoteDefaults which provides a NSDictionary* of all the remote configuration property default values that were set in your overridden reset method.
Objective-C example
[FIRApp configure];
self.remoteConfig = [FIRRemoteConfig remoteConfig];
...
// add the following line
[self.remoteConfig setDefaults: [[APLConfiguration sharedConfiguration] remoteDefaults]];The nz.co.electricbolt.appfiguratelibrary.Configuration class has a method remoteDefaults which provides a HashMap<String,Object> of all the remote configuration property default values that were set in your overridden reset method.
Kotlin example
FirebaseApp.initializeApp(this)
this.remoteConfig = FirebaseRemoteConfig.getInstance()
...
// add the following line
this.remoteConfig.setDefaultsAsync(Configuration.sharedConfiguration().remoteDefaults())The nz.co.electricbolt.appfiguratelibrary.Configuration class has a method remoteDefaults which provides a HashMap<String,Object> of all the remote configuration property default values that were set in your overridden reset method.
Java example
FirebaseApp.initializeApp(this)
this.remoteConfig = FirebaseRemoteConfig.getInstance()
...
// add the following line
this.remoteConfig.setDefaultsAsync(Configuration.sharedConfiguration().remoteDefaults());Notify Appfigurate Library when Firebase has received remote configuration values
We need to tell Appfigurate Library that Firebase has received remote configuration, so that it can keep your Configuration subclass remote properties in sync.
Add a call to APLFlushRemoteConfiguration in any existing Firebase Remote Config activate(completion:) blocks (inside fetch(completionHandler:) and optionally addOnConfigUpdateListener(remoteConfigUpdateCompletion:).
Swift example
self.remoteConfig.fetch { status, error in
if status == .success {
self.remoteConfig.activate { changed, error in
...
APLFlushRemoteConfiguration() // add this line
}
}
}Add a call to APLFlushRemoteConfiguration in any existing Firebase Remote Config activateWithCompletion: blocks (inside fetchWithCompletionHandler: and optionally addOnConfigUpdateListener:).
Objective-C example
__weak __typeof__(self) weakSelf = self;
[self.remoteConfig fetchWithCompletionHandler: ^(FIRRemoteConfigFetchStatus status, NSError* error) {
if (status == FIRRemoteConfigFetchStatusSuccess) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
...
APLFlushRemoteConfiguration(); // add this line
}];
}];Add a call to Appfigurate.flushRemoteConfiguration in your existing Firebase Remote Config fetchAndActivate block:
Kotlin fetch and activate example
this.remoteConfig.fetchAndActivate().addOnCompleteListener(this) { task ->
if (task.isSuccessful()) {
...
Appfigurate.flushRemoteConfiguration() // add this line
}
}If you are optionally using Firebase Remote Config real time updates add a call to Appfigurate.flushRemoteConfiguration in your existing addOnConfigUpdateListener block:
Kotlin real time update example
this.remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
override fun onUpdate(configUpdate: ConfigUpdate) {
FirebaseRemoteConfig.getInstance().activate().addOnCompleteListener {
...
Appfigurate.flushRemoteConfiguration() // add this line
}
}
...
})Add a call to Appfigurate.flushRemoteConfiguration in your existing Firebase Remote Config fetchAndActivate block:
Java fetch and activate example
this.remoteConfig.fetchAndActivate().addOnCompleteListener(this, task -> {
if (task.isSuccessful()) {
...
Appfigurate.flushRemoteConfiguration(); // add this line
}
});If you are optionally using Firebase Remote Config real time updates add a call to Appfigurate.flushRemoteConfiguration in your existing addOnConfigUpdateListener block:
Java real time update example
this.remoteConfig.addOnConfigUpdateListener(new ConfigUpdateListener() {
@Override public void onUpdate(ConfigUpdate configUpdate) {
this.remoteConfig.activate().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
...
Appfigurate.flushRemoteConfiguration(); // add this line
}
});
}
...
});Complete initialisation example
Swift complete initialisation example
FirebaseApp.configure()
let remoteConfig = RemoteConfig.remoteConfig()
// add the following line
remoteConfig.setDefaults(APLConfiguration.shared().remoteDefaults())
// add this block of code
APLFetchRemoteConfiguration { propertyKey, propertyType, _ in
if propertyType == .string {
return self.remoteConfig.configValue(forKey: propertyKey).stringValue as NSObject
} else if propertyType == .bool {
return self.remoteConfig.configValue(forKey: propertyKey).boolValue as NSObject
} else { // .int || .double
return self.remoteConfig.configValue(forKey: propertyKey).numberValue as NSObject
}
}
remoteConfig.fetch { status, error in
if status == .success {
self.remoteConfig.activate { changed, error in
APLFlushRemoteConfiguration() // add this line
}
}
}
remoteConfig.addOnConfigUpdateListener { configUpdate, error in
if error == nil {
self.remoteConfig.activate { changed, error in
APLFlushRemoteConfiguration() // add this line
}
}
}Objective-C complete initialisation example
[FIRApp configure];
self.remoteConfig = [FIRRemoteConfig remoteConfig];
// add the following line
[self.remoteConfig setDefaults: [[APLConfiguration sharedConfiguration] remoteDefaults]];
// add this block of code
APLFetchRemoteConfiguration(^NSObject* (NSString* propertyKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
if (propertyType == APLRemotePropertyTypeString)
return [self.remoteConfig configValueForKey: propertyKey].stringValue;
else if (propertyType == APLRemotePropertyTypeBool)
return [NSNumber numberWithBool: [self.remoteConfig configValueForKey: propertyKey].boolValue];
else // APLRemotePropertyTypeInt || APLRemotePropertyTypeDouble
return [self.remoteConfig configValueForKey: propertyKey].numberValue;
});
__weak __typeof__(self) weakSelf = self;
[self.remoteConfig fetchWithCompletionHandler: ^(FIRRemoteConfigFetchStatus status, NSError* error) {
if (status == FIRRemoteConfigFetchStatusSuccess) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
if (error == nil) {
APLFlushRemoteConfiguration(); // add this line
}
}];
}
}];
[self.remoteConfig addOnConfigUpdateListener: ^(FIRRemoteConfigUpdate* configUpdate, NSError* error) {
if (error == nil) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
if (error == nil) {
APLFlushRemoteConfiguration(); // add this line
}
}];
}
}];Kotlin complete initialisation example
FirebaseApp.initializeApp(this)
val remoteConfig = FirebaseRemoteConfig.getInstance()
// add the following line
remoteConfig.setDefaultsAsync(Configuration.sharedConfiguration().remoteDefaults())
// add this block of code
Appfigurate.fetchRemoteConfiguration { propertyKey: String, propertyType: RemotePropertyType, defaultValue: Any? ->
when (propertyType) {
RemotePropertyTypeString -> remoteConfig.getString(propertyKey)
RemotePropertyTypeBoolean -> remoteConfig.getBoolean(propertyKey)
RemotePropertyTypeInt -> remoteConfig.getLong(propertyKey)
RemotePropertyTypeDouble -> remoteConfig.getDouble(propertyKey)
}
}
remoteConfig.fetchAndActivate()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful()) {
Appfigurate.flushRemoteConfiguration() // add this line
}
}
remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
override fun onUpdate(configUpdate: ConfigUpdate) {
remoteConfig.activate().addOnCompleteListener {
Appfigurate.flushRemoteConfiguration() // add this line
}
}
...
})Java complete initialisation example
FirebaseApp.initializeApp(this);
FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
// add the following line
remoteConfig.setDefaultsAsync(Configuration.sharedConfiguration().remoteDefaults());
// add this block of code
Appfigurate.fetchRemoteConfiguration((remoteKey, propertyType, defaultValue) -> {
return switch (propertyType) {
case RemotePropertyTypeString -> this.remoteConfig.getString(remoteKey);
case RemotePropertyTypeBoolean -> this.remoteConfig.getBoolean(remoteKey);
case RemotePropertyTypeInt -> this.remoteConfig.getLong(remoteKey);
case RemotePropertyTypeDouble -> this.remoteConfig.getDouble(remoteKey);
};
});
remoteConfig.fetchAndActivate().addOnCompleteListener(this, task -> {
if (task.isSuccessful()) {
Appfigurate.flushRemoteConfiguration(); // add this line
}
});
remoteConfig.addOnConfigUpdateListener(new ConfigUpdateListener() {
@Override public void onUpdate(ConfigUpdate configUpdate) {
this.remoteConfig.activate().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Appfigurate.flushRemoteConfiguration(); // add this line
}
});
}
...
});Best practice and usage
Replace all calls to Firebase Remote Config configValue(forKey:):
if remoteConfig.configValue(forKey: "alwaysDarkMode").boolValue {
...with the following:
if (CONFIGURATION().alwaysDarkMode) {
...Replace all calls to Firebase Remote Config configValueForKey:
if ([remoteConfig configValueForKey: @"alwaysDarkMode"].boolValue]) {
...with the following:
if (CONFIGURATION.alwaysDarkMode) {
...Replace all calls to Firebase Remote Config getBoolean(), getLong(), getDouble(), getString()
if this.remoteConfig.getBoolean("alwaysDarkMode") {
...with the following:
if (CONFIGURATION().alwaysDarkMode) {
...Replace all calls to Firebase Remote Config getBoolean(), getLong(), getDouble(), getString()
if (this.remoteConfig.getBoolean("alwaysDarkMode")) {
...with the following:
if (CONFIGURATION().alwaysDarkMode) {
...Last updated