實際使用場景:
- debug模式和release模式的一些配置不一樣,比如開發(fā)環(huán)境訪問的后臺服務(wù)器和線上版本訪問的服務(wù)器地址不一樣。
- 發(fā)布開發(fā)版程序AppIcon不同,包含搶鮮版或者體驗版的特有圖標(biāo)
通??梢詣?chuàng)建一個變量,保存開發(fā)版服務(wù)器地址,然后再復(fù)制創(chuàng)建同一個變量名保存線上版服務(wù)器地址。開發(fā)或者發(fā)布的時候根據(jù)需要,將需要的打開,不需要的注釋。這樣來回切換,達(dá)到匹配相應(yīng)服務(wù)器的結(jié)果。這種方式雖然可以解決問題,但是不夠優(yōu)雅,稍微一個不留神,就會出錯,如果不同的環(huán)境有多個不同的參數(shù),那更得小心翼翼,提心吊膽的感覺是真難受。
那么如何優(yōu)雅的解決這個問題?
這里提供3種方式:
- 通過多target的方式
- 通過配置變量configurations,Scheme環(huán)境變量的配置達(dá)到多環(huán)境的目的
- xcconfig文件,通過xcconfig的形式將對應(yīng)的配置寫到相應(yīng)的文件里面,通過文件管理,更清晰一些,改動的幅度更小一些,同時配合git,將一些文件隱藏起來
隨便打開一個項目代碼,可能打開的是一個project或者一個workspace。那么project在我們開發(fā)中,在我們xcode中到底是什么東西?在Scheme中選擇環(huán)境debug或者release又是代表什么意思呢?
先介紹一下xcode幾個常見的名詞
- Project:包含了項目所有的代碼,資源文件,所有信息。
- Target:對指定代碼和資源文件的具體構(gòu)建方式。
- Scheme:對指定Target的環(huán)境配置。
- xcconfig:將配置寫到對應(yīng)的文件中
- workSpace:是多個project的合集。
所有的開發(fā)都是面向project,它是一個大的集合,包含了所有內(nèi)容。Target指定某些代碼來如何生成IPA包,Scheme作為一個監(jiān)控,通過它來配置你編譯時候的環(huán)境變量。Scheme中可以選擇決定target中是debug起作用還是release起作用。
第一部分 通過多Target實現(xiàn)多環(huán)境




復(fù)制出的.plist文件是絕對路徑。而且包含copy,這顯然要做修改。



對于有強(qiáng)迫癥的我,還做了兩步:將plist文件的路徑改為相對路徑。修改Scheme的名稱,帶copy字樣太難看了。



準(zhǔn)備工作就緒。通過選擇不同的Scheme,運(yùn)行不同的target,在手機(jī)上會生成兩個App,而且displayName和AppIcon已經(jīng)變了。目前是兩個target操作同一份代碼。如果想在Dev模式下執(zhí)行不同的代碼,應(yīng)該怎么做?可以通過宏定義,預(yù)先定義宏。
OC中可以在 target 的 Preprocessor Macros 里設(shè)置。preprocessor macros的意思就是 預(yù)處理器宏。




OC下的多環(huán)境配置通過多Target的方式處理完畢。
如果是Swift項目呢,或者是OC和Swift的混編項目要怎么通過多Target的方式處理呢?
在剛才項目中新建一個swift文件,讓xcode給我們自動創(chuàng)建一個橋接器。新建兩個Swift的UIViewController,分別命名為SecondViewController和ThirdViewController,現(xiàn)在想讓FirstDemo的viewdidload進(jìn)入SecondViewController,F(xiàn)irstDemo-Dev的viewdidload進(jìn)入ThirdViewController。



檢查Product Module Name是否為工程名FirstDemo-Dev


到這里就可以實現(xiàn),運(yùn)行不同的target進(jìn)入到不同的ViewController。


選中 target --> Build Settings --> 搜索 Swift Compiler - Custom Flags
展開 Active Compilation Conditions 進(jìn)行設(shè)置,只能寫名稱:TEST, 不能像OC一樣設(shè)置TEST=1
展開 Other Swift Flags 同樣可以設(shè)置 TEST,需要這樣寫:-D TEST,填完后是 -D 和 TEST 分成兩行的,(如果項目中用到了 CocoaPods 可以參考它也是這樣的)
-D的含義:就是將聲明的變量設(shè)置為TRUE。
如果是 OC 和 Swift 混編的項目,OC也需要用到,則還是在 Preprocessor Macros 里添加一遍。也就是說OC中的預(yù)定義宏和Swift中是完全分開,無法共用的。

總結(jié):1.步驟太多,操作繁瑣2.存在多個plist文件,預(yù)定義宏變量相互對應(yīng)關(guān)系復(fù)雜,稍有不慎,便會弄錯
第二部分 通過Scheme實現(xiàn)多環(huán)境
Scheme是什么?就是控制環(huán)境變量,讓項目中哪個變量起作用。常見的有debug和release。






