12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Firebase Remote Configで強制アップデート

Last updated at Posted at 2022-01-13

FirebaseのRemote Configを使用して強制アップデートを実装したので記事にします。

強制アップデートとは

アプリの新しいバージョンをリリースした時に、ユーザに必ずアップデートをして欲しい場合があります。
例えば、以下のような場合です。

  • 既存の機能に大幅な変更が加わった場合
  • 致命的なバグを修正した場合

この時、ユーザがアプリを立ち上げた際、ユーザにアップデートを促しアップデートを完了させるまでアプリの使用を事実上不可能にする処理が強制アップデートです。

アプリの使用を不可能にする処理としては、以下のようなアラートを出し、アプリストアへの遷移以外できなくすることなどが挙げられます。(ストアからアップデートせずに戻ってきても再度表示される)

スクリーンショット 2022-01-12 20.12.39.png

また、強制アップデートは最初に公開した時に入れとかないと強制アップデートさせたくてもさせることができない場面が出てくるので、できるだけ最初のバージョンで入れておきたい機能です。

Firebase Remote Configとは

Firebase Remote Configはアプリの動作や外見を変更するパラメータをFirebaseに持たせることができるクラウドサービスです。
アプリが通信処理を行いこのパラメータにアクセスすることにより、アプリ自身を変更することなく動作や外見を変更することができます。

今回の強制アップデートの場合、アプリの最新のバージョン情報などをFirebase Remote Configに設定しておきます。

実装

ここからは実際にどのような手順で実装するのか見ていきます。

Firebase側

まずFirebaseのコンソールから左下、「エンゲージメント」の中にある「Remote Config」を選びます。

スクリーンショット 2022-01-12 20.45.38.png

初めての場合はRemote Configのトップページが表示されます。「構成を作成」を押すと最初のパラメータを入力することができるので、バージョン情報を入力して保存しましょう。
また、それぞれ項目は以下の通りです。

  • パラメータ名
    • アプリ側でパラメータにアクセスするためのキー
  • データ型
    • パラメータのタイプ
  • Description
    • パラメータの説明
  • Default value
    • 設定するパラメータ

スクリーンショット 2022-01-12 20.50.26.png

保存すると以下のような画面になります。

スクリーンショット 2022-01-12 20.55.42.png

この状態ではまだ変更が適用されていません。右上の「変更を公開」ボタンを押すと変更が適用されて、設定したパラメータをアプリで使用できるようになります。
また、新たにパラメータを追加することも可能です。
私の場合は、current_versionの他にrequire_force_update(強制アップデートさせるかどうか)とapp_store_url(app storeのURL)も追加しています。

最後に「変更を公開」ボタンを押してFirebase側の設定は完了です。

スクリーンショット 2022-01-12 21.00.32.png

アプリ側

次はアプリ側の実装についてです。
まず全体のコードを載せてしまいます。

RemoteConfigProvider.swift
import FirebaseRemoteConfig

/// Firebaseのコンソールで設定したパラメータ名に対応
enum ConfigKey: String {
    case currentVersion = "current_version"
    case forceUpdateRequired = "require_force_update"
    case storeUrl = "app_store_url"

    static func makeDefaults() -> [String: Any] {
        [
            currentVersion.rawValue: "",
            forceUpdateRequired.rawValue: false,
            storeUrl.rawValue: ""
        ]
    }
}

final class FirebaseRemoteConfigProvider {
    private let remoteConfig = RemoteConfig.remoteConfig()

    /// パラメータを取ってくる
    func fetchConfig(completion: (() -> Void)? = nil) {

        // 取得するパラメータのデフォルトを設定
        remoteConfig.setDefaults(ConfigKey.makeDefaults() as? [String: NSObject])
        
        // FetchとActivateを一括で行う
        remoteConfig.fetchAndActivate(completionHandler: { status, error in
            switch status {
            case .successUsingPreFetchedData, .successFetchedFromRemote:
                completion?()
            case .error:
                if let error = error {
                    print(error.localizedDescription)
                }
            @unknown default: fatalError()
            }
        })
    }

    /// RemoteConfigから取ってきたパラメータを取得
    func getConfig(key: ConfigKey) -> RemoteConfigValue {
        remoteConfig.configValue(forKey: key.rawValue)
    }
}
ForceUpdateManager.swift
import Combine
import FirebaseRemoteConfig

final class UpdateCheckManager {

    static let shared = UpdateCheckManager()
    private let remoteConfigProvider = FirebaseRemoteConfigProvider()
    private var cancellable: AnyCancellable?

    private init() {}

    func setup() {
        observeApplicationDidBecomeActive()
    }

    /// アプリがActiveになった際にアップデートチェック
    private func observeApplicationDidBecomeActive() {
        cancellable = NotificationCenter.Publisher(center: .default, name: UIApplication.didBecomeActiveNotification, object: nil)
            .sink(receiveValue: { [weak self] _ in
                FirebaseRemoteConfigProvider().fetchConfig(completion: {
                    self?.forceUpdateIfNeeded()
                })
            })
    }

    /// requireForceUpdateがtrueかつ現在のバージョンが最新のバージョンと異なる場合に強制アップデート
    private func forceUpdateIfNeeded() {
        let localVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
        let requireForceUpdate = remoteConfigProvider.getConfig(key: .forceUpdateRequired).boolValue

        if requireForceUpdate, 
           let currentVersionString = remoteConfigProvider.getConfig(key: .currentVersion).stringValue,
           localVersionString != currentVersionString 
        {
            guard let storeUrlString = remoteConfigProvider.getConfig(key: .storeUrl).stringValue,
                  let storeUrl = URL(string: storeUrlString)
            else {
                return
            }
            // 強制アップデートのアラートを出す
        }
    }
}

AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UpdateCheckManager.shared.setup()
        return true
    }
    ...
}

各クラスの説明は以下の通りです。

クラス 機能
UpdateCheckmanager 強制アップデートに関する処理を担当
FirebaseRemoteConfigProvider Firebase Remote Configとの通信とパラメータ取得
ConfigKey Firebase Remote Configのパラメータ名を定義

コードを見ていただくとわかりますが、UpdateCheckManagerdidBecomeActiveを監視しています。アプリがアクティブになった際は、FirebaseRemoteConfigProviderにFirebaseのコンソールで設定したパラメータを取ってきてもらいます。

また今回は、「アプリがアクティブになった際に強制アップデートのアラートを出す」という方針を取っております。これにより、ストアに移動してアップデートせずに戻ってきた場合でも再度アラートを出すことができます。

まとめ

Firebase Remote Configを使用した強制アップデートの実装についてまとめました。
強制アップデートの実装については、他にも強制アップデート用のライブラリを利用する等の方法があるようなので、作成するアプリに適した方法を使用してください。

参考文献

Firebase Remote Configを使ってみる
Implementing Force Update Feature using Firebase Remote Config in iOS

12
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?