Firebase Remote Config 建立強制/建議更新機制

Zhi-Hong Lin
10 min readOct 3, 2023
  1. Firebase 設定
  2. 判斷更新類型
  3. 建立 AppUpdateManager 處理 remote config 更新邏輯
  4. 即時監聽更新

1. Firebase 設定

前往 Firebase 後台設定 Remote Config

  • key:update_version
  • value(JSON 格式):{ "force_update_version": "設定的強制更新版本號", "recommend_update_version": "設定的建議更新版本號" }

建立一個 struct 存取資料

struct UpdateResponse: Codable {
let forceUpdateVersion, recommendUpdateVersion: String

enum CodingKeys: String, CodingKey {
case forceUpdateVersion = "force_update_version"
case recommendUpdateVersion = "recommend_update_version"
}
}

2. 判斷更新類型

宣告一個 enum 用來定義更新的類型

compareVersions 用來比較兩個數字的字串

checkForUpdates 主要邏輯,判斷要回傳什麼 UpdateType

enum UpdateType {
/// 不更新
case none
/// 建議更新
case recommend
/// 強制更新
case force

static func checkForUpdates(currentVersion: String, forceUpdateVersion: String, recommendUpdateVersion: String) -> UpdateType {
let currentVersionComparison = compareVersions(currentVersion, forceUpdateVersion)
let recommendUpdateComparison = compareVersions(currentVersion, recommendUpdateVersion)

if currentVersionComparison == .orderedSame && recommendUpdateComparison == .orderedAscending {
return .recommend
} else if currentVersionComparison == .orderedAscending || recommendUpdateComparison == .orderedAscending {
return .force
} else {
return .none
}
}

private static func compareVersions(_ version1: String, _ version2: String) -> ComparisonResult {
return version1.compare(version2, options: .numeric)
}
}

3. 建立 AppUpdateManager 以處理 remote config 的更新邏輯

定義一個 protocol 讓其他類別處理各種更新類型邏輯

protocol UpdateTypeHandler {
func handleUpdate(type: UpdateType)
}

建立 AppUpdateManager,讓外部注入 RemoteConfig

updateVersion remote config 設定的 key

currentVersion 目前 App 版號

class AppUpdateManager {
let updateVersion = "update_version"

private var currentVersion: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
}

private let remoteConfig: RemoteConfig

init(remoteConfig: RemoteConfig) {
self.remoteConfig = remoteConfig
}

private var updateTypeHandler: UpdateTypeHandler?

func registerUpdateTypeHandler(handler: UpdateTypeHandler) {
self.updateTypeHandler = handler
}
}

將 remote config 的 JSON 轉換成 model,並取得更新類型

private func getUpdateType() -> UpdateType {
let jsonData = remoteConfig[updateVersion].dataValue
guard let updateResponse = UpdateResponse.decode(from: jsonData) else {
return .none
}

let forceUpdateVersion = updateResponse.forceUpdateVersion
let recommendUpdateVersion = updateResponse.recommendUpdateVersion

let type = UpdateType.checkForUpdates(currentVersion: currentVersion,
forceUpdateVersion: forceUpdateVersion,
recommendUpdateVersion: recommendUpdateVersion)
return type
}

處理更新的 key

func handleUpdateKey() {
let type = getUpdateType()
self.updateTypeHandler?.handleUpdate(type: type)
}

調用 fetchWithCompletionHandler: 取得在後台設置的所有值

private func checkAppVersion() {
remoteConfig.fetch() { [weak self] (status, error) in
guard let self = self else { return }
guard status == .success, error == nil else {
print("錯誤:\\(error)")
return
}
remoteConfig.activate()
self.handleUpdateType(type)
}
}

建立一個類別 conform UpdateTypeHandler 處理顯示建議、強制更新的邏輯

class AppVersionUpdateHandler: UpdateTypeHandler {
func handleUpdate(type: UpdateType) {
switch type {
case .none:
break
case .recommend:
print("建議更新")
case .force:
print("強制更新")
}
}
}

在以下兩個 App 生命週期呼叫 checkAppVersion

private lazy var appUpdateManager: AppUpdateManager = {
let appUpdateManager = AppUpdateManager(remoteConfig: remoteConfig)
let appVersionUpdateHandler = AppVersionUpdateHandler()
appUpdateManager.registerUpdateTypeHandler(handler: appVersionUpdateHandler)
return appUpdateManager
}()

//第一次開啟 App 時呼叫
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.

appUpdateManager.checkAndHandleAppUpdate()

return true
}
//回到 App 前景時呼叫
func applicationWillEnterForeground(_ application: UIApplication) {
appUpdateManager.checkAndHandleAppUpdate()
}

4. 即時監聽更新

Firebase 提供了即時監聽更新機制,調用 addOnConfigUpdateListener 以開始監聽更新並自動提取任何新參數值或更新後的參數值。

不建議做強制、建議更新時做即時監聽更新的機制,會影響使用者體驗

  • configUpdate.updatedKeys 取得所有更新的 key 值
  • self.appUpdateManager.updateVersion 是一個自定義的常數字串,對應 remote config 所設定的 key
extension AppDelegate {
private func listenForConfigUpdates() {
remoteConfig.addOnConfigUpdateListener { [weak self] configUpdate, error in
guard let self = self else { return }

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

self.remoteConfig.activate { [weak self] changed, error in
guard let self = self else { return }

for updateKey in configUpdate.updatedKeys {
switch updateKey {
case self.appUpdateManager.updateVersion:
self.appUpdateManager.handleUpdateKey()
default:
break
}
}
}
}
}
}

完整程式碼

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response

Recommended from Medium

Lists

See more recommendations