天天看點

iOS開發必備--環境變量配置(Debug & Release)

http://www.cocoachina.com/ios/20151023/13869.html

http://www.cocoachina.com/ios/20151027/13916.html

本篇文章主要講述了如何使用(Xcode配置檔案xcconfig)去動态配置開發或者生産網絡環境, 以及在多項目和運作中如何切換環境。

關于xcconfig檔案, 目前在官方很難找到一篇專門的指南介紹, 但是國外有篇非官方指南《The Unofficial Guide to xcconfig files》詳細的介紹了xcconfig。估計很多新入門的iOS開發對xcconfig檔案都不是很熟悉, 但是大家可能都用過Cocoapods, 其實Cocoapods的項目配置管理很多都是依賴xcconfig檔案去實作的。

Debug宏應該在哪裡定義?

iOS系統本身就區分了Configurations選項讓開發者去修改對應的開發環境配置, 但是因為很多開發者卻又在同一個Configuration環境中自定義了開發環境配置的開發, 使得iOS系統本身的配置成為了擺設, 僅僅用于區分打包方式選項和證書配置。

網絡環境切換是每一個網際網路App開發者都會頻繁用到的功能, 那麼大家都是用什麼樣的方式在切換環境的呢?我本人接觸的項目中最多的就是在預編譯頭檔案裡面寫一行宏定義, 然後根據宏定義去判斷目前的環境。

最典型的例子是在預編譯頭pch檔案中添加一行代碼#define DEBUG 1。然後通過這個DEBUG參數去判斷目前環境是否處于開發網絡或者生産網絡環境。

使用DEBUG宏去判斷判斷開發環境還是生産環境沒有任何問題, 關鍵的問題是我們在什麼時候去定義這個宏和怎麼去動态配置這個宏。

動态配置不同的網絡開發環境

開發環境的切換在代碼中最實用的還是宏定義, 那麼我們怎麼樣才能夠讓宏定義動态可配置呢?

其中一種辦法就是使用GCC預編譯頭參數GCC_PREPROCESSOR_DEFINITIONS。

通常我們可以在Project檔案下的Build Settings對預編譯宏定義進行預設指派。在Xcode6下的路徑為Build Settings->Apple LLVM 6.x Preprocessing->Preprocessor Macros

iOS開發必備--環境變量配置(Debug & Release)

想必大家看這個宏的名字已經知道它的作用了, 實際上就是和在pch頭檔案中添加宏定義沒有太大的差別, 實際上還是有一些好處:

  • Xcode的Project的Build Settings是由一個plist檔案進行描述的, plist本質上是一個XML配置檔案, 通過外部的腳本比較容易去修改。
  • Preprocessor Macros可以按照Configuration選項進行預設配置, 也就是說可以根據不同的環境預先制定不同定義的宏
  • xcconfig配置Build Settings

Xcode Project的Build Settings屬性有很多, 如果每一個屬性都在配置項改過去比較麻煩, 而且容易忘記, 而且Build Settings用源碼的打開可閱讀性也不是很高, 這個時候, 我們可以使用xcconfig檔案去配置Build Settings參數。

xcconifg支援可以根據不同的Configuration選項配置不同的檔案。不同的xcconfig可以指定不同的Build Settings裡的屬性值, 這樣子我們就可以通過項目xcconifg去修改GCC_PREPROCESSOR_DEFINITIONS的值了(最終目的就達到了)。

利用xcconfig配置Build Settings的方式比直接在項目Build Settings修改對應的屬性值要優雅的多, 英國的iOS大神Justin Spahr-Summers書寫的開源庫xcconfigs提供了一個類權威的模闆, 大家可以參考編寫以及學習使用xcconfig。

Object-C下配置的支援

在項目中的Info類目下, 大家可以配置Configuration對應的選項的xcconfig, 通過xcconfig來配置Build Setting中的參數(見下圖)。

iOS開發必備--環境變量配置(Debug & Release)

配置Configuration的各個xcconfig

PS: 如果大家對Cocoapods比較熟悉的話, 你會發現其實Pods也是通過xcconfig檔案去修改項目配置參數的。

Swift下配置的支援

這裡區分Object-C和Swift沒有太大的意義。隻不過因為C語言使用一些非常不安全的預處理器指令能力,Swift則隻使用預處理器指令的安全子集。是以預編譯頭參數在Swift并不會生效, 需要增加OTHER_SWIFT_FLAGS标記才能夠将Debug作用于Swift的條件式判斷。标記書寫方式參考下方示例:

1

OTHER_SWIFT_FLAGS = -D DEBUG

動态修改配置檔案

環境切換的标志位宏被提取到Build Setting中的GCC_PROCESSOR_DEFINTIONS有什麼好處呢?

  • 外部修改隻需要修改工程的project.pbxproj即可對GCC_PROCESSOR_DEFINTIONS參數進行操作修改
  • 可以通過xcconfig去配置參數, 而配有xcconfig的Configuration可以通過xcodebuild指令指定
  • 可以避免将最基礎的Debug和Release網絡環境切換書寫在代碼中

自動化腳本支援(便于自動化建構)

一個優秀的iOS工程師一定會使用自動化建構應用去解放自己的打包時間。《搭建自動化建構服務》講述了如何搭建一個自動化建構程式, 可以作為參考。

自動化建構的核心在于使用xcodebuild指令和各類腳本, 本文講述2個場景:

  • 場景1: 環境變量由宏定義并且書寫在項目的預編譯頭檔案中或者在預編譯頭檔案引用的.h檔案中;
  • 通過腳本動态替換行, 可以采用sed指令來替換, 最典型的執行個體如下:
1

sed -i 

''

's/^#define Debug 1/\/\/#define Debug 1/'

