Xcode 項目多環境配置最佳實踐

您根據不一樣的環境配置了哪些內容?您可能具備僅用於調試的視圖,或者您可能但願關閉發佈版本的日誌記錄。您可能有多個後端環境可配置爲 dev,QA,UAT,stage,prod 等。其中每一個都須要不一樣的 root url,api key和app secret。該應用程序還可能與社交媒體,崩潰報告工具或其餘分析工具集成,咱們不該該經過咱們的測試工做污染這些數據。咱們可能還但願更改應用程序圖標和應用程序名稱,以使其顯示已安裝的應用程序正在運行的環境。shell

開發簡單的 iOS 應用程序並不用擔憂配置太多,這很容易。當你剛剛開始時,可使用代碼進行一些設置,根據須要修改值。您甚至能夠嘗試註釋/取消註釋代碼行以在不一樣配置之間切換。有些人使用#if DEBUG。這些中的任何一個都會很快成爲問題。它容易出錯而且很是耗時。那麼咱們該怎麼辦?swift

讓咱們來看一個設置具備多個環境的項目的示例。我只會作兩個,但能夠根據須要重複這些步驟。後端

創建

您可使用現有項目或建立新項目。單個視圖應用程序適用於此演示。我稱之爲「EnvironmentsTest」。默認狀況下,您將擁有一個 scheme 和兩個 configurations(debug和release)。讓咱們從一個結構開始,以保存咱們的可配置屬性。api

struct Config {
    let scheme: String
    let host: String
    let key: String
    
    init() {
        scheme = "https"
        host = "api.testapp.com"
        key = "key.testapp.prod"
    }
}
extension Config {
    static var current: Config = Config()
}
複製代碼

爲了測試這一點,咱們能夠在應用程序完成啓動時打印配置。注意:不要忘記刪除它,您不但願您的應用程序在生產中泄露此信息。xcode

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {        
    print(Config.current)
    return true
}
複製代碼

在控制檯中,您將看到指望的配置被打印出來。安全

Config(scheme: "https", host: "api.testapp.com", key: "key.testapp.prod")
複製代碼

Debug & Release vs. Dev & Prod

當你只有兩個環境擔憂時,幾乎能夠天然地將調試分配給 dev 並釋放到 prod。我已經看過不少了。不幸的是,這不起做用。 Dev 和 prod 表示環境配置,而 debug 和 release 是構建配置。您應該可以在某種程度上混合和匹配它們。例如,您可能想要調試 prod 構建。您永遠不會發布開發構建,但您可能想要分析開發構建,咱們一般使用發佈配置。固然,當您想要添加更多環境時,您將徹底崩潰。bash

經過 info.plist 注入配置

咱們能夠在 Info.plist 文件中建立能夠讀入的條目來替換默認配置。app

<key>Config</key>
	<dict>
		<key>scheme</key>
		<string>http</string>
		<key>host</key>
		<string>dev-api.testapp.com</string>
		<key>key</key>
		<string>key.testapp.dev</string>
	</dict>
複製代碼

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea60c980701?w=1070&h=151&f=png&s=39814

而後更改 Config 擴展名以讀取它。直接使用 infoDictionary 可能很誘人,特別是當咱們只有三個值時,但隨着你的配置變得愈來愈複雜,擁有可解碼的東西會更好。ide

struct ConfigContainer: Decodable {
    let Config: Config
}

extension Config: Decodable {
    static var current: Config = {
        guard let infoURL = Bundle.main.url(forResource: "Info", withExtension: "plist") else { fatalError("No info.plist in main bundle") }
        do {
            let infoData = try Data(contentsOf: infoURL)
            let decoder = PropertyListDecoder()
            let item = try decoder.decode(ConfigContainer.self, from: infoData)
            return item.Config
        } catch {
            return Config()
        }
    }()
}
複製代碼

如今咱們能夠看到開發環境的配置。工具

Config(scheme: "http", host: "dev-api.testapp.com", key: "key.testapp.dev")
複製代碼

因此,如今咱們經過 info.plist 注入配置,這是朝着正確方向邁出的一步。如今,您如何在這些環境之間切換?咱們須要一種方法來定義全部配置,而後可以在它們之間進行選擇。

Targets

