前言
在開始之前钉蒲,先問幾個問題,在測試的時候彻坛,App 一般需要連接測試服務(wù)器顷啼,那么在上架后踏枣,還需要連生產(chǎn)服務(wù)器嗎?在發(fā)布前钙蒙,App 需要通過 Ad-hoc 分發(fā)給內(nèi)部測試組嗎茵瀑?在發(fā)布到 App Store 的時候,App 需要同時支持免費版和收費版嗎仪搔?
如果回答是“是”瘾婿,那么App 就需要搭建多環(huán)境支持蜻牢,優(yōu)化開發(fā)的工作流程烤咧。多環(huán)境提供很多好處,比如能基于同一套源代碼自動構(gòu)建出有差異功能的 App抢呆;能支持多個團隊并行開發(fā)煮嫌,也能分離測試和生產(chǎn)環(huán)境,提高產(chǎn)品的迭代速度抱虐,保證上架的 App 通過嚴格測試和功能驗證昌阿。
在 App 項目中,一般使用了三個不同的環(huán)境恳邀,分別是開發(fā)環(huán)境懦冰,測試環(huán)境和生產(chǎn)環(huán)境。它們到底有什么區(qū)別呢谣沸?
- 開發(fā)環(huán)境刷钢,用于日常的開發(fā),一般有未完成的功能模塊乳附。編譯時内地,也不進行任何優(yōu)化,可以打印更多的日志赋除,幫助開發(fā)者快速定位問題阱缓。
- 測試環(huán)境,主要是用于測試举农,以及為產(chǎn)品經(jīng)理進行功能驗證荆针,包括部分完成的功能模塊,也提供一些隱藏功能颁糟,方便進行開發(fā)和迭代航背,例如快速切換用戶,清理 Cache滚停,連接到不同后臺服務(wù)器等等沃粗。
- 生產(chǎn)環(huán)境,只包含通過了測試并驗證過的功能模塊键畴,它是最終提交到 App Store 供終端用戶使用的版本最盅。
多環(huán)境支持需要用到 Xcode 的構(gòu)建配置突雪,下面會一一闡述。
Xcode 構(gòu)建基礎(chǔ)概念
一般在構(gòu)建一個 iOS App 的時候涡贱,需要用到 Xcode Project咏删,Xcode Target,Build Settings问词,Build Configuration 和 Xcode Scheme 等構(gòu)建配置督函。它們各有什么用呢?
Xcode Project
Xcode Project用于組織源代碼文件和資源文件激挪。一個 Project 可以包含多個 Target辰狡,例如當我們新建一個 Xcode Project 的時候,它會自動生成 App 的主 Target垄分,Unit Test Target 和 UI Test Target宛篇。
例如在 Moments App 項目中,主 Target 就是 Moments薄湿,Unit Test Target 是 MomentsTests叫倍, UI Test Target 就是 MomentsUITests。
Xcode Target
Xcode Target用來定義如何構(gòu)建出一個產(chǎn)品(例如 App豺瘤, Extension 或者 Framework)吆倦,Target 可以指定需要編譯的源代碼文件和需要打包的資源文件,以及構(gòu)建過程中的步驟坐求。
例如在 Moments App 項目中蚕泽,負責單元測試的MomentsTestsTarget 就指定了 14 個測試文件需要構(gòu)建(見下圖的 Compile Sources),并且該 Target 依賴了主 App TargetMoments(見下圖的 Dependencies)瞻赶。
有了 Target 的定義赛糟,構(gòu)建系統(tǒng)就可以讀取相關(guān)的源代碼文件進行編譯,然后把相關(guān)的資源文件進行打包砸逊,并嚴格按照 Target 所指定的設(shè)置和步驟執(zhí)行璧南。那么 Target 所指定的設(shè)置哪里來的呢?來自 Build Settings师逸。
Build Settings
Build Setting保存了構(gòu)建過程中需要用到的信息司倚,它以一個變量的形式而存在,例如所支持的設(shè)備平臺篓像,或者支持操作系統(tǒng)的最低版本等动知。
通常,一條 Build Setting 信息由兩部分組成:名字和值员辩。比如下面是一條 Setting 信息盒粮,iOS Development Target是名字,而iOS 14.0是值奠滑。
有了這些基礎(chǔ)以后丹皱,接下來就結(jié)合 Moments App 來介紹如何進行多環(huán)境配置妒穴,從而生成不同環(huán)境版本的 App。
Moments App 構(gòu)建配置
一般用 Xcode 編譯出不同環(huán)境版本的 App 有多種辦法摊崭,例如拷貝復制所有源代碼讼油,建立多個 Target 來包含不同的源碼文件等等。不過呢簸,推薦使用 Build Configuration 和 Xcode Scheme 來管理多環(huán)境矮台,進而構(gòu)建出不同環(huán)境版本的 App。為什么根时?因為這兩個是目前管理成本最低的辦法瘦赫。一一介紹下。
Build Configuration
當在 Xcode 上新建一個項目的時候啸箫,Xcode 會自動生成兩個 Configuration:Debug和Release耸彪。Debug 用于日常的本地開發(fā)伞芹,Release 用于構(gòu)建和分發(fā) App忘苛。在 Moments App 項目中,有三個 configuration:Debug唱较,Internal 和 AppStore扎唾。它們分別用于構(gòu)建開發(fā)環(huán)境、測試環(huán)境和生產(chǎn)環(huán)境南缓。 其中 Internal 和 AppStore 是從自動生成的 Release 拷貝而來的胸遇。
那什么是 Build Configuration 呢?
Build Configuration就是一組 Build Setting汉形。 我們可以通過 Build Configuration 來分組和管理不同組合的 Build Setting 集合纸镊,然后傳遞給 Xcode 構(gòu)建系統(tǒng)進行編譯。
有了 Build Configuration 以后概疆,我們就能為同一個 Build Setting 設(shè)置不同的值逗威。例如Build Active Architecture Only在 Debug configuration 是Yes,而在 Internal 和 AppStore configuration 則是No岔冀。這樣就能做到同一份源代碼通過使用不同的 Build Configuration 來構(gòu)建出功能不一樣的 App 了凯旭。
那么,在構(gòu)建過程中怎樣才能選擇不同的 Build Configuration 呢使套?答案是使用 Xcode Scheme罐呼。
Xcode Scheme
Xcode Scheme用于定義一個完整的構(gòu)建過程,其包括指定哪些 Target 需要進行構(gòu)建侦高,構(gòu)建過程中使用了哪個 Build Configuration 嫉柴,以及需要執(zhí)行哪些測試案例等等。在項目新建的時候只有一個 Scheme奉呛,但可以為同一個項目建立多個 Scheme计螺。不過這么多 Scheme 中期奔,同一時刻只能有一個 Scheme 生效。
下圖是 Moments App 項目的 Scheme 危尿。 Moments App 項目有三個 Scheme 來分別代表三個環(huán)境呐萌,Moments Scheme 用于開發(fā)環(huán)境,Moments-Internal Scheme 用于測試環(huán)境谊娇,而 Moments-AppStore Scheme 用于生產(chǎn)環(huán)境肺孤。
下面是MomentsScheme 的配置。
左邊是該 Scheme 的各個操作济欢,如當前選擇了 Build 操作赠堵;右邊是對應(yīng)該操作的配置,比如 Build 對應(yīng)的 Scheme 可以構(gòu)建三個不同的 Targets法褥。不同的 Scheme 所構(gòu)建的 Target 數(shù)量可以不一樣茫叭,例如下面是Moments-InternalScheme 的配置。
該 Scheme 只構(gòu)建主 App TargetMoments半等,而不能構(gòu)建其他兩個測試 Target揍愁。
當選擇 Run、Test杀饵、Profile莽囤、 Analyze 和 Archive 等操作時,在右欄有一個很關(guān)鍵的配置是叫作 Build Configuration切距,可以通過下拉框來選擇 Moments App 項目里面三個 Configuration (Debug朽缎,Internal 和 AppStore) 中的其中一個。
為了方便管理谜悟,通常的做法是话肖,一個 Scheme 對應(yīng)一個 Configuration。有了這三個 Scheme 以后葡幸,我們就可以很方便地構(gòu)建出 Moments α(開發(fā)環(huán)境)最筒,Moments β(測試環(huán)境)和 Moments(生產(chǎn)環(huán)境)三個功能差異的 App。
這三個 App 的名字都不一樣礼患,怎么做到的呢是钥?實際上是為不同的 Configuration 設(shè)置了不一樣的 Build Setting。其中決定 App 名字的 Build Setting 叫作PRODUCT_BUNDLE_NAME缅叠,然后在 Info.plist 文件里面為 Bundle name 賦值悄泥,就能構(gòu)建出名字不一樣的 App。
為了構(gòu)建出不同環(huán)境版本的 App肤粱,需要經(jīng)常為各個 Build Configuration 下的 Build Setting 設(shè)置不一樣的值弹囚。 在這其中,使用好 xcconfig 配置文件就顯得非常重要领曼。
xcconfig 配置文件
xcconfig 會起到什么作用呢鸥鹉?
一般修改 Build Setting 的辦法是在 Xcode 的 Build Settings 界面上進行蛮穿。 例如下面的例子中修改 Suppress Warnings。
這樣做有一些不好的地方毁渗,首先是手工修改很容易出錯践磅,例如有時候很難看出來修改的 Setting 到底是 Project 級別的還是 Target 級別的。其次灸异,最關(guān)鍵的是每次修改完畢以后都會修改了 xcodeproj 項目文檔 (如下圖所示)府适,導致 Git 歷史很難查看和對比。
所以Xcode 提供了一個統(tǒng)一管理這些 Build Setting 的便利方法肺樟,那就是使用 xcconfig 配置文件來管理檐春。
xcconfig 概念及其作用
xcconfig也叫作 Build configuration file(構(gòu)建配置文件),可以使用它來為 Project 或 Target 定義一組 Build Setting么伯。由于它是一個純文本文件疟暖,可以使用 Xcode 以外的其他文本編輯器來修改,而且可以保存到 Git 進行統(tǒng)一管理。 這樣遠比在 Xcode 的 Build Settings 界面上手工修改要方便很多,而且還不容易出錯。
在 xcconfig 文件里面的每一條 Setting 都是下面的格式:
BUILD_SETTING_NAME = value
其中,BUILD_SETTING_NAME表示 Build Setting 的名字瞄勾,而value是該 Setting 的值。下面是一個例子虑瀑。
SWIFT_VERSION = 5.0
SWIFT_VERSION是用于定義 Swift 語言版本的 Build Setting馆纳,其值是5.0。Setting 的名字都是由大寫字母糠亩,數(shù)值和下劃線組成虐骑。這種命名法我們一般成為蛇型命名法,例如SNAKE_CASE_NAME赎线。
如何獲取Build Setting 的鍵值呢廷没,在build setting選項上command+c就可以直接復制獲取,delete的話會恢復xcode初始默認值垂寥。
當使用 xcconfig 時颠黎,Xcode 構(gòu)建系統(tǒng)會按照下面的優(yōu)先級來計算出 Build Setting 的最后生效值:
- Platform Defaults (平臺默認值)
- Xcode Project xcconfig File(Project 級別的 xcconfig 文件)
- Xcode Project File Build Settings(Project 級別的手工配置的 Build Setting)
- Target xcconfig File (Target 級別的 xcconfig 文件)
- Target Build Settings(Target 級別的手工配置的 Build Setting)
Xcode 構(gòu)建系統(tǒng)會按照上述列表從上而下讀取 Build Setting,如果發(fā)現(xiàn)同樣的 Setting 滞项,就會把下面的 Setting 覆蓋掉上面的狭归,越往下優(yōu)先級別越高。
例如在 Project 級別的 xcconfig 文件配置了SWIFT_VERSION = 5.0而在Target 級別的 xcconfig 文件配置了SWIFT_VERSION = 5.1文判,那么Target 級別的 Build Setting 會覆蓋 Project 級別的SWIFT_VERSION設(shè)置过椎,最終SWIFT_VERSION生效的值是5.1。
那么戏仓,要怎樣做才能做到不覆蓋原有的 Build Setting 呢疚宇?可以使用下面例子中的$(inherited)來實現(xiàn)亡鼠。
BUILD_SETTING_NAME = $(inherited) additional value
可以保留原先的 Setting,然后把新的值添加到后面去敷待。比如:
FRAMEWORK_SEARCH_PATHS = $(inherited) ./Moments/Pods
其中的FRAMEWORK_SEARCH_PATHS會保留原有的值间涵,然后加上./Moments/Pods作為新值。
在配置 Build Setting 時榜揖,還可以引用其他已定義的 Build Setting浑厚。
例如下面的例子中,F(xiàn)RAMEWORK_SEARCH_PATHS使用了另外一個 Build Setting PROJECT_DIR根盒。
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
為了重用钳幅,可以通過#include引入其他 xcconfig 文件。
#include "path/to/OtherFile.xcconfig"
Moments App xcconfig 配置文件
下面是 Moments App 項目如何管理 xcconfig 配置文件炎滞。
把所有 xcconfig 文件分成三大類:Shared敢艰、 Project 和 Targets。
其中 Shared 文件夾用于保存分享到整個 App 的 Build Setting册赛,例如 Swift 的版本號钠导、App 所支持的 iOS 版本號等各種共享的基礎(chǔ)信息。 下面是 SDKAndDeviceSupport.xcconfig 文件里面所包含的信息:
TARGETED_DEVICE_FAMILY = 1
IPHONEOS_DEPLOYMENT_TARGET = 14.0
TARGETED_DEVICE_FAMILY表示支持的設(shè)備森瘪,1表示 iPhone牡属。而IPHONEOS_DEPLOYMENT_TARGET表示支持 iOS 的最低版本,我們的 Moments App 所支持的最低版本是 iOS 14.0扼睬。
Project 文件夾用于保存 Xcode Project 級別的 Build Setting逮栅,其中 BaseProject.xcconfig 會引入 Shared 文件夾下所有的 xcconfig 配置文件,如下所示:
#include "CompilerAndLanguage.xcconfig"
#include "SDKAndDeviceSupport.xcconfig"
#include "BaseConfigurations.xcconfig"
然后根據(jù)三個不同的環(huán)境分別建了三個xcconfig 配置文件窗宇,如下:
- DebugProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
- InternalProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) INTERNAL
- AppStoreProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) PRODUCTION
它們的共同點是都引入了用于共享的 BaseProject.xcconfig 文件措伐,然后分別定義了 Swift 編譯條件配置SWIFT_ACTIVE_COMPILATION_CONDITIONS。其中$(inherited)表示繼承原有的配置军俊,$(inherited)后面的DEBUG或者INTERNAL表示在原有配置的基礎(chǔ)上后面添加了一個新條件侥加。有了這些編譯條件,就可以在代碼中這樣使用
#if DEBUG
print("Debug Environment")
#endif
該段代碼只在開發(fā)環(huán)境執(zhí)行粪躬,因為只有開發(fā)環(huán)境的SWIFT_ACTIVE_COMPILATION_CONDITIONS才有DEBUG的定義担败。這樣做能有效分離各個環(huán)境,保證同一份代碼構(gòu)建出對應(yīng)不同環(huán)境的 App镰官。
Targets 文件夾用于保存 Xcode Target 級別的 Build Setting提前,也是由一個 BaseTarget.xcconfig 文件來共享所有 Target 都需要使用的信息。
PRODUCT_BUNDLE_NAME = Moments
這里的PRODUCT_BUNDLE_NAME是 App 的名字朋魔。
下面是三個不同環(huán)境的 Target xcconfig 文件岖研。
- DebugTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.debug.xcconfig"
#include "BaseTarget.xcconfig"
PRODUCT_BUNDLE_NAME = $(inherited) α
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments.development
- InternalTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.internal.xcconfig"
#include "BaseTarget.xcconfig"
PRODUCT_BUNDLE_NAME = $(inherited) β
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments.internal
- AppStoreTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.appstore.xcconfig"
#include "BaseTarget.xcconfig"
PRODUCT_BUNDLE_NAME = $(inherited)
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments
它們都需要引入 CocoaPods 所生成的 xcconfig 和共享的 BaseTarget.xcconfig 文件,然后根據(jù)需要改寫 App 的名字。例如DebugTarget 覆蓋了PRODUCT_BUNDLE_NAME的值為Moments α*, 其所構(gòu)建的 App 叫作Moments α孙援。
一般在 App Store 上所有 App 的標識符都必須是唯一的害淤。如果你的項目通過 Configuration 和 Scheme 來生成免費版和收費版的 App,那么拓售,你必須在兩個 Configuration 中分別為PRODUCT_BUNDLE_IDENTIFIER配置對應(yīng)的標識符窥摄,例如com.company.free和com.company.paid。
在 Moments App 中础淤,也為各個環(huán)境下的 App 使用了不同的標識符崭放,以方便通過 CI 自動構(gòu)建,并分發(fā)到內(nèi)部測試組或者 App Store鸽凶。同時币砂,這也能為各個環(huán)境版本的 App 分離用戶行為數(shù)據(jù),方便統(tǒng)計分析玻侥。
一旦有了這些 xcconfig 配置文件决摧,今后就可以在 Xcode 的 Project Info 頁面里的 Configurations 上引用它們。
下面是所有 Configurations 所引用的 xcconfig 文件
在配置好所有 xcconfig 文件的引用以后凑兰,可以在 Build Settings 頁面查看某個 Build Setting 的生效值掌桩。我們以IPHONEOS_DEPLOYMENT_TARGET為例,一起看看姑食。
當選擇All和Levels時波岛,可以看到所有配置信息分成了不同的列。這些列分別代表前面的 Build Settng 優(yōu)先級:
- 平臺默認值
- Project 級別的 xcconfig 文件
- Xcode 項目文件中的 Project 級別配置
- Target 級別的 xcconfig 文件
- Xcode 項目文件中的 Target 級別配置
Build Settng 的優(yōu)先級是從左到右排序的音半。越是左邊優(yōu)先級就越高则拷。例如,在 Project 級別的 xcconfig 文件里面定義了IPHONEOS_DEPLOYMENT_TARGET的值為14.0祟剔,那么Project 級別的 xcconfig 文件(Project Config File) 一列上就會顯示iOS 14.0隔躲,它覆蓋了系統(tǒng)的默認值 (iOS Default)iOS 14.2。這就是因為 Project 級別的 xcconfig 文件物延,它的優(yōu)先級高于系統(tǒng)默認值,因此最后生效的值是iOS 14.0仅父。
總結(jié)
在使用 xcconfig 配置時叛薯,需要注意以下兩點:
首先,我們必須把所有 Build Setting 都配置在 xcconfig 文件里面笙纤,并通過 Git 進行統(tǒng)一管理耗溜;
其次,我們千萬不要在 Xcode 的 Build Settings 頁面修改任何 Setting省容,否則該配置會覆蓋 xcconfig 文件里面的配置抖拴。如果你不小心修改了,可以通過點擊delete鍵把頁面設(shè)置的配置刪掉。
最后阿宅,如何獲取Build Setting 的鍵值呢候衍,在build setting選項上command+c就可以直接復制獲取,delete的話 會恢復xcode初始默認值洒放。
最后蛉鹿,如何獲取Build Setting 的鍵值呢,在build setting選項上command+c就可以直接復制獲取往湿,delete的話會恢復xcode初始默認值妖异。
最后,如何獲取Build Setting 的鍵值呢领追,在build setting選項上command+c就可以直接復制獲取他膳,delete的話會恢復xcode初始默認值。
重要的事情說三遍??