3
1

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.

[Swift] リアルタイム更新に対応したRemoteConfigを導入する

Last updated at Posted at 2023-08-13

はじめに

リアルタイム更新に対応したFirebase RemoteConfigを導入したくドキュメントを読みながら実装しました。

環境

Xcode 14.3

内容

Firebaseはセットアップ済みの状態を想定しRemoteConfig部分のみまとめていきます。

RemoteConfigとは

Firebase Remote Config を使ってアプリ内パラメータを定義し、その値をクラウドで更新できます。これにより、アプリのアップデートを配布しなくてもアプリの外観や動作を変更できます。

設定できる値には以下のような制限がある

パラメータ値のデータ型

  • String
  • Boolean
  • Number
  • JSON

パラメータと条件の制限

  • 最大2,000個のパラメータ、最大500個の条件を設定できる
  • パラメータキーの長さは最大256文字
  • プロジェクト内のパラメータ値文字列の合計長は、1,000,000文字以内にする必要がある

基本はドキュメント通りやっていきます

Remote Config をアプリに追加

まずはSPMを使ってFirebaseRemoteConfigSwiftを取得

RemoteConfigを扱うクラスにimport

import FirebaseRemoteConfig
import FirebaseRemoteConfigSwift

Remote Config シングルトン オブジェクトを作成

remoteConfig = RemoteConfig.remoteConfig()
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings

ここで設定するminimumFetchIntervalとはRemoteConfigの値を取得する間隔ですが、リアルタイム更新に対応すれば不要になる

Apple platforms SDK v10.7.0 以降では、リアルタイムの Remote Config を使用して、更新されたパラメータ値が公開されるとすぐに Remote Config バックエンドから自動的にフェッチできます。これにより、最小フェッチ間隔の設定が不要になります。

フェッチ間隔の優先順位は以下の順番

  1. fetch(long) のパラメータ
  2. FIRRemoteConfigSettings.MinimumFetchInterval のパラメータ
  3. デフォルト値(12 時間)

サンプルコードのセットアップ部分を抜き出すとこのような形

import FirebaseRemoteConfig
import FirebaseRemoteConfigSwift

class RemoteConfigViewController: UIViewController {
  private var remoteConfig: RemoteConfig!
  private var remoteConfigView: RemoteConfigView

  /// Convenience init for injecting Remote Config instances during testing
  /// - Parameter remoteConfig: a Remote Config instance
  convenience init(remoteConfig: RemoteConfig) {
    self.init()
    self.remoteConfig = remoteConfig
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    setupRemoteConfig()
  }

  /// Initializes defaults from `RemoteConfigDefaults.plist` and sets config's settings to developer mode
  private func setupRemoteConfig() {
    remoteConfig = RemoteConfig.remoteConfig()
    let settings = RemoteConfigSettings()
    settings.minimumFetchInterval = 0
    remoteConfig.configSettings = settings
    ...
  }
}

アプリ内デフォルトパラメータ値を設定する

この対応は必須ではないのでスキップでもOK

NSDictionaryまたはplistを使用して、一連のパラメータ名とデフォルトパラメータ値を定義しておくと、Remote Configバックエンドに接続する前もしくは値が設定されていない場合にデフォルト値を使用できる。

plistはRemoteConfigのコンソールから取得できる

アプリ内でパラメータ値がgetメソッドによって返される優先順位

  1. バックエンドから値がフェッチされ、有効化された場合、アプリはそのフェッチされた値を使用します。有効化されたパラメータ値は永続的です。
  2. バックエンドから値がフェッチされなかった場合、または Remote Config バックエンドからフェッチされた値が有効化されていない場合、アプリはアプリ内デフォルト値を使用します。
  3. アプリ内デフォルト値が設定されていない場合、アプリは静的型の値(int の場合は 0、boolean の場合は false など)を使用します。

サンプルコードのserDefaults部分

  private func setupRemoteConfig() {
    remoteConfig = RemoteConfig.remoteConfig()
    // This is an alternative to remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults"))
    do {
      try remoteConfig.setDefaults(from: QSConfig(topLabelKey: "myTopLabel",
                                                  bottomLabelKey: "Buy one get one free!",
                                                  freeCount: 4))
    } catch {
      print("Failed to set Defaults.")
    }
    ...
  }