當開發人員但願在同一設備上安裝多個版本(不一樣環境)的應用程序時,他們一般會添加 target。大多數狀況下,添加target 是多餘的。添加 target 時,您必須記住設置每一個 target 的設置。例如,若是要在應用程序中啓用攝像頭訪問,則必須單獨爲每一個 target 設置所需的 Info.plist。此外,不管什麼時候向項目添加文件,都必須確保將其正確添加到每一個 target。

咱們真正追求的是從下拉列表中選擇環境/配置並運行它。schemes 是一種很好的方法。首先,咱們須要設置一些構建配置。

Build Configurations

咱們從 debug 和 release 配置開始。讓咱們將這些與咱們的生產環境相結合。只需將 Debug 重命名爲 Prod-Debug 並將其發佈到 Prod-Release。接下來,建立一個新配置,複製 Prod-Debug。重命名新配置 Dev-Debug。

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea6186b2fe8?w=1966&h=1200&f=png&s=325687

在大多數狀況下,您不須要複製發佈版本。爲每一個環境執行調試和發佈會變得混亂且難以維護。我建議只爲您要分析或分發的環境添加發布配置。

Schemes

如今已經創建了構建配置,咱們能夠建立 scheme。單擊方案下拉列表,而後選擇 「Edit Scheme…」

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea61ae8a7c4?w=1122&h=222&f=png&s=255227

選擇左下角的**「Duplicate Scheme」**按鈕。命名新方案以指明target和環境;我稱之爲「EnvironmentsTest - Dev」.

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea6150d026f?w=1792&h=1008&f=png&s=196530

確保 scheme 是選中 shared 的。即便您不與團隊合做,也能夠確保在移動到另外一臺計算機時保存設置。 對於左側的每種類型的構建**(Run, Test, Profile, Analyze)** ,選擇Info選項卡,而後將Build Configuration設置爲Dev-Debug。 若是您爲此環境建立了發佈配置,則應將其用於 Profile。在 Debug 配置中進行性能分析的後果超出了本文的範圍,在某些狀況下能夠,只知道在 Release 中可能會有不一樣的結果。 我老是把 Archive 設置爲 Prod-Release。這樣,不管選擇何種方案,archive build都將爲 Prod 構建。而後我沒必要擔憂意外上傳 Dev 版本。最終,最好依靠其餘工做流程工具來防止這樣的錯誤,但這是另外一話題了。

完成後按關閉按鈕。

此時,咱們添加了構建配置和scheme,以便爲不一樣的環境構建。可是,若是您運行該應用程序,則每一個環境都會獲得相同的結果。咱們如何實際定製它?

配置設置文件

配置設置文件是設置每一個環境、配置設置的好地方。轉到File -> New -> File… 或按⌘N。向下滾動到「Other」部分,而後選擇Configuration Settings File。按Next並將其命名爲 Dev.xcconfig 和Create。輸入或複製如下內容:

scheme = http
host = dev-api.testapp.com
key = key.testapp.dev
複製代碼

而後返回到 Info.plist 文件並使用如下內容替換 Config 部分。

<key>Config</key>
	<dict>
		<key>scheme</key>
		<string>$(scheme)</string>
		<key>host</key>
		<string>$(host)</string>
		<key>key</key>
		<string>$(key)</string>
	</dict>
複製代碼

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea6376b3177?w=972&h=148&f=png&s=44823

最後,返回建立項目配置的位置。您會注意到右欄是Based on Configuration File。修改 Dev-Debug 項目以使用Dev配置。

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea6158738d4?w=1064&h=360&f=png&s=82220

如今,當您使用 Dev 方案運行項目時,您將看到 Dev 配置的結果,當您使用 Prod 方案運行項目時,您將看到一個空配置。那麼,咱們應該爲 Prod 構建建立另外一個配置設置文件。可是,因爲曝光,我不喜歡這種解決方案。我建議咱們以不一樣的方式處理 prod。

Prod config

若是您要將其分發給公衆,Info.plist 不是放置任何應用程序機密的正確位置。所以,咱們須要找到另外一種配置 Prod 的方法。真正保護這些值超出了本文的範圍,但一個好的開始是將這些值從 Info.plist 中移出並轉換爲已編譯的代碼。若是你回顧咱們的Config結構,它已經存在了。咱們所要作的就是從 Info.plist 中刪除(空)值,代碼應加載咱們的默認配置,即Prod。

