SwiftUI 內功之 feature flags的使用

Swift中的feature flags

在爲應用開發新功能時,採用某種形式的機制逐步推出新的實現和功能可能非常有用,而不必一次向每個用戶啓動。 這不僅可以幫助“降低”重大更改的啓動風險(如果出現問題,我們可以隨時回滾),還可以幫助我們收集有關部分完成的功能的反饋,或使用A / B等技術進行實驗 測試。

功能標誌可以充當這種機制。 從本質上講,它們使我們可以在編譯時或運行時在某些條件下關閉代碼庫的某些部分。本文讓我們看一下在Swift中可以使用功能標記的幾種不同方式。

Conditional compilation 條件編譯

處理代碼庫時,在處理正在進行的功能時,可以使用多種策略。例如,我們可以使用諸如功能分支之類的東西,並使用版本控制來使正在開發的功能與我們的主master分支完全分開。準備發佈該功能後,我們只需將其合併併發布。

但是,將新的實現和功能連續集成到我們的主分支中有一些巨大的優勢。它使我們能夠及早發現錯誤和問題,如果兩個分支之間的分歧很大,則可以免去解決大量合併衝突的痛苦,還可以讓我們在內部或內部發布新功能的預發佈版本。到Beta測試人員。

但是我們仍然需要一些方法來刪除不應實時發佈到App Store的代碼。一種方法是使用編譯器標誌,它使我們可以標記代碼塊,以便僅在設置了標誌的情況下才將其編譯。假設我們的應用當前正在使用Core Data,並且我們希望在生產中保持這種方式(目前),同時仍然能夠嘗試一種新的解決方案-Realm。爲此,我們可以使用DATABASE_REALM編譯器標誌,該標誌僅用於我們要在其中使用Realm的內部版本(例如,對於beta內部版本)。然後,我們可以告訴編譯器在構建應用程序時檢查該標誌,如下所示:

class DataBaseFactory {
    func makeDatabase() -> Database {
        #if DATABASE_REALM
        return RealmDatabase()
        #else
        return CoreDataDatabase()
        #endif
    }
}

要打開或關閉上述標誌,我們可以簡單地在Xcode中打開目標的構建設置,然後在Swift編譯器-自定義標誌>活動編譯條件下添加或刪除DATABASE_REALM。 這對於仍處於活動開發中的功能特別有用,這使使用該功能的開發人員可以輕鬆地在本地將其打開,而不會影響生產版本。

靜態標誌

當您想從應用程序中完全刪除新實現的代碼時,條件編譯非常有用。 但是有時候這不是必需的,就是不實際的,在那些情況下,在代碼中定義功能標記可能是一個更好的選擇。

一種非常簡單的方法是使用靜態屬性。 例如,我們可以創建一個包含所有標誌的FeatureFlags結構,如下所示:

struct FeatureFlags {
    static let searchEnabled = false
    static let maximumNumberOfFavorites = 10
    static let allowLandscapeMode = true
}

有了上面的FeatureFlags類型,我們現在可以將檢查放在激活給定功能的代碼路徑中。 這是一個有條件地激活搜索功能的示例方法,該功能從ListViewController的viewDidLoad()方法中調用:

extension ListViewController {
    func addSearchIfNeeded() {
        // If the search feature shouldn't be enabled, we simply return
        guard FeatureFlags.searchEnabled else {
            return
        }

        let resultsVC = SearchResultsViewController()
        let searchVC = UISearchController(
            searchResultsController: resultsVC
        )

        searchVC.searchResultsUpdater = resultsVC
        navigationItem.searchController = searchVC
    }
}

靜態標誌的好處在於,它們與編譯器標誌一樣,非常容易設置和集成。但是,它們不允許我們在應用程序編譯後修改標誌的值。爲了做到這一點,我們需要開始使用運行時標誌。

