Utilizing Firebase Remote Config for Remote App Configuration
— NameRover, Firebase — 4 min read
Firebase Remote Config is a powerful tool that allows developers to dynamically change the behavior and appearance of their apps without requiring users to download an update from the app store. You can remotely adjust various app settings, feature flags, and other configuration values, ensuring your app remains adaptable and reactive to changing requirements.
How Firebase Remote Config Works
Firebase Remote Config fetches configuration values from the Firebase server and applies them to the app. These values can be of various types, such as numbers, booleans, strings, and JSON data. The fetched values can then control different aspects of the app, such as turning features on or off, changing UI elements, or setting thresholds for certain operations.
Example Usage
Here is a trimmed down example of how Firebase Remote Config is used in NameRover:
@lazySingletonclass RemoteConfigRepository { RemoteConfigRepository(IRemoteConfigClient remoteConfigClient) : _remoteConfigClient = remoteConfigClient;
final IRemoteConfigClient _remoteConfigClient;
final _parameters = <RemoteConfigParameter<dynamic>>[];
List<RemoteConfigParameter<dynamic>> get parameters => _parameters;
Future<void> initAndFetch() async { await _remoteConfigClient.init(); await _remoteConfigClient.fetch(); }
/// ...
Future<T> getValue<T>(String key) async { final parameter = _parameters.firstWhere( (element) => element.key == key, orElse: () => throw RemoteConfigRepositoryException('Parameter $key not found'), );
return await _remoteConfigClient.getValue<T>( parameter.key, defaultValue: parameter.defaultValue as T, ); }}
I created a RemoteConfigRepository
class that uses an IRemoteConfigClient
to interact with an abstract Remote Config client.
This client is injected at build time and can be implemented with any remote config SDK, like Firebase, ConfigCat, or others.
The RemoteConfigRepository
class provides methods to initialize and fetch the remote config values and to get specific values by key.
Special Case: UpdateController
In the Namerover project, the UpdateController
class is a case that utilizes the RemoteConfigRepository
to check for updates and new builds.
This controller ensures that users are always on the latest version of the app, providing a seamless and secure experience.
The UpdateController
fetches the latest and minimal build numbers from the remote config.
class UpdateController { UpdateController({ required RemoteConfigRepository remoteConfigRepository, }) : _remoteConfigRepository = remoteConfigRepository { if (Platform.isAndroid) { minimalBuildKey = 'minimal_android_build'; latestBuildKey = 'latest_android_build'; } else { minimalBuildKey = 'minimal_ios_build'; latestBuildKey = 'latest_ios_build'; } }
final RemoteConfigRepository _remoteConfigRepository;
late String minimalBuildKey; late String latestBuildKey;
int? latestRemoteBuild; int? minimalRemoteBuild;
int currentBuild = int.tryParse(kAppBuildNumber) ?? 9999999;
Future<void> check() async { latestRemoteBuild = await _remoteConfigRepository.getValue<int>(latestBuildKey); minimalRemoteBuild = await _remoteConfigRepository.getValue<int>(minimalBuildKey); }
bool get isUpdateAvailable { if (latestRemoteBuild != null && latestRemoteBuild! > 0 && minimalRemoteBuild != null && minimalRemoteBuild! > 0) { return currentBuild < latestRemoteBuild! || currentBuild < minimalRemoteBuild!; }
return false; }
bool get isUpdateRequired => minimalRemoteBuild != null && minimalRemoteBuild! > 0 && currentBuild < minimalRemoteBuild!;}
How It Works?
- Initialization: The
UpdateController
is initialized with aRemoteConfigRepository
. It sets the keys for fetching the minimal and latest build numbers based on the platform (Android or iOS). - Fetching Build Numbers: The check method fetches the latest and minimal build numbers.
- Checking for Updates: The
isUpdateAvailable
getter checks if the current build is less than the latest or minimal remote build, indicating an update is available. TheisUpdateRequired
getter checks if the current build is less than the minimal remote build, indicating that an update is required.
At app initialization, the system is set to check for updates periodically. When an update is detected, an update dialog is displayed to the user:
Improvements
We can improve the system by adding optional messages to the build number that can be displayed to the user or by restricting the update based on the platform OS version or SDK.
This can be done by storing JSON data in the remote config repository and then parsing it in the UpdateController.
e.g.
{ "minimal_android_build": 100, "latest_android_build": 200, "minimal_ios_build": 100, "latest_ios_build": 200, "update_message": { "minimal": "Please update to the latest version. This is a mandatory update.", "latest": "Critical bug fixes" }}
NameRover does not auto-update itself. Instead, it prompts users to update the app when a new version is available. The button opens the app store page, where the user can update the app manually.
Auto-updating functionality can be added by integrating an Flutter package in_app_update, but it will not be considered for the current version of NameRover.