本篇文章主要講述了如何使用(Xcode配置文件xcconfig)去動(dòng)態(tài)配置開發(fā)或者生產(chǎn)網(wǎng)絡(luò)環(huán)境, 以及在多項(xiàng)目和運(yùn)行中如何切換環(huán)境坚嗜。
關(guān)于xcconfig文件, 目前在官方很難找到一篇專門的指南介紹, 但是國(guó)外有篇非官方指南《The Unofficial Guide to xcconfig files》詳細(xì)的介紹了xcconfig坏为。估計(jì)很多新入門的iOS開發(fā)對(duì)xcconfig文件都不是很熟悉, 但是大家可能都用過Cocoapods, 其實(shí)Cocoapods的項(xiàng)目配置管理很多都是依賴xcconfig文件去實(shí)現(xiàn)的陨囊。
Debug宏應(yīng)該在哪里定義?
iOS系統(tǒng)本身就區(qū)分了Configurations選項(xiàng)讓開發(fā)者去修改對(duì)應(yīng)的開發(fā)環(huán)境配置, 但是因?yàn)楹芏嚅_發(fā)者卻又在同一個(gè)Configuration環(huán)境中自定義了開發(fā)環(huán)境配置的開發(fā), 使得iOS系統(tǒng)本身的配置成為了擺設(shè), 僅僅用于區(qū)分打包方式選項(xiàng)和證書配置。
網(wǎng)絡(luò)環(huán)境切換是每一個(gè)互聯(lián)網(wǎng)App開發(fā)者都會(huì)頻繁用到的功能, 那么大家都是用什么樣的方式在切換環(huán)境的呢?我本人接觸的項(xiàng)目中最多的就是在預(yù)編譯頭文件里面寫一行宏定義, 然后根據(jù)宏定義去判斷當(dāng)前的環(huán)境剧辐。
<font color='orange'>最典型的例子</font>是在預(yù)編譯頭pch文件中添加一行代碼#define DEBUG 1
寒亥。然后通過這個(gè)DEBUG
參數(shù)去判斷當(dāng)前環(huán)境是否處于開發(fā)網(wǎng)絡(luò)或者生產(chǎn)網(wǎng)絡(luò)環(huán)境。
使用DEBUG
宏去判斷判斷開發(fā)環(huán)境還是生產(chǎn)環(huán)境沒有任何問題, 關(guān)鍵的問題是我們?cè)谑裁磿r(shí)候去定義這個(gè)宏和怎么去動(dòng)態(tài)配置這個(gè)宏荧关。
動(dòng)態(tài)配置不同的網(wǎng)絡(luò)開發(fā)環(huán)境
開發(fā)環(huán)境的切換在代碼中最實(shí)用的還是宏定義, 那么我們?cè)趺礃硬拍軌蜃尯甓x<font color='red'>動(dòng)態(tài)</font>可配置呢?
其中一種辦法就是使用GCC預(yù)編譯頭參數(shù)GCC_PREPROCESSOR_DEFINITIONS
溉奕。
通常我們可以在Project文件下的Build Settings對(duì)預(yù)編譯宏定義進(jìn)行默認(rèn)賦值。在Xcode6下的路徑為Build Settings
->Apple LLVM 6.x Preprocessing
->Preprocessor Macros
想必大家看這個(gè)宏的名字已經(jīng)知道它的作用了, 實(shí)際上就是和在pch頭文件中添加宏定義沒有太大的區(qū)別, 實(shí)際上還是有一些好處:
- Xcode的Project的Build Settings是由一個(gè)plist文件進(jìn)行描述的, plist本質(zhì)上是一個(gè)XML配置文件, 通過外部的腳本比較容易去修改忍啤。
- Preprocessor Macros可以按照Configuration選項(xiàng)進(jìn)行默認(rèn)配置, 也就是說可以根據(jù)不同的環(huán)境預(yù)先制定不同定義的宏
xcconfig配置Build Settings
Xcode Project的Build Settings屬性有很多, 如果每一個(gè)屬性都在配置項(xiàng)改過去比較麻煩, 而且容易忘記, 而且Build Settings用源碼的打開可閱讀性也不是很高, 這個(gè)時(shí)候, 我們可以使用xcconfig文件去配置Build Settings參數(shù)加勤。
xcconifg支持可以根據(jù)不同的Configuration選項(xiàng)配置不同的文件仙辟。不同的xcconfig可以指定不同的Build Settings里的屬性值, 這樣子我們就可以通過項(xiàng)目xcconifg去修改GCC_PREPROCESSOR_DEFINITIONS
的值了(最終目的就達(dá)到了)。
利用xcconfig配置Build Settings的方式比直接在項(xiàng)目Build Settings修改對(duì)應(yīng)的屬性值要優(yōu)雅的多, 英國(guó)的iOS大神Justin Spahr-Summers書寫的開源庫xcconfigs提供了一個(gè)類權(quán)威的模板, 大家可以參考編寫以及學(xué)習(xí)使用xcconfig鳄梅。
Object-C下配置的支持
在項(xiàng)目中的Info類目下, 大家可以配置Configuration對(duì)應(yīng)的選項(xiàng)的xcconfig, 通過xcconfig來配置Build Setting中的參數(shù)(見下圖)叠国。
PS: 如果大家對(duì)Cocoapods比較熟悉的話, 你會(huì)發(fā)現(xiàn)其實(shí)Pods也是通過xcconfig文件去修改項(xiàng)目配置參數(shù)的。
Swift下配置的支持
這里區(qū)分Object-C和Swift沒有太大的意義戴尸。只不過因?yàn)镃語言使用一些非常不安全的預(yù)處理器指令能力粟焊,Swift則只使用預(yù)處理器指令的安全子集。因此預(yù)編譯頭參數(shù)在Swift并不會(huì)生效, 需要增加OTHER_SWIFT_FLAGS
標(biāo)記才能夠?qū)?code>Debug作用于Swift的條件式判斷孙蒙。標(biāo)記書寫方式參考下方示例:
OTHER_SWIFT_FLAGS = -D DEBUG
動(dòng)態(tài)修改配置文件
環(huán)境切換的標(biāo)志位宏被提取到Build Setting中的GCC_PROCESSOR_DEFINTIONS
有什么好處呢?
- 外部修改只需要修改工程的
project.pbxproj
即可對(duì)GCC_PROCESSOR_DEFINTIONS
參數(shù)進(jìn)行操作修改 - 可以通過xcconfig去配置參數(shù), 而配有xcconfig的Configuration可以通過
xcodebuild
命令指定 - 可以避免將最基礎(chǔ)的Debug和Release網(wǎng)絡(luò)環(huán)境切換書寫在代碼中
自動(dòng)化腳本支持(便于自動(dòng)化構(gòu)建)
一個(gè)優(yōu)秀的iOS工程師一定會(huì)使用自動(dòng)化構(gòu)建應(yīng)用去解放自己的打包時(shí)間项棠。《搭建自動(dòng)化構(gòu)建服務(wù)》講述了如何搭建一個(gè)自動(dòng)化構(gòu)建程序, 可以作為參考。
自動(dòng)化構(gòu)建的核心在于使用xcodebuild命令和各類腳本, 本文講述2個(gè)場(chǎng)景:
- 場(chǎng)景1: 環(huán)境變量由宏定義并且書寫在項(xiàng)目的預(yù)編譯頭文件中或者在預(yù)編譯頭文件引用的
.h
文件中;- 通過腳本動(dòng)態(tài)替換行, 可以采用sed命令來替換, 最典型的實(shí)例如下:
sed -i '' 's/^#define Debug 1/\/\/#define Debug 1/' "./Demo/STSwitch.h"
- 場(chǎng)景2: 環(huán)境變量由宏定義但是配置在
GCC_PREPROCESSOR_DEFINITIONS
編譯選項(xiàng)中马篮。- 如果
GCC_PREPROCESSOR_DEFINITIONS
由xcconfig文件指定并配置對(duì)應(yīng)的Configuration中, 直接通過xcodebuild
命令指定-configuration
參數(shù)來選擇沾乘。 - 如果
GCC_PREPROCESSOR_DEFINITIONS
需要在Build Settings中動(dòng)態(tài)修改, 可以在Podfile中書寫Hook代碼或者用腳本解析配置文件進(jìn)行動(dòng)態(tài)修改。
- 如果
Cocoapods下支持
如果大家對(duì)Cocoapods比較熟悉的話, 就會(huì)知道每次執(zhí)行完pod install
之后, Cocoapods都會(huì)對(duì)每一個(gè)工程的Configuration配置一個(gè)xcconfig文件浑测。
默認(rèn)情況下, 如果配置項(xiàng)已經(jīng)存在了xcconfig文件, Cocoapods是不會(huì)將生產(chǎn)的xcconfig文件設(shè)置入配置項(xiàng)的翅阵。Cocoapods是通過xcconfig文件去修改外部鏈接依賴的, 因此如果沒有正常替換配置文件, 有肯能會(huì)導(dǎo)致整個(gè)工程無法編譯通過(缺少依賴庫能通過才怪啦)。
解決方法
- 如果自己修改的xcconfig文件內(nèi)容不多, 可以通過在Podfile中編寫hook去實(shí)現(xiàn)修改對(duì)應(yīng)的項(xiàng)目參數(shù), 參考示例如下:
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文件內(nèi)容較多, 可以在自己的編寫的xcconfig include Cocoapods生產(chǎn)的xcconfig文件的方式進(jìn)行處理, 參考示例如下:
// 自定義的xcconfig (例如: st.debug)
#include "../Pods.debug"
// 自定義的xcconfig (例如: st.release)
#include "../Pods.release"
多項(xiàng)目環(huán)境下支持
多項(xiàng)目的環(huán)境配置往往是比較麻煩的, 比如有B迁央、C掷匠、D三個(gè)子工程, A工程引用了B、C岖圈、D三個(gè)子工程讹语。怎么把統(tǒng)一的環(huán)境變量怎么應(yīng)用到A、B蜂科、C顽决、D三個(gè)工程里呢?
- 做法1: 建立一個(gè)公有引用的項(xiàng)目(無論P(yáng)ods還是手動(dòng)), 所有的項(xiàng)目均引用這個(gè)公有的項(xiàng)目, 公有的項(xiàng)目暴露一個(gè)頭文件里面定義了所有的環(huán)境變量。
- 做法2: 每一個(gè)項(xiàng)目均維護(hù)自己的初始值, 通過外部的腳本一次性修改所有項(xiàng)目的初始值保持統(tǒng)一导匣。
- 做法3: 每一個(gè)項(xiàng)目維護(hù)自己的初始值, 通過上文描述的GCC編譯屬性或者xcconfig控制, 通過腳本或者Podfile控制每一個(gè)項(xiàng)目初始值
- 做法4: 每個(gè)項(xiàng)目的維護(hù)自己的初始值, 但是所有環(huán)境變量動(dòng)態(tài)維護(hù), 在主工程的AppDelegate中加載A項(xiàng)目的初始值并通過接口賦值給每一個(gè)子工程才菠。(該方式宏定義智能作為初始值, 參考下文
動(dòng)態(tài)切換配置
)
動(dòng)態(tài)切換配置
文章前面所述均少了一個(gè)關(guān)鍵字初始值
, 前面所添加的環(huán)境變量的方式都是在添加初始環(huán)境變量常量
。
假設(shè)有一個(gè)運(yùn)營(yíng)或者測(cè)試需求, 需要能夠用戶自己去選擇網(wǎng)絡(luò)配置或者環(huán)境基礎(chǔ)變量, 按照文章前面描述的方法, 是無法實(shí)現(xiàn)的贡定。 因此, 上述的方式都只能提供一個(gè)初始默認(rèn)值, 并無法在運(yùn)行中去修改, 因?yàn)樯鲜雠渲玫姆绞蕉际峭ㄟ^預(yù)編譯去實(shí)現(xiàn)的赋访。
在App運(yùn)行時(shí)切換環(huán)境, 那配置參數(shù)都不能簡(jiǎn)單的用宏或者常量來控制了, 需要講環(huán)境配置參數(shù)存儲(chǔ)在變量中, 通常是用NSUserDefault
或者Singleton
去維護(hù)環(huán)境變量集合。通過開發(fā)配置頁面對(duì)維護(hù)的變量進(jìn)行動(dòng)態(tài)的修改缓待。(建議在Debug模式下開啟放置在系統(tǒng)的Setting界面下)
另外一種選擇
除了通過宏初始化, 是否還有其它的讀取配置文件的方式初始化呢?
我們可以維護(hù)一個(gè)單例去管理所有的初始值, 在iOS應(yīng)用開發(fā)中用屬性值去管理開發(fā)環(huán)境和生產(chǎn)環(huán)境(壞處很明顯, 控制力度沒有宏
這么大)蚓耽。
資源文件加載其實(shí)就是把參數(shù)寫在資源文件中, 然后通過代碼在AppDelegate啟動(dòng)的時(shí)候去加載初始值到全局維護(hù)的單例中, 然后在工程中到處使用單例的實(shí)例變量去判斷環(huán)境。
Cocoapods-keys
通過配置加載環(huán)境配置環(huán)境變量是否能夠做到工程依賴無關(guān)呢? 換言之就是在不同的安裝目錄或者不同的機(jī)器環(huán)境下配置不一樣的環(huán)境變量, 讓環(huán)境變量不與工程直接關(guān)聯(lián)而與工程所在目錄環(huán)境關(guān)聯(lián)起來呢?
做過服務(wù)端開發(fā)的童鞋們應(yīng)該熟悉有一種config配置的方式, 讓config在外部注入, 而不是在開發(fā)工程中寫死, 即使寫死也只是個(gè)初始值旋炒。
Cocoapods提供了一款插件Cocoapods-keys, 提供了外部注入鍵值對(duì)的功能, 通過Cocoapods-keys插件, 我們可以在工程中調(diào)用外部注入的鍵值對(duì), 通過外部的鍵值對(duì)工程進(jìn)行一定力度的控制步悠。
Cocoapods-keys注入的鍵值對(duì)均存儲(chǔ)在~/.cocoapods/keys
下, 是以yml的格式保存的, yml中描述了已經(jīng)添加的鍵值對(duì)和對(duì)應(yīng)項(xiàng)目路徑。
總結(jié)
文章想表達(dá)的核心思想是將環(huán)境切換初始化提取到配置文件處, 方便外部腳本修改(例如Podfile瘫镇、自己寫的Bash Shell等等)鼎兽。在多項(xiàng)目環(huán)境下, 配置文件修改配置項(xiàng)更加容易可控, 防止多處修改代碼或者使用腳本動(dòng)態(tài)修改代碼芹壕。
文章的作用是給我本人備忘用的哈~ 水平有限, 有錯(cuò)誤支持請(qǐng)大家及時(shí)指出哈~
轉(zhuǎn)載請(qǐng)注明出處哦~
參考文章: