3
2

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 2023-05-31

0. 初めに

先日、iOSアプリケーションの強制アップデートの実装をしました。
方法は色々あるなと考えたのですが、今回はGoogleさんが提供しているFirebase Remote Configを使用する方法が簡単だったので備忘録的にまとめました。

参考にしていただければ幸いです。

目次

  1. Firebaseのセットアップ
  2. Remote Configを設定する
  3. アプリとFirebaseを繋ぐ
  4. 強制アップデートのコードを実装する
  5. 最後に

手順

Firebaseは、Googleのモバイルプラットフォームです。
Firebaseには無料で使える便利な機能が多く、ドキュメントも充実しており、読みやすいのでおすすめです。
今回はその中の、Remote Config機能を使用していきます。

1. Firebaseで新規プロジェクトを作成する

まずはFirebaseの公式サイトへ行き、右上の"コンソールへ移動"します。

スクリーンショット 2023-05-31 8.28.38.png

次に、Firebase プロジェクトから"プロジェクトを追加"し、作成手順へ進みます。
スクリーンショット 2023-05-31 8.31.05.png

手順通りに進めていきます。
スクリーンショット 2023-05-31 8.37.58.png

手順(2/3)のGoogleアナリティクスは有効にすることをおすすめします!
プロジェクト内で、AnalyticsやCrashlytics等を使用する際に必要となるからです。
スクリーンショット 2023-05-31 8.38.07.png

手順(3/3)で"Default Account for Firebase"をセレクトし、プロジェクトを作成!
少し作成までに時間がかかることもありますが、これで完了です。(簡単だぜ)
スクリーンショット 2023-05-31 8.38.21.png

以下のようなプロジェクト概要画面が表示されます。
スクリーンショット 2023-05-31 8.43.45.png

2. Remote Configを設定する

Remote Configでは、任意の名前(パラメータ名)をつけた値(value)を登録できます。
Firebaseとアプリを繋ぎ、その値をアプリ側で取得することで様々な機能を実装できます!

今回実装する強制アップデートでは、事前にRemote Configに最新のアプリバージョンの値を登録しておき、Remote Configのバージョンと、ローカルのアプリのバージョンを比較することでアプリ上に強制アップデートの通知を出すように実装していきます。

それでは、プロジェクト概要画面の左から "構築 > Remote Config" を選んでいきましょう!
スクリーンショット 2023-05-31 8.46.27.png

以下の"構成を作成"へ進みます!ここでパラメータ名等を作ります。
スクリーンショット 2023-05-31 9.02.11.png

右側からパラメータの作成画面が出ますので、名前(パラメータ名)と値(value)、説明(Description)を入れます。
今回は最新のバージョンを登録したいので、名前(current_version)、値(2.0)、説明(現在ストアに公開中のアプリバージョン)を記入し、保存しましょう!
これでRemote Configの設定は完了です!
スクリーンショット 2023-05-31 9.05.02.png

構成を公開のアラートについて

Remote Configの構成は公開しないとアプリ側で取得はできません。
なので以下のように警告が出ていますが、後で公開するので一旦スルーします。
スクリーンショット 2023-05-31 9.09.49.png

3. アプリとFirebaseを繋ぐ

では次に、アプリとFirebaseを繋ぎます。
先ほど作成したFirebaseのプロジェクトにアプリを登録すると言った方がいいかもしれません。

まずは、適当なアプリを用意しましょう!
新規作成でもOKです。
スクリーンショット 2023-05-31 16.11.21.png

次に、"①アプリ > ②TARGET > ③Sining & Capability" から "④Bundle Identifier" をコピーします。
スクリーンショット 2023-05-31 16.23.05.png

Firebaseの"プロジェクト概要"に戻り、"iOS+"をクリックします。
スクリーンショット 2023-05-31 16.28.22.png

先ほどコピーしたBundle Idcentifierを記入し、
以下、手順に従います。
スクリーンショット 2023-05-31 16.30.36.png

手順②で、Info.plistをダウンロードして、アプリに入れましょう。
スクリーンショット 2023-05-31 16.30.58.png
以下のように、自分のアプリのinfo.plistの下あたりにインポートすれば問題ありません。
スクリーンショット 2023-05-31 16.34.39.png

③手順にて、PackageをInstallします。
スクリーンショット 2023-05-31 16.39.18.png

以下のように、URLを入れ、"Add Package"をクリック
スクリーンショット 2023-05-31 16.39.47.png

Packageが表示されるので、"FirebaseAnalytics" と"FirebaseRemoteConfig"を追加し、
"Add Package"をクリック
スクリーンショット 2023-05-31 16.42.54.png

手順④の通りに、アプリのAppDelegate.swiftへ移動し、以下を記載

    import UIKit
    import FirebaseCore //ここ追加だよ!
    
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
      var window: UIWindow?

      func application(_ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions:
          [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()  //ここ追加だよ!

        return true
      }
    }

最後に、手順⑤を完了させれば連携は完了!!!

4. 強制アップデートのコード実装を実装する

あとはコードを書くだけです。
まずは全文書きます。

AppDelegate.swift
import UIKit
import FirebaseCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        // Configure Firebase
       FirebaseApp.configure()
         
         //バージョン確認を実行
       VersionChecker.sharedChecker.setApplicationDidBecomeActive()
        
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}
VersionChecker.swift
import UIKit
import Firebase
import FirebaseRemoteConfig
import FirebaseRemoteConfigSwift

class VersionChecker {
    private let remoteConfig = RemoteConfig.remoteConfig()
    
    static let sharedChecker = VersionChecker()
    
    public func setApplicationDidBecomeActive() {
        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
    }
    
    @objc private func applicationDidBecomeActive() {
        fetchRemoteConfigAndCheckVersion()
    }
    
    private func fetchRemoteConfigAndCheckVersion() {
        remoteConfig.fetch(withExpirationDuration: 0) { (status, error) in
            guard error == nil else {
                print("error in fetching. value: \(error)")
                return
            }
            self.remoteConfig.fetchAndActivate { _, _ in
                if self.checkVersion() {
                    self.showUpdateAlertIfNeeded()
                }
            }
        }
    }
    
    private func checkVersion() -> Bool {
        let currentVersion = remoteConfig.configValue(forKey: "current_version").stringValue ?? ""
        let localVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
        return currentVersion != localVersionString
    }
    
    private func showUpdateAlertIfNeeded() {
        guard let rootViewController = getTopViewController() else { return }
        
        let alertController = UIAlertController(title: "アップデートが必要です", message: "新しいバージョンがApp Storeにあります。アップデートしてください。", preferredStyle: .alert)

        let updateAction = UIAlertAction(title: "アップデート", style: .default) { _ in
            guard let url = URL(string: "https://www.apple.com/jp/app-store/"),
                  UIApplication.shared.canOpenURL(url) else { return }
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }

        let laterAction = UIAlertAction(title: "あとで", style: .cancel, handler: nil)
        
        alertController.addAction(updateAction)
        alertController.addAction(laterAction)
        rootViewController.present(alertController, animated: true, completion: nil)
    }
    
    private func getTopViewController(_ viewController: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
        if let navigationController = viewController as? UINavigationController {
            return getTopViewController(navigationController.visibleViewController)
        } else if let tabBarController = viewController as? UITabBarController, let selected = tabBarController.selectedViewController {
            return getTopViewController(selected)
        } else if let presented = viewController?.presentedViewController {
            return getTopViewController(presented)
        } else {
            return viewController
        }
    }
}

各部分の説明:

  1. Firebase, FirebaseRemoteConfig, FirebaseRemoteConfigSwift で必要なフレームワークをインポートします。
  2. class VersionChecker: バージョンのチェックと必要ならばアップデートのアラートを表示するためのクラスを定義します。
  3. private let remoteConfig = RemoteConfig.remoteConfig(): Firebase Remote Configのインスタンスをプライベートプロパティとして保持します。
  4. static let sharedChecker = VersionChecker(): VersionCheckerのシングルトンインスタンスを作成します。
  5. public func setApplicationDidBecomeActive(): UIApplicationがアクティブになった時に呼ばれる関数を設定します。
  6. @objc private func applicationDidBecomeActive(): UIApplicationがアクティブになった時にRemote Configを取得し、バージョンのチェックを行います。
  7. private func fetchRemoteConfigAndCheckVersion(): Remote Configを取得し、バージョンのチェックを行う関数です。
  8. private func checkVersion() -> Bool: Firebaseから取得したバージョンとローカルのアプリのバージョンを比較し、一致しなければtrueを返します。
  9. private func showUpdateAlertIfNeeded(): バージョンが一致しない場合にアップデートを促すアラートを表示します。
  10. private func getTopViewController(...) -> UIViewController?: 表示中の最上位のViewControllerを取得します。

今回の場合、アプリを開いた際にすぐに強制アップデートを起動させたいのでAppDelegateにVersionChecker()を記載しています。
また、開いた画面の最上位のViewControllerを取得することで、他の画面と被ることを防いでいます。
難しく見えますが、コードのブロックごとを見れば優しい内容です。

最後に、"Firebase RemoteConfig"へ戻り、変更を公開を実施します。
スクリーンショット 2023-05-31 17.15.57.png

公開後、アプリを起動すると、以下のAlertが出れば完成です!!!
Simulator Screenshot - iPhone 14 Pro - 2023-05-31 at 17.28.02.png

ローカルアプリのバージョンと、Firebase RemoteConfig上(構成のパラメーター値)のバージョンが同じな場合、アラートは出ません!

5. 最後に

いかがでしたでしょうか?
RemoteConfigはこのような使い方だけでなく、他の使い方もあります。
今回はバージョンアップのみにフォーカスしました。

まだまだこの強制アップデートの課題として、バックエンド側の運用としては、RemoteConfigの値を管理していかなくてはいけない欠点はあります。ここら辺自動的に管理できれば楽なのですが・・・・(勉強不足)

少しでもみなさんの学習の一助になれば幸いです。
ではでは〜

参考:RemoteConfigドキュメント

3
2
0

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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?