Firebase Remote Config
Firebase Remote Config allows changes to the behavior of apps already installed and running on user devices to be made without the need to perform an upgrade or re-installation. This chapter will explain how, by making use of Remote Config, dynamic app changes can be controlled via the Firebase console and pushed out either to all app users, or to different user segments.
An Overview of Firebase Remote Config
Prior to the introduction of Firebase Remote Config, there was no easy way to make dynamic changes to the way an app looks and functions without issuing an upgrade to existing users. There was also no way to control which users received the update, making it all but impossible to gradually roll out changes, perform A/B testing to gauge the success of the changes, or to target specific groups of users based on demographics such as device type or geographical location.
Firebase Remote Config addresses this problem by providing a simple way to make remote configuration changes to an app and then target specific groups of users to receive those changes.
As with most other Firebase features, Remote Config begins with integration of a library into the app project. Within the app, a set of parameters are then defined either in a Map object or an XML resource file. These parameters take the form of key-value pairs that are used to control the default configuration of the app. The code of the app is implemented such that these parameters are checked at predefined intervals and used in the logic of the running app. An in-app parameter could, for example, be declared with a default setting to configure a blue background or to display a particular welcome message.
These default in-app parameters may then be overridden on a per-parameter basis using a set of matching server-side parameters, the values of which are controlled from within the Firebase console. The decision as to whether or not a server-side parameter overrides the corresponding in-app parameter may also be made conditionally based on factors such as user location, device operating system and the user’s language and locale settings. Parameters may also be targeted to randomized percentages of the user base, or in conjunction with audiences defined using Firebase Analytics.
Once a server-side parameter has been configured, it is published and ready to be fetched by the app. Once fetched, the parameters must then be activated before they can be used to override the corresponding in-app parameters.
The FirebaseRemoteConfig Object
The main object responsible for providing Remote Config support within an Android app is the FirebaseRemoteConfig object. The first step in implementing Remote Config support within an app is to obtain a reference to this object as follows:
FirebaseRemoteConfig fbRemoteConfig = FirebaseRemoteConfig.getInstance();
The FirebaseRemoteConfig object is responsible for storing the local in-app parameters, fetching the server-side parameters and controlling when those parameters are activated for use within the app.
Declaring and Setting In-App Parameters
The default in-app parameters may be declared within an XML resources bundled with the app, or contained within a Java Map object. When using an XML resources, the file must be located within the res/xml folder of the app. The listing below contains a basic Remote Config parameter file containing background color and welcome message values:
<?xml version="1.0" encoding="utf-8"?> <defaultsMap> <entry> <key>welcome_text</key> <value>Welcome to Remote Config</value> </entry> <entry> <key>main_background_color</key> <value>#42f486</value> </entry> </defaultsMap>
Once the in-app parameters have been declared, they need to be loaded into the FirebaseRemoteConfig object. This is achieved by passing the reference to the resource file to the setDefaults() method of the FirebaseRemoteConfig object. Assuming the above XML resource file was named remote_config_params.xml, the code to set the defaults would read as follows:
firebaseRemoteConfig.setDefaults(R.xml.remote_config_params);
As an alternative to using an XML resource file, the same result can be achieved using a HashMap. For example:
HashMap<String, Object> config_params = new HashMap<>(); config_params.put("welcome_text", "Welcome to Remote Config"); config_params.put("main_background_color", "#42f486"); fbRemoteConfig.setDefaults(config_params);
Accessing Remote Config Parameters
Once the in-app parameters have been applied to the FirebaseRemoteConfig object, the code will need to be able to access the values in order to make use of them in configuring the app’s appearance and behavior. This is achieved by calling one of a number of get methods on the FirebaseRemoteConfig object passing through as an argument the key for which the corresponding value is required. The correct get method to call will depend on the type of the value associated with the parameter. The following is the current list of available methods:
• getBoolean()
• getByteArray()
• getDouble()
• getLong()
• getString()
The main_background_color parameter contained within the example resource file would, for example, be obtained from the FirebaseRemoteConfig object as follows and used to set the background color of a layout accordingly:
String bg_color = fbRemoteConfig.getString("main_background_color"); layout.setBackgroundColor(Color.parseColor(bg_color));
Setting Server Side Parameters
Once the app is set up to make use of Remote Config parameters, the next step is to learn how to override the in-app parameters with remote server side parameters. As outlined earlier in the chapter, server-side parameters are declared using the Firebase console. To configure these parameters, open the Firebase console in a browser window, select the appropriate Firebase project and click on the Remote Config link in the left-hand navigation panel to display the screen shown in Figure 36‑1:
[[File:]]
Figure 36‑1
Clicking on the Add Your First Parameter button will display a popup dialog within which the key and value for the parameter may be entered. When adding server-side parameters, the key must match that of the in-app parameter being overridden. In Figure 36‑2, a server-side parameter for the background color parameter is being added:
[[File:]]
Figure 36‑2
Once the parameter has been added, it will appear in the parameter list where it may be edited by clicking the button highlighted in Figure 36‑3. Although the parameter has been added, it is not available to be fetched by the app until the Publish Changes button (also highlighted) at the top of the list is clicked:
[[File:]]
Figure 36‑3
Additional parameters may be added using the Add Parameters button.
Fetching the Parameters
Although server-side parameters may have been declared and published, nothing will change on the client app unless steps are taken to fetch and then activate the parameters. The fetching operation is initiated via a call to the fetch() method of the FirebaseRemoteConfig object.
When the parameters are fetched, they are stored within the cache of the FirebaseRemoteConfig object. By default, the cache will consider these parameters to be valid for 12 hours after being fetched. Any subsequent fetch calls within that time period will simply continue to use the cached parameters. After 12 hours have expired, the next fetch method call will once again download the parameters from the server. The following code demonstrates the use of the fetch() method with the default 12 hour expiration:
fbRemoteConfig.fetch();
A shorter cache expiration duration may specified by passing through the number of seconds as an argument to the fetch() method. In the following line of code, the cache is set to expire after one hour:
fbRemoteConfig.fetch(3600);
In practice, the expiration time should not be set too low. Remote Config fetch requests are subject to throttling by the Firebase server. In the event that an app makes too many fetch requests, future requests will be denied until a timeout period has elapsed.
When testing Remote Config during development, it makes sense to set the cache timeout to zero so that configuration changes can be tested instantly. To avoid encountering throttling during testing, the setConfigSettings() method of the FirebaseRemoteConfig object should be used to enable developer mode:
fbRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder() .setDeveloperModeEnabled(true) .build()); fbRemoteConfig.fetch(0);
This developer mode must be disabled before the app is distributed to users.
The fetch operation is performed asynchronously so a completion listener should be added to receive notification that the fetch completed:
fbRemoteConfig.fetch(0).addOnCompleteListener(this, new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { // Fetch succeeded } else { // Fetch failed } } });
Activating Fetched Parameters
Once a successful fetch operation has been performed the parameters must be activated before they will be accessible via the various get methods. To activate the most recently fetched parameters, a call needs to be made to the activateFetched() method of the FirebaseRemoteConfig object. Since it only makes sense to activate the parameters when a successful fetch has completed, this call is best made within the onComplete() method of the completion listener:
. . public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { fbRemoteConfig.activateFetched(); } else { // Fetch failed } } . .
Working with Conditions
The Remote Config section of the Firebase console allows the conditions under which a server-side parameter will be passed to the client during a fetch request to be specified. Conditions are defined using rules which are selected either when adding or editing a parameter within the console. When adding a parameter, conditions are declared by clicking on the Add value for condition menu as highlighted in Figure 36‑4:
[[File:]]
Figure 36‑4
The menu will provide the option to create a new condition or to select a previously added condition. The condition definition dialog (Figure 36‑5) requires that the condition be given a name and assigned a color by which to identify the condition within the Remote Config Conditions screen.
[[File:]]
Figure 36‑5
Clicking on the Select… menu in the Applies if… section of the dialog provides the following list of options from which to begin building the rule:
• App ID – The Android package name for the app. • App version – The value assigned to the android:versionName property in the app’s manifest file. • OS type – The operating system running on the device (iOS or Android). • User in random percentile – Targets a random sampling of users up to a specified percentage of the overall user base. This option is particularly useful for implementing A/B testing to judge how users react to different app behavior or appearance.
• User in audience – Allows for the selection of user groups defined using the Analytics features of Firebase. • Device in country/region – Users in one or more specific geographic regions. • Device language – The locale and language configured on the user’s device. • User property – Targets users based on user properties configured using Firebase Analytics.
Once a selection has been made, the corresponding criteria must be selected. If the random percentile option is selected, for example, an operator and percentage value will need to be selected as shown in Figure 36‑6:
[[File:]]
Figure 36‑6
Similarly, the device region requires that one or more countries or territories be selected:
[[File:]]
Figure 36‑7
Once a condition has been defined, the AND button may be used to add additional rules to the condition, all of which must resolve to be true in order for the condition to the met.
Once the condition is complete, clicking on the Create Condition button returns to the parameter creation dialog where a parameter value must be entered to be used when the condition rules are met. In Figure 36‑8, for example, a different background color is defined for users matching a condition named Western Europe:
[[File:]]
Figure 36‑8
Note that the above dialog contains a field titled Default value. While the condition in the above example dictates the background color that will be used for users in western Europe, the default setting defines the background color that will be provided for the remaining users. If no value is to be provided for other users (in other words the app will use the in-app parameter in other geographic regions) this field can be left as an empty value.
If conditions overlap to the extent that more than one condition might be true under certain circumstances, the system needs to know which should take priority. Condition priorities may be altered by selecting the Conditions tab within the Remote Config screen of the Firebase console. This will list all of the conditions currently configured for the Firebase project in the order in which they will be evaluated. Once a condition in the list evaluates to true no other conditions beneath that point in the list will be evaluated. To change the position of a condition in the evaluation order, simply click on the handle and drag the item to the appropriate position in the list:
[[File:]]
Figure 36‑9
Once the conditions are configured, the Publish Changes button must be clicked before the new settings take effect.
Parameter Fetching Strategies
The timing and frequency of fetch requests is largely at the discretion of the app. The app controls the expiration duration for the cache and when calls are made to the fetch() method. That being said, there are some recommended guidelines that should be followed when using Remote Config.
In general, performing significant configuration changes while the user is interacting with the app should be avoided. If such changes are necessary, they should be made the next time the user interface for an activity loads, rather than dynamically while the user is interacting with the screen.
Whenever possible, updated configuration parameters should ideally be activated the next time the user launches the app. When taking this approach, however, the user should not be made to wait for the fetch process to complete before being able to begin using the app. To avoid delaying the start of the app while the fetch operation completes, Google recommends fetching new parameters while the app is running, but not activating them until the user next launches the app. This way the new parameters are already downloaded and ready to be used instantly when the app next starts. This technique will be demonstrated in the tutorial contained in the next chapter.
To allow for the possibility that certain configuration changes may need to take effect immediately, also consider using Firebase Notifications to trigger a fetch operation within the app using a zero caching expiration value. Care should be taken when using this approach for large user bases to avoid throttling when all running app instances attempt to fetch parameters simultaneously.
Getting Status Information
Status information is available from the FirebaseRemoteConfig object via a call to the getInfo() method. This method returns a FirebasebaseRemoteConfigInfo object on which the getLastFetchStatus() method may be called. A call to this method will return one of the following status values:
• LAST_FETCH_STATUS_NO_FETCH_YET – A fetch attempt has yet to be made by the app. • LAST_FETCH_STATUS_SUCCESS – The last fetch was performed successfully. • LAST_FETCH_STATUS_FAILURE – The last fetch failed. • LAST_FETCH_STATUS_THROTTLED – The last fetch attempt was throttled indicating that the app has made too many fetch requests.
Code to check for throttling might, for example, read as follows:
FirebaseRemoteConfigInfo info = fbRemoteConfig.getInfo(); int status = info.getLastFetchStatus()); if (status == FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED) { // Last fetch was throttled }
In addition to the getInfo() method, the timestamp of the last successful fetch (in milliseconds since January 1st, 1970) is available via a call to the getFetchTimeMillis() method.
Access to the current settings of the FirebaseRemoteConfig object is available via the getConfigSettings() method which returns a FirebaseRemoteConfigSettings object. The only method currently provided by this object is the isDeveloperModeEnabled() method which, as the name suggests, returns a Boolean value indicating whether developer mode is enabled.
Summary
Firebase Remote Config provides a way to make configuration changes to an app after it has been installed on user devices without the need to issue upgrades. The service is based on the concept of sets of parameters in the form of key-value pairs that are used internally by the app logic to control the look and behavior of the app. Local parameters provide the default values for the app which are then overridden on a per-parameter basis from within the Firebase console. Conditions provide control over which groups of users receive modified parameters based on criteria such as geographical location, app version, language or device operating system.