"./Demo/STSwitch.h"

場景2: 環境變量由宏定義但是配置GCC_PREPROCESSOR_DEFINITIONS編譯選項中。

如果GCC_PREPROCESSOR_DEFINITIONS由xcconfig檔案指定并配置對應的Configuration中, 直接通過xcodebuild指令指定-configuration參數來選擇。

如果GCC_PREPROCESSOR_DEFINITIONS需要在Build Settings中動态修改, 可以在Podfile中書寫Hook代碼或者用腳本解析配置檔案進行動态修改。

Cocoapods下支援

如果大家對Cocoapods比較熟悉的話, 就會知道每次執行完pod install之後, Cocoapods都會對每一個工程的Configuration配置一個xcconfig檔案。

預設情況下, 如果配置項已經存在了xcconfig檔案, Cocoapods是不會将生産的xcconfig檔案設定入配置項的。Cocoapods是通過xcconfig檔案去修改外部連結依賴的, 是以如果沒有正常替換配置檔案, 有肯能會導緻整個工程無法編譯通過(缺少依賴庫能通過才怪啦)。

解決方法

  • 如果自己修改的xcconfig檔案内容不多, 可以通過在Podfile中編寫hook去實作修改對應的項目參數, 參考示例如下:
1 2 3 4 5 6 7 8 9

post_install 

do

|installer|

installer.pods_project.targets.each 

do

|target|

target.build_configurations.each 

do

|config|

if

config.name == 

'Debug'

config.build_settings[

'GCC_PREPROCESSOR_DEFINITIONS'

] = 

'Debug=1'

end

end

end

end

  • 如果自己修改的xcconfig檔案内容較多, 可以在自己的編寫的xcconfig include Cocoapods生産的xcconfig檔案的方式進行處理, 參考示例如下:
1 2

// 自定義的xcconfig (例如: st.debug)

#include "../Pods.debug"

1 2

// 自定義的xcconfig (例如: st.release)

#include "../Pods.release"

多項目環境下支援

多項目的環境配置往往是比較麻煩的, 比如有B、C、D三個子工程, A工程引用了B、C、D三個子工程。怎麼把統一的環境變量怎麼應用到A、B、C、D三個工程裡呢?

  • 做法1: 建立一個公有引用的項目(無論Pods還是手動), 所有的項目均引用這個公有的項目, 公有的項目暴露一個頭檔案裡面定義了所有的環境變量。
  • 做法2: 每一個項目均維護自己的初始值, 通過外部的腳本一次性修改所有項目的初始值保持統一。
  • 做法3: 每一個項目維護自己的初始值, 通過上文描述的GCC編譯屬性或者xcconfig控制, 通過腳本或者Podfile控制每一個項目初始值
  • 做法4: 每個項目的維護自己的初始值, 但是所有環境變量動态維護, 在主工程的AppDelegate中加載A項目的初始值并通過接口指派給每一個子工程。(該方式宏定義智能作為初始值, 參考下文動态切換配置)

動态切換配置

文章前面所述均少了一個關鍵字初始值, 前面所添加的環境變量的方式都是在添加初始環境變量常量。

假設有一個營運或者測試需求, 需要能夠使用者自己去選擇網絡配置或者環境基礎變量, 按照文章前面描述的方法, 是無法實作的。 是以, 上述的方式都隻能提供一個初始預設值, 并無法在運作中去修改, 因為上述配置的方式都是通過預編譯去實作的。

在App運作時切換環境, 那配置參數都不能簡單的用宏或者常量來控制了, 需要講環境配置參數存儲在變量中, 通常是用NSUserDefault或者Singleton去維護環境變量集合。通過開發配置頁面對維護的變量進行動态的修改。(建議在Debug模式下開啟放置在系統的Setting界面下)

另外一種選擇

除了通過宏初始化, 是否還有其它的讀取配置檔案的方式初始化呢?

我們可以維護一個單例去管理所有的初始值, 在iOS應用開發中用屬性值去管理開發環境和生産環境(壞處很明顯, 控制力度沒有宏這麼大)。

資源檔案加載其實就是把參數寫在資源檔案中, 然後通過代碼在AppDelegate啟動的時候去加載初始值到全局維護的單例中, 然後在工程中到處使用單例的執行個體變量去判斷環境。

Cocoapods-keys

通過配置加載環境配置環境變量是否能夠做到工程依賴無關呢? 換言之就是在不同的安裝目錄或者不同的機器環境下配置不一樣的環境變量, 讓環境變量不與工程直接關聯而與工程所在目錄環境關聯起來呢?

做過服務端開發的童鞋們應該熟悉有一種config配置的方式, 讓config在外部注入, 而不是在開發工程中寫死, 即使寫死也隻是個初始值。

Cocoapods提供了一款插件Cocoapods-keys, 提供了外部注入鍵值對的功能, 通過Cocoapods-keys插件, 我們可以在工程中調用外部注入的鍵值對, 通過外部的鍵值對工程進行一定力度的控制。

Cocoapods-keys注入的鍵值對均存儲在~/.cocoapods/keys下, 是以yml的格式儲存的, yml中描述了已經添加的鍵值對和對應項目路徑。

總結

文章想表達的核心思想是将環境切換初始化提取到配置檔案處, 友善外部腳本修改(例如Podfile、自己寫的Bash Shell等等)。在多項目環境下, 配置檔案修改配置項更加容易可控, 防止多處修改代碼或者使用腳本動态修改代碼。

文章的作用是給我本人備忘用的哈~ 水準有限, 有錯誤支援請大家及時指出哈~

轉載請注明出處哦~

參考文章:

http://pewpewthespells.com/blog/xcconfig_guide.html

http://www.jianshu.com/p/44c82630bd50