【Flutter】リアルタイムRemoteConfigで画面内容を変更する
課題
アプリは一度リリースしてしまうともしバグが発生してしまった場合にロールバックするのが難しいです。
いくらRevertして急ぎでAppleに申請しても、レビューが完了するのは次の日(申請を出した日の深夜とか)になってしまいます。(PlayStoreも1~3時間ぐらいかかるイメージです)
また、すでにインストールをしてしまったユーザーに対しても強制的に最新のアプリ利用を呼びかける必要があると思います。
FirebaseのRemoteConfigで外部的にフラグを定義して、新しく実装した機能をロールバックできるようにできないかなと前々から思っていましたがアプリからRemoteConfigのサーバーに対してリクエストできる回数は1時間に5回という制限がありました。
また、基本的にはRemoteConfigでサーバーにあるデータを取得するタイミングはアプリ起動時が多く、利用中のユーザーに対してのサポートは難しい状況でした。
なにをしたいか
RemoteConfigにもう少しリアルタイム性があればなと思っていたところ、どうやらリアルタイムRemoteConfigなるものがあるということでどんなものなんだろうと触ってみました。
何ができればOKか
- アプリを一度起動&タスクから削除。FetchInterval期間中にRemoteConfigの値を変更し、次回起動時に最新の値を取得したい
- アプリを起動中。RemoteConfigの値を変更し、リアルタイムに値が反映されるか
と、いうわけでやってみました
作ってみたもの
「画面にラベルが一つ表示されているだけのアプリ」を作成しました。
画面上の「default local label」の文字列をRemoteConfig上から取得し、反映するようにしてみます。
環境
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.13.2, on Microsoft Windows [Version 10.0.22621.2134], locale ja-JP)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.3.4)
[√] Android Studio (version 2022.3)
[√] VS Code (version 1.81.1)
[√] Connected device (4 available)
[√] Network resources
No Issues found!
コード
RemoteConfigの設定で次回設定取得までの間隔と通信が発生した場合のタイムアウトまでの期間を設定できますが、今回は特に設定していません。
なので、次回設定取得までの間隔は12時間、タイムアウトまでの期間は1分になっています。
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:real_time_remote_config/remote_config_state.dart';
const _defaultSettings = {
"top_page_label": "default local label",
};
final remoteConfigProvider =
StateNotifierProvider<RemoteConfigNotifier, RemoteConfigState>(
(_) => RemoteConfigNotifier(),
);
class RemoteConfigNotifier extends StateNotifier<RemoteConfigState> {
RemoteConfigNotifier() : super(RemoteConfigState.initialize()) {
_initialize();
}
Future<void> _initialize() async {
final remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setDefaults(_defaultSettings);
state = RemoteConfigState(state: remoteConfig.getAll());
remoteConfig.onConfigUpdated.listen((_) async {
final result = await remoteConfig.activate();
if (result) {
state = RemoteConfigState(state: remoteConfig.getAll());
}
});
final result = await remoteConfig.fetchAndActivate();
if (result) {
state = RemoteConfigState(state: remoteConfig.getAll());
}
}
}
コードは色々ありますが、肝となる部分としては以下の部分でonConfigUpdatedを購読することでリアルタイムに値を反映できるようです。
remoteConfig.onConfigUpdated.listen((_) async {
final result = await remoteConfig.activate();
if (result) {
state = RemoteConfigState(state: remoteConfig.getAll());
}
});
今回はRemoteConfig全体のパラメータをStateとして保持するStateProviderを使ってそのStateをwatchしてリアルタイムで更新できるかを確認していきます。
画面では以下のようにremoteConfigProvider
を参照しています。
Consumer(
builder: (_, ref, __) {
final remoteConfigData = ref.watch(remoteConfigProvider);
return Text(
remoteConfigData.valueAsString('top_page_label'),
style: const TextStyle(fontSize: 24),
);
},
)
アプリが端末に入っていなくインストール直後の起動では、アプリ内にキャッシュのデータがない為に必ず通信が発生します。
その後、アプリを一度タスクから削除しもう一度起動すると通信は発生しませんでした。
次回の設定取得まではデフォルトの12時間で設定されているため、本来であればRemoteConfigのコンソール上で値を変更しても反映されるのは12時間後になります。
一度アプリをタスクから削除します。
その間にRemoteConfigのコンソール画面で任意の文字に変更します。
その後アプリを起動すると、一時的に前の文字列が表示されますが変更した文字列に切り替わりました。
アプリ起動時に通信をしているか確認してみましたが、起動時には通信をしてなかったです。
onConfigUpdated
が呼ばれる前に通信をしているようでした。
(こちらの記載どおりにonConfigUpdated
ではactivate
のみしているので、fetchは内部で自動に行われているようでした。)
次にアプリを起動中に値を変更してみます。
体感で変更後の2秒更新されていました。
と、いうわけでリアルタイムRemoteConfigを使ってみたところ前述していた以下の要件はクリアしました。
何ができればOKか
- アプリを一度起動&タスクから削除。FetchInterval期間中にRemoteConfigの値を変更し、次回起動時に最新の値を取得したい
- アプリを起動中。RemoteConfigの値を変更し、リアルタイムに値が反映されるか
そのため、もし新規の機能で不具合が見つかったときのロールバックや、強制バージョンアップをユーザーに促したい場合などにも利用できそうと思いました。
Discussion