iOS 多個(gè)環(huán)境一次打包

概述

偶然看到一個(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ù)。


20170717103707845.jpeg

這里我們新增一套環(huán)境:Configuration。在這一套包含了一些編譯參數(shù)的配置集合。如果此時(shí)項(xiàng)目里面有cocopods的話,打開(kāi)Configuration Set就會(huì)發(fā)現(xiàn)是如下的樣子。


20170717104056461.jpeg

然后打開(kāi)項(xiàng)目的pod文件,打開(kāi)配置是會(huì)看到如下信息:
20170717104217603.jpeg

說(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,可以忽略一下這一段。


20170717104400404.jpeg
2,新建Scheme

20170717104508168.jpeg

新建完成之后,我們就可以編輯剛剛新建的Scheme,我們可以把Run模式和Archive都改成新建Scheme。
20170717104623591.jpeg

注意:如果是使用了Git這些協(xié)同工具的同學(xué)這里還需要把剛剛新建的Scheme共享出去,否則其他人看不到這個(gè)Scheme。
20170717104714778.jpeg

3,新建User-defined Build Settings

回到Project的Build Settings里面來(lái),Add User-Defined Setting。

20170717105115264.jpeg

CustomAppBundleld是為了之后打包可以分開(kāi)打成多個(gè)包,這里需要3個(gè)不同的Id,建議是直接在原來(lái)的Bundleld加上Scheme的名字即可。CustomProductName為app顯示在手機(jī)上的名字,建議直接按環(huán)境給予描述,例如:測(cè)試(debug),線上(relase),UAT等。
20170717105322581.jpeg

需要注意的是: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。


20170717110144760.jpeg

配置好上述之后,就可以選擇不同環(huán)境運(yùn)行app了。可以在手機(jī)上生成不同的環(huán)境的app,可以同時(shí)安裝。


20170717110324703.jpeg

接下來(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ù)先制定不同定義的宏。

20170717110621421.jpeg

使用plist文件動(dòng)態(tài)配置環(huán)境變量

首先,新建3個(gè)名字一樣的plist作為3個(gè)環(huán)境的配置文件(新建三個(gè)配置文件,分別放在3個(gè)不同文件夾下面即可)。


20170717110945047.jpeg

接下來(lái)我們要做的是在編譯的時(shí)候(運(yùn)行app前),動(dòng)態(tài)的copy Configuration.plist到app里面,這里需要設(shè)置一個(gè)copy腳本。


20170717111122742.jpeg

進(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è)置一下。



20170717121203902.jpeg

接下來(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 。


20170717121647562.jpeg

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)配置。


20170717121849444.jpeg

除了上面的功能以為,還能利用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。


20170717140246032.png

一種方法是完全新建一個(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 IDBuild SettingsPackage分欄下的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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容