# Firebase Remote Config

{% hint style="success" %}
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 [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
  {% endhint %}

We assume you already have Appfigurate Library and [Firebase](https://firebase.google.com/docs/remote-config) integrated into your app with the following example remote properties created in the [Firebase Console](https://console.firebase.google.com/):

<figure><img src="https://1008176080-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fw1fcw3dvtSrfUh3YtO9Z%2Fuploads%2FQLmapsDXTX7kb4JtnWcJ%2Fimage.png?alt=media&#x26;token=ad0a6b60-9d80-4c2e-9b5d-429ca6f812cd" alt="" width="563"><figcaption></figcaption></figure>

### 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`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method. See also [Supported property types](https://docs.electricbolt.co.nz/configuration-subclasses/supported-property-types#remote-properties).

{% tabs %}
{% tab title="Swift" %}

> Swift Configuration example

```swift
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
    }
...
}
```

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C Configuration header example

```objectivec
@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

```objectivec
#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;
}
...
@end
```

{% endtab %}
{% endtabs %}

### 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](#apply-default-values-to-firebase) section below.

{% tabs %}
{% tab title="Swift" %}

> Swift example

```swift
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
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C example

```objectivec
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;
    }
});
```

{% endtab %}
{% endtabs %}

### Apply default values to Firebase

Apply default values to Firebase using the values you set in the [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method of your configuration subclass. The default values are used by Firebase when the remote configuration has not yet been received.

{% tabs %}
{% tab title="Swift" %}
The [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) class has a method `remoteDefaults` which provides a `Dictionary` of all the remote configuration property default values that were set in your overridden [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

> Swift example

```swift
FirebaseApp.configure()
self.remoteConfig = RemoteConfig.remoteConfig()
...
// add the following line
self.remoteConfig.setDefaults(APLConfiguration.shared().remoteDefaults())
```

{% endtab %}

{% tab title="Objective-C" %}
The [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) class has a method `remoteDefaults` which provides a `NSDictionary*` of all the remote configuration property default values that were set in your overridden [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

> Objective-C example

```objectivec
[FIRApp configure];    
self.remoteConfig = [FIRRemoteConfig remoteConfig];
...
// add the following line
[self.remoteConfig setDefaults: [[APLConfiguration sharedConfiguration] remoteDefaults]];
```

{% endtab %}
{% endtabs %}

### 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.

{% tabs %}
{% tab title="Swift" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Firebase Remote Config `activate(completion:)` blocks (inside `fetch(completionHandler:)` and optionally `addOnConfigUpdateListener(remoteConfigUpdateCompletion:`).

> Swift example

```swift
self.remoteConfig.fetch { status, error in
    if status == .success {
        self.remoteConfig.activate { changed, error in
            ...
            APLFlushRemoteConfiguration() // add this line
        }
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Firebase Remote Config `activateWithCompletion:` blocks (inside `fetchWithCompletionHandler:` and optionally `addOnConfigUpdateListener:`).

> Objective-C example

```objectivec
__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
        }];
}];
```

{% endtab %}
{% endtabs %}

### Complete initialisation example

{% tabs %}
{% tab title="Swift" %}

> Swift complete initialisation example

```swift
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
        }
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C complete initialisation example

```objectivec
[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
            }
        }];
    }
}];
```

{% endtab %}
{% endtabs %}

### Best practice and usage

{% tabs %}
{% tab title="Swift" %}
Replace all calls to Firebase Remote Config `configValue(forKey:)`:

```swift
if remoteConfig.configValue(forKey: "alwaysDarkMode").boolValue {
   ...
```

with the following:

```swift
if (CONFIGURATION().alwaysDarkMode) {
   ...
```

{% endtab %}

{% tab title="Objective-C" %}
Replace all calls to Firebase Remote Config `configValueForKey`:

```objectivec
if ([remoteConfig configValueForKey: @"alwaysDarkMode"].boolValue]) {
   ...
```

with the following:

```objectivec
if (CONFIGURATION.alwaysDarkMode) {
   ...
```

{% endtab %}
{% endtabs %}