Target就是往編譯的時候添加參數(shù),指明編譯的方式,告訴編譯器代碼怎么編譯成相應(yīng)的結(jié)果。并不是控制或者改變一些什么東西。

在Scheme中切換到哪一種,就會使用Build Settings中對應(yīng)的相關(guān)設(shè)置。Scheme的設(shè)置是全局的Configuration的設(shè)置。
有沒有一種方式不用去手動切換Scheme中的3個Beta,Debug和Release?




這樣有什么好處?針對先前開發(fā)環(huán)境和發(fā)布環(huán)境不同的服務(wù)器地址。我們可以有新的辦法。


Host_URL變量的根據(jù)需要已經(jīng)設(shè)置成了我們想要的狀態(tài),那么如何去讀取到這個值?我們可以在info.plist中,設(shè)置一個變量進(jìn)行讀取,這樣來達(dá)到暴露出來供程序使用的目的。

NSString *path = [NSBundle.mainBundle pathForResource:@"Info" ofType:@"plist"];
NSDictionary *infoDic = [[NSDictionary alloc] initWithContentsOfFile:path];
NSLog(@"%@",infoDic[@"HOST_URL"]);

這樣就達(dá)到了多環(huán)境的目的。
總結(jié):比上面多target方式要好,不用創(chuàng)建多target,只需要在一個Build Setting里面就能配置。有多少個環(huán)境,就可以創(chuàng)建多少個Configuration。但還是有個弊端,還是必須要在Build Setting中進(jìn)行相關(guān)的配置。
我們正常的一個項目,通過Cocoapods進(jìn)行引用的時候,對應(yīng)的我們的target,Build Setting中Other Link Flags,Path to Link Map File,Framework Search Paths等等都是有參數(shù)的,如果使用Scheme這種方式,我們還是要在Build Setting中不停的配置。
那么能否通過一個文件的方式,來對整個Build Setting中我們想做的修改,做統(tǒng)一的管理呢?放在一起,放在一個文件。
第三部分 xcconfig文件管理Build Setting多環(huán)境配置
Cocoapods中使用的就是xcconfig文件,當(dāng)我們通過Cocoapods導(dǎo)入一個項目的時候,Cocoapods通常會給我們生成兩個文件。一個為“項目名.debug.xcconfig”,一個為“項目名.release.xcconfig”。實際上是根據(jù)Configuration中的配置生成的。



xcconfig的本質(zhì)是一個Key-Value形式。
創(chuàng)建我們自己的XCConfig文件
注意:以保存的目錄作為開頭XCConfig,-拼上APP的名稱FirstDemo,拼上環(huán)境debug。



準(zhǔn)備工作完成,如何實現(xiàn)多環(huán)境配置呢?
在xcconfig中開始寫我們需要做區(qū)分的內(nèi)容。比如添加一個端口號,在debug.xcconfig中加入PORT = 8080,在release.xcconfig中假如PORT = 5001。
一樣需要在info.plist中暴露出來。

編譯過后,再觀察Build Setting。

xcconfig寫入的規(guī)則是什么?與Build Setting中什么字段相對應(yīng)??梢栽谔O果官方提供的資料里面查。Xcode Build Settings

總結(jié):完美的實現(xiàn)在xcconfig文件中對Build Setting中參數(shù)的設(shè)置。
額外部分
1.在自己創(chuàng)建的xcconfig中引入pod生成的xcconfig文件:
#include "Pods/Target Support Files/Pods-FirstDemo/Pods-FirstDemo.debug.xcconfig"

啊哈,在這里運(yùn)行的時候出現(xiàn)一個錯誤:
dyld: Library not loaded: @rpath/AFNetworking.framework/AFNetworking
Referenced from: /private/var/containers/Bundle/Application/F4E7F2A4-56BA-4E73-AA37-8349B2791333/FirstDemo-Dev.app/FirstDemo-Dev
Reason: image not found
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
這里先不理會,但是證明了一個事情,那就是加載pod的xcconfig是起效的。
2.xcconfig文件沖突解決
自己創(chuàng)建的xcconfig文件中存在pod生成的xcconfig文件中相同的字段,那么就會覆蓋pod中的字段,因此要想兩個文件中的字段都生效需要使用繼承標(biāo)識:
$(inherited)

3.URL變量中存在//,后面的字符串會被當(dāng)做注釋
// 通過定義一個變量來解決
SLASH =/
HOST_URL = http:${SLASH}/192.168.1.100
4.條件設(shè)置
OTHER_LDFLAGS[config=Debug][sdk=iphonesimulator*][arch=x86_64] = $(inherited) -framework "AFNetworking"
經(jīng)過上述條件設(shè)置后AFNetworking庫僅會在Debug、模擬器、X86_64架構(gòu)下編譯
[sdk=iphoneos*]表示真機(jī)