運行時標誌
添加選項以在運行時配置我們的應用程序的功能標誌可能有點“雙刃劍”。一方面,它可以使我們爲特定比例的用戶羣更改給定標誌的值,從而使我們能夠執行A / B測試;另一方面,它可以使我們的應用更難以維護和調試-因爲最終使用的代碼路徑在編譯時尚未完全確定。

運行時標誌通常是從某種形式的後端系統加載的,並且有可能(取決於應用程序的體系結構)甚至包含在應用程序作爲用戶登錄的一部分而收到的響應中(否則,通常有一個/ feature_flags端點或類似的標誌)該應用在啓動時進行查詢)。 (可選)我們還可以使用某種形式的調試UI啓用在應用程序本身中調整標誌的功能。

無論我們如何加載功能標記的值,我們都希望更新FeatureFlags類型以使用實例屬性而不是靜態屬性。這樣,我們可以加載標誌的值,然後將其轉換爲FeatureFlags實例,然後在需要時將其注入。我們的標誌類型現在看起來像這樣:

struct FeatureFlags {
    let searchEnabled: Bool
    let maximumNumberOfFavorites: Int
    let allowLandscapeMode: Bool
}

爲了能夠從序列化格式轉換實例,我們還將添加帶有字典的初始化程序。 這樣,我們可以從JSON後端響應或從應用程序本地存儲的值(例如,從緩存)創建功能標誌:

extension FeatureFlags {
    init(dictionary: [String : Any]) {
        searchEnabled = dictionary.value(for: "search", default: false)
        maximumNumberOfFavorites = dictionary.value(for: "favorites", default: 10)
        allowLandscapeMode = dictionary.value(for: "landscape", default: true)
    }
}

private extension Dictionary where Key == String {
    func value<V>(for key: Key,
                  default defaultExpression: @autoclosure () -> V) -> V {
        return (self[key] as? V) ?? defaultExpression()
    }
}

現在,我們可以在應用啓動或用戶登錄時加載功能標誌(取決於我們是否希望我們的標誌是特定於用戶的),並在需要時注入它們,如下所示:

class FavoritesManager {
    private let featureFlags: FeatureFlags

    init(featureFlags: FeatureFlags) {
        self.featureFlags = featureFlags
    }

    func canUserAddMoreFavorites(_ user: User) -> Bool {
        let maxCount = featureFlags.maximumNumberOfFavorites
        return user.favorites.count < maxCount
    }
}

現在,我們可以自由切換某些功能的打開或關閉,或調整確定應用程序邏輯一部分的值。 我們還可以在我們的應用程序運行時使我們的標誌發生變化(並添加某種形式的觀察API來對更改做出反應),但是我個人很少認爲增加這麼多的複雜性值得。 只需加載一次值並在其後設置應用程序,這將使事情變得簡單,並避免引入棘手的極端情況。

結論
能夠快速迭代應用程序時,使用功能標誌可能是關鍵,尤其是隨着應用程序團隊的不斷壯大以及代碼庫的更新速度大大提高。通過能夠有條件地啓用某些功能或調整它們的行爲,我們通常可以更快地將代碼集成到我們的主分支中,並且仍然繼續交付我們的應用程序。

功能標誌也可能給我們的設置增加一些複雜性,尤其是在使用運行時標誌時。重現僅在標誌的特定組合處於啓用狀態時才發生的錯誤會變得更加困難,並且測試我們應用程序的所有潛在代碼路徑可能很快變得更加複雜且耗時。

如果您以前從未使用過功能標誌,我建議您從簡單開始(也許使用編譯器標誌或靜態標誌),然後從那裏開始。與大多數工具一樣,它們可能會要求您稍微採用工作流程,尤其是在項目當前不使用太多自動化測試的情況下(這會使使用功能標誌的風險大大降低)。

推薦

基礎文章推薦

經典教程推薦

上新

技術源碼推薦

推薦文章

CoreData篇

Combine篇

TextField篇

JSON文件篇


一篇文章系列

技術交流

QQ:3365059189
SwiftUI技術交流QQ羣:518696470

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章