概述
偶然看到一個(gè)很有趣的問(wèn)題:如何在ios環(huán)境下實(shí)現(xiàn)多個(gè)環(huán)境同時(shí)打包。
談到多環(huán)境,我想大多公司都至少有2-3個(gè)環(huán)境,比如Test環(huán)境,UAT(User Acceptance Test)用戶驗(yàn)收測(cè)試環(huán)境,Release環(huán)境等等。當(dāng)需要開(kāi)發(fā)打多個(gè)包的時(shí)候,一般常見(jiàn)做法就是直接代碼里面修改環(huán)境變量,改完之后Archive一下就打包了。或者在App中內(nèi)置一個(gè)切換的按鈕,實(shí)現(xiàn)環(huán)境的切換。
或者,你們公司已經(jīng)搭建了Jenkins環(huán)境,利用Jenkins環(huán)境就可以給app來(lái)配置一下多個(gè)環(huán)境變量,之后Jenkins分別再不同環(huán)境下自動(dòng)集成即可。
那么如何實(shí)現(xiàn)ios的多環(huán)境打包呢?參照網(wǎng)上的一些方案這里做一個(gè)簡(jiǎn)單的總結(jié)。要實(shí)現(xiàn)多環(huán)境打包,現(xiàn)在主流的方案有三種(各有優(yōu)劣)。
1.利用Build Configuration來(lái)配置多環(huán)境
2.利用xcconfig文件來(lái)配置多環(huán)境
3.利用Targets來(lái)配置多環(huán)境
一、Build Configuration方式
做過(guò)ios開(kāi)發(fā)的人都知道Build Configuration,系統(tǒng)默認(rèn)提供2個(gè)環(huán)境,一個(gè)Debug,一個(gè)Release。這里,我們利用的就是Build Configuration給我們提供的配置功能來(lái)實(shí)現(xiàn)多環(huán)境打包。具體步驟如下:
1,新建Build Configuration
點(diǎn)擊Project里面找到Configuration,然后選擇添加一個(gè)Configuration,系統(tǒng)默認(rèn)是2個(gè),一個(gè)Debug,一個(gè)Release。這里我們需要選擇是復(fù)制一個(gè)Debug還是Release。Release是不能調(diào)試程序,因?yàn)槟J(rèn)是屏蔽了可調(diào)試的一些參數(shù)。
這里我們新增一套環(huán)境:Configuration。在這一套包含了一些編譯參數(shù)的配置集合。如果此時(shí)項(xiàng)目里面有cocopods的話,打開(kāi)Configuration Set就會(huì)發(fā)現(xiàn)是如下的樣子。
然后打開(kāi)項(xiàng)目的pod文件,打開(kāi)配置是會(huì)看到如下信息:
說(shuō)明:pod安裝完成之后會(huì)自動(dòng)生成xcconfig文件,如果你手動(dòng)新建這個(gè)xcconfig,然后把原來(lái)的debug和release對(duì)應(yīng)的pod xcconfig文件內(nèi)容復(fù)制進(jìn)來(lái),這樣做是無(wú)效的,需要pod自己去生成xcconfig文件才能被識(shí)別到。
新建完Build Configuration,這個(gè)時(shí)候需要新建pod里面對(duì)應(yīng)的Build Configuration,要不然一會(huì)編譯會(huì)報(bào)錯(cuò)。如果沒(méi)用pod,可以忽略一下這一段。
2,新建Scheme
新建完成之后,我們就可以編輯剛剛新建的Scheme,我們可以把Run模式和Archive都改成新建Scheme。
注意:如果是使用了Git這些協(xié)同工具的同學(xué)這里還需要把剛剛新建的Scheme共享出去,否則其他人看不到這個(gè)Scheme。
3,新建User-defined Build Settings
回到Project的Build Settings里面來(lái),Add User-Defined Setting。
CustomAppBundleld是為了之后打包可以分開(kāi)打成多個(gè)包,這里需要3個(gè)不同的Id,建議是直接在原來(lái)的Bundleld加上Scheme的名字即可。CustomProductName為app顯示在手機(jī)上的名字,建議直接按環(huán)境給予描述,例如:測(cè)試(debug),線上(relase),UAT等。
需要注意的是:Pods的Build_DIR這些目錄其實(shí)是Pods自己生成好的,之前執(zhí)行過(guò)Pod install 之后,這里默認(rèn)都是配置好的,不需要再改動(dòng)了。
4,修改info.plist文件 和 Images.xcassets
打開(kāi)info.plist文件。由于我們新添加了2個(gè)CustomAppBundleld 和 CustomProductName,這里我們需要把info.plist里面的Bundle display name修改成我們自定義的這個(gè)字典。編譯過(guò)程中,編譯器會(huì)根據(jù)我們?cè)O(shè)置好的Scheme去自己選擇Debug,Release,TestRelease分別對(duì)應(yīng)的ProductName。
為了方便區(qū)分不同的環(huán)境,你還可以對(duì)不同環(huán)境下App Icon,名字等做一個(gè)修改。
既然我們已經(jīng)新建了這幾個(gè)scheme,那接下來(lái)怎么把他們都打包成app呢?
這里分享下實(shí)際打包過(guò)程中的一些經(jīng)驗(yàn)。
每個(gè)環(huán)境都要設(shè)置好Debug 和 Release!!!
千萬(wàn)別認(rèn)為線上的版本只設(shè)置Release就好,哪天需要調(diào)試線上版本,沒(méi)有設(shè)置Debug就無(wú)從下手了。也千萬(wàn)別認(rèn)為測(cè)試環(huán)境的版本只要設(shè)置Debug就好,萬(wàn)一哪天要發(fā)布一個(gè)測(cè)試環(huán)境需要發(fā)Release包,那又無(wú)從下手了。我的建議就是每個(gè)環(huán)境都配置Debug 和 Release。
在打包的時(shí)候,一定要注意將Scheme的名字和編譯方式區(qū)分開(kāi)。選擇一個(gè)Scheme,只是相當(dāng)于選擇了一個(gè)環(huán)境,并不是代表這Debug還是Release。
配置好上述之后,就可以選擇不同環(huán)境運(yùn)行app了。可以在手機(jī)上生成不同的環(huán)境的app,可以同時(shí)安裝。
接下來(lái)說(shuō)幾種動(dòng)態(tài)配置環(huán)境變量的方法。
動(dòng)態(tài)配置環(huán)境變量
使用GCC預(yù)編譯頭參數(shù)GCC_PREPROCESSOR_DEFINITIONS
進(jìn)入到Build Settings里面,可以找到Apple LLVM Preprocessing,找到Preprocessor Macros,在這里我們是可以加一些環(huán)境變量的宏定義來(lái)標(biāo)識(shí)符。Preprocessor Macros可以根據(jù)不同的環(huán)境預(yù)先制定不同定義的宏。
使用plist文件動(dòng)態(tài)配置環(huán)境變量
首先,新建3個(gè)名字一樣的plist作為3個(gè)環(huán)境的配置文件(新建三個(gè)配置文件,分別放在3個(gè)不同文件夾下面即可)。
接下來(lái)我們要做的是在編譯的時(shí)候(運(yùn)行app前),動(dòng)態(tài)的copy Configuration.plist到app里面,這里需要設(shè)置一個(gè)copy腳本。
進(jìn)入到我們的Target里面,找到Build Phases,我們新建一個(gè)New Copy Files Phase,并且重命名為Copy Configuration Files。其中,腳本能保證我們的Configuration.plist 文件可以在編譯的時(shí)候,選擇其中一個(gè)打包進(jìn)入app。再通過(guò)讀取這個(gè)plist里面的信息就可以做到動(dòng)態(tài)化。
使用單例來(lái)處理環(huán)境切換
當(dāng)然使用一個(gè)單例也可以做到環(huán)境切換。新建一個(gè)單例,然后可以在設(shè)置菜單里面加入一個(gè)列表,里面列出所有的環(huán)境,然后用戶選擇以后,單例就初始化用戶所選的環(huán)境。
二、利用xcconfig文件配置多環(huán)境
提到xcconfig,就要先說(shuō)說(shuō)幾個(gè)概念。Xcode Workspace、Xcode Scheme、Xcode Project、Xcode Target、Build Settings 。
Xcode Project
project就是一個(gè)個(gè)的倉(cāng)庫(kù),里面會(huì)包含屬于這個(gè)項(xiàng)目的所有文件,資源,以及生成一個(gè)或者多個(gè)軟件產(chǎn)品的信息。每一個(gè)project會(huì)包含一個(gè)或者多個(gè) targets,而每一個(gè) target 告訴我們?nèi)绾紊a(chǎn) products。project 會(huì)為所有 targets 定義了默認(rèn)的 build settings,每一個(gè) target 也能自定義自己的 build settings,且 target 的 build settings 會(huì)重寫(xiě) project 的 build settings。
Xcode Project 文件會(huì)包含以下信息,對(duì)資源文件的引用(源碼.h和.m文件,frame,資源文件plist,bundle文件等,圖片文件image.xcassets還有Interface Builder(nib),storyboard文件)、文件結(jié)構(gòu)導(dǎo)航中用來(lái)組織源文件的組、Project-level build configurations(Debug\Release)、Targets、可執(zhí)行環(huán)境,該環(huán)境用于調(diào)試或者測(cè)試程序。
Xcode Target
target 會(huì)有且唯一生成一個(gè) product, 它將構(gòu)建該 product 所需的文件和處理這些文件所需的指令集整合進(jìn) build system 中。Projects 會(huì)包含一個(gè)或者多個(gè) targets,每一個(gè) target 將會(huì)產(chǎn)出一個(gè) product。需要注意的是, Project 可以包含多個(gè) target, 但是在同一時(shí)刻,只會(huì)有一個(gè) target 生效,可用 Xcode 的 scheme 來(lái)指定是哪一個(gè) target 生效。
Build Settings
build setting 中包含了 product 生成過(guò)程中所需的參數(shù)信息。project的build settings會(huì)對(duì)于整個(gè)project 中的所有targets生效,而target的build settings是重寫(xiě)了Project的build settings,重寫(xiě)的配置以target為準(zhǔn)。
一個(gè) build configaration 指定了一套 build settings 用于生成某一 target 的 product,例如Debug和Release就屬于build configaration。
Xcode Scheme
一個(gè)Scheme就包含了一套targets(這些targets之間可能有依賴關(guān)系),一個(gè)configuration,一套待執(zhí)行的tests。
xcconfig使用
1,新建一個(gè)xcconfig文件,然后在project設(shè)置一下。
接下來(lái),把配置文件換成我們剛剛新建的文件。細(xì)心的讀者可能發(fā)現(xiàn),其實(shí)我們一直使用的cocopods就是用這個(gè)文件來(lái)配置編譯參數(shù)的。
GCC_PREPROCESSOR_DEFINITIONS 是 GCC 預(yù)編譯頭參數(shù),通常我們可以在 Project 文件下的 Build Settings 對(duì)預(yù)編譯宏定義進(jìn)行默認(rèn)賦值。
在Build Settings里面的 Apple LLVM 7.X - Preprocessing - Preprocessor Macros 。
Preprocessor Macros 其實(shí)是按照 Configuration 選項(xiàng)進(jìn)行默認(rèn)配置的, 它是可以根據(jù)不同的環(huán)境預(yù)先制定不同定義的宏,或者為不同環(huán)境下的相同變量定義不同的值。
在xcconfig 我們可以寫(xiě)入不同的 Configuration 選項(xiàng)配置不同的文件。每一個(gè) xcconfig 可以配置 Build Settings 里的屬性值, 其實(shí)實(shí)質(zhì)就是通過(guò) xcconfig 去修改 GCC_PREPROCESSOR_DEFINITIONS 的值,這樣我們就可以做到動(dòng)態(tài)配置環(huán)境的需求了。
現(xiàn)在本地有這么多配置,到底哪一個(gè)最終生效呢?打開(kāi)Build 里面的level,優(yōu)先級(jí)是從左往右,依次降低的。Resolved = target-level > project-level > 自定義配置文件 > iOS 默認(rèn)配置。
除了上面的功能以為,還能利用xcconfig動(dòng)態(tài)配置Build Settings里面的很多參數(shù)。這其實(shí)類似于cocopods的做法。詳情資料請(qǐng)大家自行搜索。
三、利用Targets配置多環(huán)境
其實(shí)使用Scheme和xcconfig就可以實(shí)現(xiàn)多環(huán)境的功能,使用Targets反而顯得更加麻煩。利用Targets可以瞬間大批量產(chǎn)生大量的app。相關(guān)使用可以查看:使用多Target來(lái)構(gòu)建大量相似App
僅僅只用一套代碼,就可以生產(chǎn)出7個(gè)app。7個(gè)app的證書(shū)都是不同的,配置也都不同,但是代碼只需要維護(hù)一套代碼,就可以完成維護(hù)7個(gè)app的目標(biāo)。
下面來(lái)看一下如何使用Targets來(lái)生成不同的app。
一種方法是完全新建一個(gè)Targets,另外一種方法是復(fù)制原有的Targets。
其實(shí)第一種方法建立出Targets,之后看你需求是怎么樣的。如果也想是做OEM這種,可以把新建出來(lái)的project刪掉,本地還是維護(hù)一套代碼,然后在新建的Targets 的Build Phases里面去把本地現(xiàn)有代碼加上,參數(shù)自己可以隨意配置。這樣也是一套代碼維護(hù)多個(gè)app。
第二種方法就是復(fù)制一個(gè)原有的Targets,這種做法只用自己去改參數(shù)就可以了。
再來(lái)說(shuō)說(shuō)Targets的參數(shù)。
由于我們新建了Targets,相當(dāng)于新建了一個(gè)app了。所以里面的所有的文件全部都可以更改。包括info.plist,源碼引用,Build Settings……所有參數(shù)都可以改,這樣就不僅僅局限于修改Scheme和xcconfig,所以之前說(shuō)僅僅配置一個(gè)多環(huán)境用Targets有點(diǎn)興師動(dòng)眾,但是它確實(shí)能完成目的。根據(jù)第二章里面我們也提到了,Targets相當(dāng)于流水線,僅次于Project的地位,可以想象,有了Targets,我們沒(méi)有什么不能修改的。
注意點(diǎn)
在開(kāi)發(fā)過(guò)程中遇到比較坑爹的問(wèn)題:證書(shū)不使用xcode的Automatically manage signing
的時(shí)候,(環(huán)境不同bundle ID
不同的時(shí)候)每個(gè)版本選擇不同的描述文件,需要注意的地方 bundle ID
依Build Settings
中Package
分欄下的Product Bundle Identifier
的為準(zhǔn),要不然選擇描述文件的時(shí)候有問(wèn)題,就會(huì)提示
The provisioning profile specified in your build settings ("xxx") has an AppID of "com.xxx" which does not match your bundle identifier "com.xxx"
http://blog.csdn.net/l2i2j2/article/details/51661443