從 Project Navigator 中選擇項目,選擇目標並轉到Build Phases選項卡。單擊**+按鈕,而後選擇「New Run Script Phase」**。單擊標題將其重命名爲不太通用的名稱。我將標題設置爲「Remove Prod Config」,將如下內容複製到腳本區域。

PLISTBUDDY="/usr/libexec/PlistBuddy"
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
if [[ -z ${host} ]]; then
$PLISTBUDDY -c "Delete :Config" "${INFO_PLIST}" || true
fi
複製代碼

這就是說,若是「host」變量爲空(或不存在),則從 Info.plist 文件中刪除 Config 對象。

注意:您必須執行**clean build(⌘⇧K)**以強制build phase運行。

如今,您將在每一個 scheme 中得到正確的結果! 🎉

App Transport Security

您可能已經意識到,Apple 如今須要應用程序來支持最佳實踐 HTTPS 安全性。對於每一個環境都遵循此規則一般是個好主意,即便在開發中也是如此。可是,您不可能老是這樣作(例如,在準備「本地」環境時)或者可能還沒有配置,但您仍須要繼續開發。咱們能夠經過另外一個Run Script Phase處理這個問題。將此命名爲「Http - Allows Arbitrary Loads」並將如下內容複製到腳本區域。

PLISTBUDDY="/usr/libexec/PlistBuddy"
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
if [ ${scheme} == "http" ]; then
$PLISTBUDDY -c "Add :NSAppTransportSecurity dict" "${INFO_PLIST}"
$PLISTBUDDY -c "Add :NSAppTransportSecurity:NSAllowsArbitraryLoads bool true" "${INFO_PLIST}" || true
$PLISTBUDDY -c "Set :NSAppTransportSecurity:NSAllowsArbitraryLoads true" "${INFO_PLIST}"
else
$PLISTBUDDY -c "Delete :NSAppTransportSecurity:NSAllowsArbitraryLoads" "${INFO_PLIST}" || true
fi
複製代碼

經過在application(_:didFinishLaunchingWithOptions:)打印值來測試每一個 scheme.

print(String(describing: Bundle.main.infoDictionary?["NSAppTransportSecurity"]))
複製代碼

很是好,不是嗎?

並行運行 Dev 和 Prod

有時在同一設備上安裝Dev build和Prod build很方便,這樣你就能夠在二者之間切換。要實現這一點,您所要作的就是更改包ID。您還須要一種簡單的方法來肯定哪個是哪一個。爲此,咱們能夠更改顯示名稱和圖標。

注意:對於全部這些設置,我始終保持生產版本的構建;這是進入App Store的構建,不該修改。因爲這個規則,它也很容易識別,由於它沒有任何修飾符。

Bundle Identifier

要更改Bundle Identifier,請轉到 target 的build settings並搜索「bundle identifier」,而後單擊箭頭以展開全部配置。我一般會附加一個「 - 」後跟配置名稱。

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea675dec9f4?w=2882&h=598&f=png&s=366310

Display Name

這些步驟與Bundle Identifier相同,但此次搜索「product name」。爲此,我建議用環境替換名稱。不然,文本可能太長而沒法使用。

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea6af622079?w=2880&h=590&f=png&s=591305
您還可使用顯示名稱中的版本號快速肯定安裝的版本。

Icon

更改圖標幾乎一樣容易。在同一屏幕上,搜索「Icon」。您應該看到「Asset Catalog App Icon Set Name.」。再次,添加一個破折號,後跟配置名稱。

https://user-gold-cdn.xitu.io/2019/8/27/16cd0ea7045e6260?w=2880&h=588&f=png&s=620719
如今,在 asset catalog 中爲每一個配置建立圖標集。

總結

每一個配置執行全部這些步驟彷佛須要作不少工做才能更改一些設置,但這很是值得。固然,它須要一些時間來完成全部設置,但完成後切換變得微不足道。花時間作正確的事,從長遠來看,你將節省時間。您能夠經過在環境之間快速切換來節省時間。你能夠省去手動操做時犯錯誤的麻煩。

相關文章
相關標籤/搜索