こんにちは。Safieでモバイルアプリの開発をしている渡部です。
モバイル版Safie Viewerでは、バージョン3.11.0から、新しいバージョンがリリースされるとユーザーに向けてアップデートを知らせるアラートが表示されるようになっています。
今回は、このアップデート機能の実装について振り返ってまとめてみました。
実装の経緯
モバイル版Safie Viewerでは現在約1ヶ月おきに定期リリースを行っています。以前は開発サイクルがもう少し長期間だったのですが、チーム体制が充実したこともありコンスタントに新しいバージョンを用意することができるようになりました。
それに伴い、ユーザーに新バージョンをアピールする必要性もより高まりました。新機能を実装したり、バグを修正するスピードが早まったものの、ユーザーに気づいてもらえない限りは意味がありません。
より安定したバージョンを使ってもらうためにも、アップデート機能を実装することになりました。
必要な要件
実装にあたって、まずはモバイルアプリのアップデート機能で求める要件を洗い出しました。
立ち上げたアプリが対象バージョンより低い場合、アップデートのお知らせをアラートで表示したい
対象バージョンとは必ずしも最新版ではなく、任意のバージョンを指定したい。強制と半強制を使い分けたい
アプリを立ち上げてアップデートについてアラートが表示された際に、アップデートしないと先に進めないパターン(強制)と、アップデートせずともアプリを使い続けられるパターン(半強制)の2つを、バージョンによって切り替えたい。
深刻なバグ修正の場合は強制で、それ以外の場合は半強制でお知らせしたい。その他、いろいろな設定値を持ちたい
アラート表示を開始/終了する日時、アラート表示の対象となるOSバージョンなど。iOS版・Android版の設定を一括で管理したい
手軽に更新したい
サービス選定
アップデート機能を実現するための仕組みはすでに色々なサービスで提供されています。
例えば、
①iTunes Search APIを使用する(iOSのみ)
②Google Play Core Libraryを使用する(Androidのみ)
③アップデート機能用の3rdパーティライブラリを使用する
④バックエンドサービスにアップデートの設定ファイルを置き、アプリ側で読み込む
など。
①や②は公式のサービスですが、カスタム性は乏しいです。③もメンテナンスのことを考えるとできれば避けたくなります。
ということで、今回は、前述した要件を満たすために④の方法で、サービスはFirebase Remote Configを活用することにしました。
④を実現するだけなら、その他のBaaSを活用するやり方もありますが、
すでにFirebaseを導入済みだったこと
コンソール上のUIのみで複数ファイルを更新できる手軽さ
以上を加味して、 Remote Configを選びました。
実装
Firebaseプロジェクトの作成については、今回は割愛します。
RemoteConfigの設定
要件をふまえて、コンソールで以下のようなjsonを設定しました。
{ "parameters": { "ios_update": { "defaultValue": { "value": { "version":"3.19.0", "is_force":true, "start_date":"2023-02-28", "support_os_version":"13.0" } }, "valueType": "JSON" }, "android_update": { ... } } }
version: 対象バージョン。この数値より低いバージョンのアプリを起動したら、アップデート案内のアラートを表示する。
is_force: 強制か半強制か
start_date:アラート表示開始日
end_date: アラート表示終了日
support_os_version:アプリのサポート対象OS
アプリ側の設定
- プロジェクトにRemote ConfigのSDKを追加
iOS
CocoaPodsを使用しているので、Podfileに以下を追加。
pod 'Firebase/RemoteConfig'
Android
build.gradleに以下を追加。
implementation 'com.google.firebase:firebase-config-ktx:21.4.0'
2. アップデートのアラートを表示したいタイミングで、RemoteConfigからjsonを取得するように実装。
iOS
アプリを起動して最初に立ち上がるViewControllerにて
override func viewWillAppear(_ animated: Bool) { // ここで取得 }
通知をタップして任意の画面を開いた時
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // ここで取得 }
フォアグラウンドからの復帰時
func applicationWillEnterForeground(_ application: UIApplication) { // ここで取得 }
取得の流れ
// シングルトンオブジェクトを作成 remoteConfig = RemoteConfig.remoteConfig() // RemoteConfigから値を取得 remoteConfig.fetch() { (status, error) -> Void in remoteConfig.activate { changed, error in // RemoteConfigStruct:RemoteConfigで設定したjsonと対応するStruct let response = try? JSONDecoder().decode(RemoteConfigStruct.self, from: remoteConfig[“ios_update”].dataValue) } }
Android
起動時、フォアグラウンド復帰時にて
class MainApp : Application() { override fun onCreate() { ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { // ここで取得 } }) } }
取得の流れ
// シングルトンオブジェクトを作成 val remoteConfig = Firebase.remoteConfig // RemoteConfigから値を取得 remoteConfig.fetchAndActivate().addOnCompleteListener { task -> if(task.isSuccessful) { val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create() // RemoteConfigData:RemoteConfigで設定しているjsonと対応するDataクラス val remoteConfigData= gson.fromJson(remoteConfig.getString(”android_update”), RemoteConfigData::class.java) } }
3. 取得した設定値(remoteConfigData)によって、アラートの表示/非表示、強制/半強制を出し分けるように実装。
効果
強制アップデートの仕組みを実装してから、ユーザーは以前よりも速やかに最新版に移行してくれるようになったのか、Analyticsから確認してみました。
いずれも、新バージョンをリリースしてから1ヶ月間のユーザー推移を表しています。
強制アップデート実装から時間が経過するにつれ、旧バージョン使用数の減退が早まっています。旧バージョンの使用割合も減りました。
今まで以上に多くのユーザーがより早く新バージョンを使用することに繋がり、アプリの安定化に繋がったかなと思います🙌