値をフェッチして有効にする

Remote Config からパラメータ値をフェッチするには、fetchWithCompletionHandler:またはfetchWithExpirationDuration:completionHandler:メソッドを呼び出す。バックエンドに設定したすべての値がフェッチされ、Remote Config オブジェクトにキャッシュ保存される。

1 回の呼び出しで値をフェッチして有効化する場合は、fetchAndActivateWithCompletionHandler:を使用します。

remoteConfig.fetch { (status, error) -> Void in
  if status == .success {
    print("Config fetched!")
    self.remoteConfig.activate { changed, error in
      // ...
    }
  } else {
    print("Config not fetched")
    print("Error: \(error?.localizedDescription ?? "No error available.")")
  }
  self.displayWelcome()
}

3つのfetchメソッド👇

  • func fetch() async throws -> RemoteConfigFetchStatus
    データを取得。activate()することで取得したデータがアプリで使用できる

  • func fetch(withExpirationDuration expirationDuration: TimeInterval) async throws -> RemoteConfigFetchStatus
    データを取得し、設定データの存続期間を指定する期間を設定。activateWithCompletion:することで取得したデータがアプリで使用できる

  • func fetchAndActivate() async throws -> RemoteConfigFetchAndActivateStatus
    データを取得し、成功した場合は取得したデータをアクティブにします。フェッチ呼び出しが成功した場合、データのアクティブ化を試行した後、オプションの完了ハンドラー コールバックが呼び出される

ドキュメントとは別ですが、サンプルコードの実装はこのような形になっていた

  func fetchAndActivateRemoteConfig() {
    remoteConfig.fetchAndActivate { status, error in
      guard error == nil else { return self.displayError(error) }
      print("Remote config successfully fetched & activated!")
      do {
        let qsConfig: QSConfig = try self.remoteConfig.decoded()
        print(qsConfig)
      } catch {
        self.displayError(error)
        return
      }
      DispatchQueue.main.async {
        self.updateUI()
      }
    }
  }

リアルタイムで更新をリッスンする

パラメータ値をフェッチしたら、リアルタイム更新を使用して、バックエンドからの更新をリアルタイムでリッスンできる

remoteConfig.addOnConfigUpdateListener { configUpdate, error in
  guard let configUpdate, error == nil else {
    print("Error listening for config updates: \(error)")
  }

  print("Updated keys: \(configUpdate.updatedKeys)")

  self.remoteConfig.activate { changed, error in
    guard error == nil else { return self.displayError(error) }
    DispatchQueue.main.async {
      self.displayWelcome()
    }
  }
}

ほぼほぼドキュメントと変わりませんが、サンプルコードでこのような実装になっていた

  /// Initializes defaults from `RemoteConfigDefaults.plist` and sets config's settings to developer mode
  private func setupRemoteConfig() {
    remoteConfig = RemoteConfig.remoteConfig()
    // This is an alternative to remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults"))
    do {
      try remoteConfig.setDefaults(from: QSConfig(topLabelKey: "myTopLabel",
                                                  bottomLabelKey: "Buy one get one free!",
                                                  freeCount: 4))
    } catch {
      print("Failed to set Defaults.")
    }
    let settings = RemoteConfigSettings()
    settings.minimumFetchInterval = 0
    remoteConfig.configSettings = settings

    // [START add_config_update_listener]
    remoteConfig.addOnConfigUpdateListener { configUpdate, error in
      guard error == nil else { return self.displayError(error) }
      print("Updated keys: \(configUpdate!.updatedKeys)")

      self.remoteConfig.activate { changed, error in
        guard error == nil else { return self.displayError(error) }
        DispatchQueue.main.async {
          self.updateUI()
        }
      }
    }
    // [END add_config_update_listener]
  }

おわりに

もともとアプリの運用で非常に便利であったRemoteConfigですが、リアルタイム更新が可能になったことで、より活用の幅が広がったのかなと思います!セットアップも簡単なので個人開発アプリなどでも初回リリースから入れていきたいところです

それと今回ドキュメントを読んでいてRemote Config の更新公開時に Slack /メール メッセージを送信する方法について書かれていた部分があり、これは業務でも使えそうだな〜と思ったのでこの部分はあらためて読んでみたいと思います

参考

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?