原文:How to Use Xcode Targets to Manage Development and Production Builds
作者:EugeneTrapeznikov
想象一下,你已經(jīng)完成了應(yīng)用程序的開發(fā)和測試,現(xiàn)在準(zhǔn)備提交正式版本。問題是,一些web服務(wù)的url指向了測試服務(wù)器,同時API密鑰被配置用于測試環(huán)境。在提交app給蘋果審核前,你需要修改所有這些API密鑰和URL以適應(yīng)生產(chǎn)版本。這聽起來還好,對吧?但是相較于在開發(fā)環(huán)境和生產(chǎn)環(huán)境之間來回修改相關(guān)數(shù)值,有沒有更好的方法來處理開發(fā)和生產(chǎn)版本的構(gòu)建?這正是接下來Eugene要和你討論的。
-
進(jìn)入Eugene的教程
對于初學(xué)者來說,有些人可能會奇怪,在App開發(fā)過程中,為什么需要使用兩個單獨(dú)的數(shù)據(jù)庫和環(huán)境。原因是當(dāng)你繼續(xù)構(gòu)建新的功能特性或繼續(xù)開發(fā)你的應(yīng)用,你希望區(qū)分開現(xiàn)有的公開版本和生產(chǎn)版本。
標(biāo)準(zhǔn)的軟件開發(fā)實(shí)踐是在不同的開發(fā)環(huán)境下開發(fā)不同版本的軟件,像我們案例中講到的開發(fā)iphone應(yīng)用。應(yīng)用程序的開發(fā)版本通常使用一個不同于生產(chǎn)環(huán)境的數(shù)據(jù)庫(或如分析的其他系統(tǒng))。這就是為什么我們應(yīng)該為不同的環(huán)境中使用單獨(dú)的服務(wù)器和數(shù)據(jù)庫。開發(fā)人員在測試期間通常都使用虛擬圖像或虛擬數(shù)據(jù)。在測試環(huán)境中,使用諸如 “test comment”, “argharghargh” 和 “one more test comment”之類的測試數(shù)據(jù)并不少見。顯然,你不希望你的真實(shí)用戶看到這樣的消息。如果你的應(yīng)用程序使用了一個分析系統(tǒng)的情況下,你甚至?xí)跍y試階段發(fā)送成千上萬的事件。同樣的,你不會把測試數(shù)據(jù)和生產(chǎn)數(shù)據(jù)放在同一個數(shù)據(jù)庫中。這就是為什么總是推薦區(qū)分開發(fā)和生產(chǎn)環(huán)境。
在使用兩個獨(dú)立的環(huán)境時,你的應(yīng)用程序需要有一個辦法找出它應(yīng)該連接到的環(huán)境。一種常用的方法是在你的主應(yīng)用代理里定義一個全局變量,它會將您的應(yīng)用程序初始化為開發(fā)或生產(chǎn)模式。
enum environmentType {
case development, production
}
let environment:environmentType = .production
switch environment {
case .development:
// set web service URL to development
// set API keys to development
print("It's for development")
case .production:
// set web service URL to production
// set API keys to production
print("It's for production")
}
這種方法需要你每次切換環(huán)境時改變?nèi)肿兞俊km然這種方法也許快捷,方便,但是它有一些重要的限制。首先,因?yàn)槲覀冊陂_發(fā)和生產(chǎn)兩個環(huán)境中使用一個Bundle ID,你不能在一臺設(shè)備上安裝應(yīng)用的兩個版本。當(dāng)你需要要測試開發(fā)版本的應(yīng)用程序時,同時仍在該設(shè)備上使用生產(chǎn)版本的應(yīng)用,這就變的不方便了。此外,這種方法很有可能將應(yīng)用的開發(fā)版本上傳到應(yīng)用商店。如果你忘記了改變這個全局變量,你將會上傳錯誤的應(yīng)用給你的用戶。我記得有一次在提交應(yīng)用程序到應(yīng)用商店之前我忘記改變?nèi)肿兞浚脩粝螺d的是應(yīng)用的開發(fā)版本,這是可怕的。
在這篇文章中,我將展示一個更好的方法來區(qū)分開發(fā)和生產(chǎn)構(gòu)建。具體而言,我們將在Xcode中創(chuàng)建一個開發(fā)的target。這種方法法適用于新的和現(xiàn)有的大型項(xiàng)目,所以你可以用一個現(xiàn)有的應(yīng)用程序?qū)φ毡窘坛獭?/p>
通過應(yīng)用這種方法,應(yīng)用的開發(fā)和生產(chǎn)版本將使用相同的基礎(chǔ)代碼,但可以有不同的圖標(biāo),bundle ID 和指向不同的數(shù)據(jù)庫。發(fā)布和提交過程將會非常簡單。最重要的是,你的測試人員和經(jīng)理可以在同一設(shè)備上安裝兩個版本的應(yīng)用程序,所以他們完全知道他們在體驗(yàn)?zāi)膫€版本。
-
如何創(chuàng)建一個新的Target
所以你如何在Xcode中創(chuàng)建一個開發(fā)的target?我使用示例項(xiàng)目“todo”引導(dǎo)您一步一步完成整個過程。您也可以使用自己的項(xiàng)目并按照步驟:
- 在項(xiàng)目的導(dǎo)航面板進(jìn)入項(xiàng)目設(shè)置。在Targets區(qū)域下,右鍵單擊現(xiàn)有目標(biāo)并選擇
Duplicate
復(fù)制現(xiàn)有的目標(biāo)。
640-0.jpeg - Xcode會詢問你新的target是否是為iPad開發(fā)。對于本教程,我們只是選擇“Duplicate Only”。
640-1.jpeg
提示:如果您的項(xiàng)目支持通用設(shè)備,Xcode不會提示上述消息。 - 現(xiàn)在我們有一個名為
todo copy
的新的target和build scheme。重命名并使之更容易理解。
- 在Targets列表中選擇新的target。按Enter鍵編輯文本,添加一個更合適的名字。我更傾向于“todo Dev”。你可以自由選擇任何你喜歡的名字。
-
接下來,找到“Manage Schemes…”,選擇您在步驟1中創(chuàng)建的shceme,并按“輸入”,使scheme的名稱和新的target的名稱相同(這是你為新的target所選擇的名字)
640-2.jpeg
-
步驟4是可選的,但強(qiáng)烈推薦。如果你想簡單地區(qū)分開發(fā)和生產(chǎn)版本構(gòu)建,你應(yīng)該為每個版本使用單獨(dú)的icon和啟動頁。這將使測試人員更清晰地知道正在使用哪個app,防止上傳開發(fā)版本。
跳到Assets.xcassets
添加一個新的圖標(biāo)。右擊圖標(biāo) >App Icons & Launch Images
>New iOS App Icon
. 新圖標(biāo)重命名為AppIcon-Dev
同時添加自己的圖片。
640-3.jpeg -
現(xiàn)在回到項(xiàng)目設(shè)置,選擇您的開發(fā)target,并改變Bundle Identifier。你可以簡單地將“Dev”追加到原來的ID上。如果執(zhí)行了步驟4,請確保更改應(yīng)用app icon,設(shè)置為在上一步中創(chuàng)建的。
640-4.jpeg Xcode會自動為你的target添加plist文件(如todo copy-Info.plist)。你可以在項(xiàng)目的根文件夾找到它。將它從“copy”重命名為“Dev”,并將它放在原始的plist文件下。這里你將更容易管理文件。
-
現(xiàn)在打開你開發(fā)
target
的Build Settings
,滾動到Packaging
,并將值改為開發(fā)的plist文件(todo Dev.plist)。
640-5.jpeg -
最后,我們會為生產(chǎn)和開發(fā)target配置預(yù)處理宏/編譯器標(biāo)識。之后我們就可以使用該標(biāo)識在我們的代碼來檢測應(yīng)用程序正在運(yùn)行的版本。
對于Objective-C的項(xiàng)目,去到Build Settings
下Apple LLVM 7.0 - Preprocessing
。拓展Preprocessor Macros
在Rebug和Release區(qū)域添加一個變量。對于開發(fā)target(即todo Dev),將該值設(shè)置為DEVELOPMENT = 1
。另一個,將值設(shè)為DEVELOPMENT=0
來表示生產(chǎn)版本。
640-6.jpeg
640-7.jpeg
對于swift的項(xiàng)目,編譯器不再支持預(yù)處理指令。作為替代,它使用編譯時的屬性和build配置。選中開發(fā)target,添加一個標(biāo)識表示開發(fā)版本。找到Build Setting
往下滾動到Swift Compiler - Custom Flags
部分。將值設(shè)為-DDEVELOPMENT
表示這個target作為開發(fā)版本。
640-8.jpeg
現(xiàn)在,您已經(jīng)創(chuàng)建并配置了開發(fā)target,下一步呢?
-
使用Target和宏
根據(jù)已配置的宏DEV_VERSION,我們可以在代碼中利用它動態(tài)地編譯項(xiàng)目。下面是一個簡單的例子:
Objective-C:
#if DEVELOPMENT
#define SERVER_URL @"http://dev.server.com/api/"
#define API_TOKEN @"DI2023409jf90ew"
#else
#define SERVER_URL @"http://prod.server.com/api/"
#define API_TOKEN @"71a629j0f090232"
#endif
Objective-C中你可以使用#if
檢查DEVELOPMENT
的環(huán)境,并相應(yīng)的設(shè)置URLs/ API密鑰。
Swift:
#if DEVELOPMENT
let SERVER_URL = "http://dev.server.com/api/"
let API_TOKEN = "DI2023409jf90ew"
#else
let SERVER_URL = "http://prod.server.com/api/"
let API_TOKEN = "71a629j0f090232"
#endif```
Swift中你仍然可以使用`#if`判定build的參數(shù)動態(tài)編譯。然而,除了使用`#define`定義基本常量,在swift中我們也可以用`let`定義一個全局常量。
*提示:通常,你會把上面的代碼放在app delegate中。但這最終是取決于你在哪里初始化應(yīng)用程序設(shè)置。*
現(xiàn)在,當(dāng)您選擇“todo Dev”scheme運(yùn)行項(xiàng)目,你創(chuàng)建開發(fā)版本會自動將服務(wù)器的配置設(shè)置為開發(fā)環(huán)境。現(xiàn)在,您可以上傳開發(fā)版本到TestFlight 或 HockeyApp供測試人員和管理人員來測試。
接著如果你需要創(chuàng)建一個生產(chǎn)版本,您可以簡單地選擇"todo"scheme。不需要更改代碼。
**管理多個target的一些注意事項(xiàng)**
1. 當(dāng)你添加新的文件到項(xiàng)目中,不要忘記選擇兩個target,以保持你的代碼同步在兩個版本。

2. 如果你使用的CocoaPods,不要忘了添加新的target到你的podfile中。您可以使用`link_with`指定多個target。您可以進(jìn)一步細(xì)節(jié)請查閱的 CocoaPods 文檔。你的podfile看起來是這樣的:

3. 如果你使用持續(xù)集成系統(tǒng),如 Travis CI 或Jenkins,別忘了配置兩個target的build和deliver。
[.](https://mp.weixin.qq.com/s?__biz=MjM5OTM0MzIwMQ==&mid=2652546114&idx=1&sn=67e479d82e0d0a662b05082fe74f731b&scene=0&key=77421cf58af4a653c8399f6b45841a85c270e9c5a3a27be12bf54eb8e0f4ef3abd3fd0774f444128ce7a65dc65673883&ascene=7&uin=NTk5NjY4MDAw&devicetype=iPhone+OS9.3.2&version=16031510&nettype=WIFI&fontScale=100&pass_ticket=RvqmZR25hWgBcY4bjjgmBhNXNIEICVVC%2FJ1rHW3D8AiLKzaNJKaKDqdSrCnfUcML)