sunagimoブログ

主にUnityに関する技術を取り上げます

Firebase Remote Config【Unity】

Firebase Remote Configとは

予め設定したパラメータの値をRemoteで変更できるサービス。
ダウンロードが不要なので、ユーザーに素早く変更を通知できる。

firebase.google.com



使用例

・アプリのプラットフォームで提供する機能を分ける
・言語、国ごとに提供する機能を分ける
・指定したバージョンごとで機能を分ける
・指定した時間の間だけ機能を限定公開
・指定したパーセント値のユーザーのみに機能を限定公開
など

カスタマイズ可能
最大500の条件設定可能


使用できるパラメータの値

  • bool(true, false)
  • IEnumerable(json
  • double(0.05などの小数点)
  • long(整数)
  • source(この値のソースを示す、Default値やRemote値)
  • string(文字列)

パラメータ文字列は800000文字列以内
最大2000個のパラメーター


firebase.google.com



ポリシー

  • Remote Configを使ってユーザーの承認を必要とする機能を作成しない
  • 機密データをパラメータ値に格納しない
  • パラメータを使ってプラットフォームの要件を回避しようとしない


Remote Config設定の流れ(導入)

以下の導入方法に関してはスキップ

  • Firebaseプロジェクトの作成
  • Firebase Remote Config SDKの導入


プログラムは以下を参考
github.com



1.Firebase Remote Configコンソールでパラメータの設定

f:id:sunagimo_app:20190924002901p:plain


例:
プラットフォームごとに強制アップデートバージョンを変更したい

キー:force_update_version
条件1:android → "1.5.0"
条件2:iOS → "1.5.1"
デフォルト値:"1.5.0"

※キーの長さは256文字でアルファベットや数字を使用可能
※複数の条件でtrueとなった場合はコンソールで設定したUIの一番上が優先される
※条件に満たない場合はデフォルト値が使用される


パラメータキーとデフォルト値を入力
f:id:sunagimo_app:20190924003919p:plain


条件の値を追加を選択し、
androidプラットフォームの条件、iOSプラットフォームの条件をそれぞれ作成
f:id:sunagimo_app:20190924004242p:plain
f:id:sunagimo_app:20190924004251p:plain


条件作成後、条件に合わせたパラメータを入力
f:id:sunagimo_app:20190924004319p:plain


「変更を公開」を選択することで公開される
f:id:sunagimo_app:20190924004423p:plain


また、Conditionsタブで今まで作成した条件を確認可能
他のキーで条件の使い回しも可能
f:id:sunagimo_app:20190924004624p:plain



2.プログラム上でDefault値の設定

コンソール上で設定したキーのデフォルト値を設定する。
コンソール上で設定されていないキーも設定可能。

System.Collections.Generic.Dictionary defaults =
  new System.Collections.Generic.Dictionary();

defaults.Add("force_update_version", "1.5.0");

Firebase.RemoteConfig.FirebaseRemoteConfig.SetDefaults(defaults);



3.パラメータをフェッチ
Firebase Remote Configコンソールで設定した値を同期
TimeSpan.Zeroでキャッシュ時間を0に設定


Firebase.RemoteConfig.FirebaseRemoteConfig.ActivateFetched();
でようやく同期が完了。

System.Threading.Tasks.Task fetchTask = 
Firebase.RemoteConfig.FirebaseRemoteConfig.FetchAsync(TimeSpan.Zero);

      fetchTask.ContinueWithOnMainThread(() => {
        if (fetchTask.IsCanceled) {
          DebugLog("Fetch canceled.");
        } else if (fetchTask.IsFaulted) {
          DebugLog("Fetch encountered an error.");
        } else if (fetchTask.IsCompleted) {
          DebugLog("Fetch completed successfully!");
        }

        var info = Firebase.RemoteConfig.FirebaseRemoteConfig.Info;
        switch (info.LastFetchStatus) {
          case Firebase.RemoteConfig.LastFetchStatus.Success:
            Firebase.RemoteConfig.FirebaseRemoteConfig.ActivateFetched();
            DebugLog(String.Format("Remote data loaded and ready (last fetch time {0}).",
                                  info.FetchTime));
            break;
          case Firebase.RemoteConfig.LastFetchStatus.Failure:
            switch (info.LastFetchFailureReason) {
              case Firebase.RemoteConfig.FetchFailureReason.Error:
                DebugLog("Fetch failed for unknown reason");
                break;
              case Firebase.RemoteConfig.FetchFailureReason.Throttled:
                DebugLog("Fetch throttled until " + info.ThrottledEndTime);
                break;
            }
            break;
          case Firebase.RemoteConfig.LastFetchStatus.Pending:
            DebugLog("Latest Fetch call still pending.");
            break;
          }
      });



3.パラメータを取得
同期が完了している場合はConsoleで設定した条件にあった値
同期に失敗している場合はDefaultで設定した値が返ってくる

Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("force_update_version").StringValue);


