jsonfirebasefirebase-remote-config

Adding new values to Remote Config JSON


I have a single JSON stored in Remote Config that contains much of my app text. If I add a key-value pair to the remote and default JSON object and then try to reference that value from the client, the client will attempt to access the value in the old cached JSON and return null — even if the value is stored in the new default JSON hard-coded in the app client.

How can I rely on the updated default value if the updated remote JSON has not been cached yet? Other suggestions are also appreciated.


Solution

  • I created a singleton that stores my default app text json as well as the remote json.

    In main(), I call AppTextSingleton(); and then run await AppTextSingleton.singleton.initialize(); after initializing RemoteConfig. I do this all before runApp gets called.

    The singleton's appText contains all cached, remote values as well as any default values that are not in the cached, remote json. This way, if the app is updated to a new version that calls a new key-value pair from the JSON, but the client is still working with an old cached version without the key-value pair, the value in the default will be used.

    import 'dart:convert';
    import 'package:flutter/services.dart';
    import '../constants.dart';
    
    class AppTextSingleton {
      Map<String, dynamic> remoteAppText =
          json.decode(remoteConfig.getString("app_text"));
      Map<String, dynamic> defaultAppText = {};
      Map<String, dynamic> appText =
          json.decode(remoteConfig.getString("app_text"));
    
      factory AppTextSingleton() => singleton;
      static final AppTextSingleton singleton = AppTextSingleton._internal();
      AppTextSingleton._internal();
    
      Future<void> initialize() async {
        await setDefaultAppText();
        mergeTexts();
      }
    
      Future<void> setDefaultAppText() async {
        defaultAppText = await json
            .decode(await rootBundle.loadString('assets/default_text.json'));
      }
    
      void mergeTexts() {
          appText = mergeMap([defaultAppText, remoteAppText], acceptNull: true);
      }
    }
    

    mergeMap is a modified version of the [belatuk_merge_map 4.0.0] 1 package. Just copied the single dart file in the package to a local project and modified the _copyValues function to only add Maps of type <String, dynamic>. Current package seems to add <dynamic, dynamic> which does not suit the needs of RemoteConfig.

    Modified package:

    dynamic _copyValues<K, V>(Map<String, dynamic> from, Map<String, dynamic> to,
        bool recursive, bool acceptNull) {
      for (var key in from.keys) {
        if (from[key] is Map<String, dynamic> && recursive) {
          if (to[key] is! Map<String, dynamic>) {
            to[key] = <String, dynamic>{};
          }
          _copyValues(from[key] as Map<String, dynamic>,
              to[key] as Map<String, dynamic>, recursive, acceptNull);
        } else {
          if (from[key] != null || acceptNull) {
            to[key] = from[key];
          }
        }
      }
    }
    
    Map<String, dynamic> mergeMap<K, V>(Iterable<Map<String, dynamic>> maps,
        {bool recursive = true, bool acceptNull = false}) {
      var result = <String, dynamic>{};
      for (var map in maps) {
        _copyValues(map, result, recursive, acceptNull);
      }
      return result;
    }