4.パラメータの更新

1.Remote Configコンソール上で更新
2.フェッチ
3.値の取得(更新された値)




パラメータの取得方法


Boolean(真偽値)

(コンソール)
f:id:sunagimo_app:20190925023431p:plain

(プログラム)

Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("sample_boolean").BooleanValue);




String(文字列)

(コンソール)
f:id:sunagimo_app:20190925023933p:plain

(プログラム)

Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("sample_string").StringValue);




Double(小数)

(コンソール)
f:id:sunagimo_app:20190925023947p:plain

(プログラム)

Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("sample_double").DoubleValue);




Long(整数)

(コンソール)
f:id:sunagimo_app:20190925024001p:plain

(プログラム)

Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("sample_long").LongValue);





ByteArray(IEnumerable)

(コンソール)
f:id:sunagimo_app:20190925024414p:plain

(プログラム)
結果としてはIEnumerable< byte >が返ってくるので、
一度byte[]に変換して、MemoryStreamでStringで読み込んで、JsonUtilityで変換する。

using System.Linq;

public class Samle
{

   [Serializable]
  public class SampleParameter
  {
    public string version;
    public int id;
  }

  public void Read()
  {
      var sample_byte_array = Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("sample_byte_array").ByteArrayValue;
      byte[] sampleByteArray = sample_byte_array.ToArray();
      var jsonData = string.Empty;
      using(var stream = new System.IO.MemoryStream(sampleByteArray, false))
      {
        using(var reader = new System.IO.StreamReader(stream, System.Text.Encoding.UTF8))
        {
          jsonData = reader.ReadToEnd();
        }
      }
      var sampleParameter = JsonUtility.FromJson<SampleParameter>(jsonData);
      Debug.Log(sampleParameter.version);
      Debug.Log(sampleParameter.id);
  }
  
}




運用上の注意


フェッチ後の値
フェッチを行ったあとの値は、端末上に保存されるのでタスクキルなどをしても
Remote Configコンソール上の値で取得される


キャッシュ問題
フェッチを行っても値が同期されない場合があるらしい?

一応、私の方で試してみましたがリアルタイムで同期されました。
最新のUnity SDKのバージョンだと問題ない可能性?



【対策】
・開発中のみだが頻繁の更新に対応できるように、
Developerモードを導入する。

var settings = Firebase.RemoteConfig.FirebaseRemoteConfig.Settings;
settings.IsDeveloperMode = true;
Firebase.RemoteConfig.FirebaseRemoteConfig.Settings = settings;


Androidの場合はfetch回数の制限があるらしいので、
頻繁に更新をしない運用にする
qiita.com



まとめ

・データをダウンロードせずに値の変更が容易に可能
データ形式も豊富
・特にJSON形式で送れるのが素晴らしい
・非同期で同期を行うためパフォーマンスに影響がない
・キャッシュ問題があるため運用に気をつける
・大規模開発の場合はAPIレスポンスで機能を十分補えるのであまり向いていない
が、サーバーレスで実装したい場合にはとても役立つ