一些初學(xué)者可能會好奇团滥,為什么在開發(fā)應(yīng)用的時(shí)候我們需要用兩套隔離的數(shù)據(jù)庫和環(huán)境劈榨。這是因?yàn)樵谀愠掷m(xù)地開發(fā)應(yīng)用或增加新特性的時(shí)候,可能希望將開發(fā)版本和已經(jīng)存在的生產(chǎn)版本的應(yīng)用進(jìn)行區(qū)分冠跷。標(biāo)準(zhǔn)的開發(fā)實(shí)踐是針對不同版本的軟件使用不同的環(huán)境衫樊,而對我們來說,這個(gè)軟件就是 iPhone 應(yīng)用锭环。一般來講端仰,開發(fā)版應(yīng)用使用的數(shù)據(jù)庫(或其它系統(tǒng),比如說統(tǒng)計(jì)分析系統(tǒng))應(yīng)該與生產(chǎn)版應(yīng)用進(jìn)行區(qū)分田藐。在測試或者開發(fā)環(huán)境中荔烧,我們經(jīng)常使用到類似 “test comment”, “argharghargh” 和 “one more test comment” 這樣的測試數(shù)據(jù)。很顯然汽久,我們并不想讓真實(shí)的用戶看到這樣的信息鹤竭。如果應(yīng)用使用了統(tǒng)計(jì)分析系統(tǒng),在測試階段我們可能會發(fā)送成千上萬次的統(tǒng)計(jì)事件景醇。我們當(dāng)然也不想讓這樣的數(shù)據(jù)跟真實(shí)的統(tǒng)計(jì)數(shù)據(jù)混在一起臀稚。這就是為什么我們應(yīng)該區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境。
當(dāng)使用不同的環(huán)境開發(fā)時(shí)三痰,應(yīng)用需要知道它當(dāng)前使用的環(huán)境吧寺。一種通用的做法是在程序的 AppDelegate 中定義一個(gè)可以將應(yīng)用初始化為開發(fā)或者生產(chǎn)模式的全局變量。
enumenvironmentType{
casedevelopment, production
}
letenvironment:environmentType= .production
switchenvironment {
case.development:
//setweb service URLtodevelopment
//setAPIkeystodevelopment
print("It's for development")
case.production:
//setweb service URLtoproduction
//setAPIkeystoproduction
print("It's for production")
}
這種方法要求我們在切換環(huán)境的時(shí)候改變?nèi)肿兞康闹瞪⒔佟km然這種方法很方便也很快捷稚机,但是它存在很大的局限性。首先获搏,因?yàn)樵陂_發(fā)與生產(chǎn)環(huán)境中都使用了同一個(gè) bundle ID赖条,我們就不能在同一個(gè)設(shè)備上同時(shí)安裝應(yīng)用的不同版本。再者常熙,這種方法有可能導(dǎo)致我們不小心把開發(fā)版本的應(yīng)用提交到 App Store 上纬乍。如果忘記切換全局變量的值,我們就會提交錯(cuò)誤版本的應(yīng)用裸卫。我曾經(jīng)就有一次在提交應(yīng)用之前忘記改變?nèi)肿兞康闹捣卤幔Y(jié)果用戶就使用到了我的開發(fā)版本。這實(shí)在是太糟糕了墓贿。
在本篇文章中我將會展示一種更好的方法來區(qū)分開發(fā)和生產(chǎn)的構(gòu)建版本茧泪。更確切地來說退个,我們將會在 Xcode 中創(chuàng)建一個(gè)開發(fā)版的 target。這個(gè)方法同時(shí)適用于新建的或現(xiàn)存的大型項(xiàng)目调炬,所以你可以使用一個(gè)現(xiàn)有的項(xiàng)目來跟著這個(gè)教程進(jìn)行實(shí)踐。
使用這種方法舱馅,生產(chǎn)版和開發(fā)版的應(yīng)用都會使用同一套基礎(chǔ)代碼缰泡,但是可以有不同的圖標(biāo)、bundle ID 以及不同的數(shù)據(jù)庫代嗤。分發(fā)和提交應(yīng)用的過程也會十分簡單棘钞。更重要地,應(yīng)用的測試人員和管理人員可以在同一個(gè)設(shè)備上同時(shí)安裝應(yīng)用的兩個(gè)版本干毅,這樣他們就可以更加清楚地了解他們現(xiàn)在試用的是哪一個(gè)版本宜猜。
如何創(chuàng)建新的 Target
那么我們?nèi)绾卧?Xcode 當(dāng)中新建一個(gè)開發(fā)版的 target 呢?我會使用我提供的示例項(xiàng)目 “todo” 來一步一步地進(jìn)行演示硝逢。你可以使用自己的項(xiàng)目姨拥,并跟隨這些步驟進(jìn)行操作:
1. ?在 Project Navigator 面板上選擇項(xiàng)目,進(jìn)入設(shè)置渠鸽。在Targets小節(jié)下面叫乌,右擊現(xiàn)有的 target 并且選擇Duplicate來對一個(gè)現(xiàn)在的 target 進(jìn)行復(fù)制。
2. ?Xcode 會詢問你這個(gè) target 是不是針對于 iPad 開發(fā)的徽缚。對于本教程來說憨奸,我們只需要選擇 “Duplicate Only” 就可以了。
提示:如果你的項(xiàng)目支持通用設(shè)置凿试,則 Xcode 不會提示上面的消息排宰。
3. ?現(xiàn)在我們有了一個(gè)名為todo copy的新 target 和構(gòu)建 scheme 。讓我們來重命名一下以便于區(qū)分那婉。
在TARGETS列表中選擇新創(chuàng)建的 target板甘。按下回車鍵來對它的名稱進(jìn)行編輯,并且給它起一個(gè)合適的名稱详炬。我傾向于使用 “todo Dev”虾啦。當(dāng)然你可以使用你喜歡的任何名字。
接下來痕寓,來到 “Manage Schemes…”傲醉,選擇在第 1 步中新建的 scheme,并按下回車鍵呻率。把新 scheme 命名為跟新 target 一樣的名字(?即我們上一小步中使用的名字)
4. ?這一步是可選的硬毕,不過我強(qiáng)烈建議進(jìn)行這一步。如果想讓開發(fā)版跟生產(chǎn)版的應(yīng)用更加容易區(qū)分礼仗,我們應(yīng)該提供不同的應(yīng)用圖標(biāo)和啟動(dòng)界面吐咳。這可以使測試更加清楚他們現(xiàn)在使用的版本逻悠,并且可以防止我們提交開發(fā)版本的應(yīng)用到商店中。 :grinning:
選中Assets.xcassets然后添加新的 App 圖標(biāo)韭脊。選擇 icon > App Icons & Launch Images > New iOS App Icon. 將新圖標(biāo)命名為 “AppIcon-Dev” 并且添加你需要的圖片童谒。
5. ?現(xiàn)在回到項(xiàng)目設(shè)置,選擇開發(fā)版的 target 然后修改 bundle ID沪羔〖⒁粒可以簡單地在原來的 ID 上添加一個(gè) “Dev” 后綴。如果有操作過第 4 步蔫饰,在這里確保你的 app 圖標(biāo)設(shè)置為在上一步中添加的圖標(biāo)琅豆。
6. ?Xcode 會自動(dòng)為新的 target 創(chuàng)建一個(gè) plist 文件(一般命名為 todo copy-Info.plist)÷ㄓ酰可以在項(xiàng)目的根目錄下找到這個(gè)文件茫因。將 “copy” 修改為 “Dev”,然后將它放到原來的 plist 文件下方杖剪。這樣可以更方便我們對文件進(jìn)行操作冻押。
7. ?現(xiàn)在選擇開發(fā)版 target 下的 “Build Settings”,滾動(dòng)到 “Packaging”盛嘿,然后修改指定 plist 文件為開發(fā)版 plist(即剛剛的 todo Dev.plist)翼雀。
8. ?最后,為生產(chǎn)版和開發(fā)版 target 同時(shí)設(shè)置一個(gè)預(yù)處理宏和編譯器標(biāo)志孩擂。這樣在之后的開發(fā)中我們就可以在代碼中使用這個(gè)標(biāo)識來檢測當(dāng)前運(yùn)行的應(yīng)用是哪個(gè)版本狼渊。
對于 Objective-C 項(xiàng)目,來到Building Settings类垦,滾動(dòng)到Apple LLVM 7.0 - Preprocessing狈邑。展開Preprocessor Macros并且為Debug和Release添加一個(gè)變量。對于開發(fā)版 target(即 todo Dev)蚤认,設(shè)置變量的值為DEVELOPMENT=1(校對注:等號兩邊不能有空格)米苹。相對地,設(shè)置開發(fā)版 target 的值為DEVELOPMENT=0砰琢。
對于 Swift 項(xiàng)目蘸嘶,編譯器不再支持預(yù)處理指令了。相對地陪汽,它使用了運(yùn)行期屬性(compile-time attributes)和構(gòu)建配置训唱。為了增加開發(fā)版構(gòu)建的標(biāo)志,選擇開發(fā) target挚冤。來到Build Settings况增,向下滾動(dòng)到Swift Compiler - Custom Flags小節(jié)。設(shè)置值為-DDEVELOPMENT來表明當(dāng)前為 target 為開發(fā)版训挡。
現(xiàn)在我們已經(jīng)創(chuàng)建并配置好了開發(fā)版的 target澳骤,接下來要做什么呢歧强?
使用 Target 和宏
因?yàn)樵O(shè)置過了DEV_VERSION的宏,我們就可以在代碼中利用這個(gè)宏對項(xiàng)目使用動(dòng)態(tài)檢測了为肮。這里有一個(gè)簡單的使用示例:
Objective-C:
#ifDEVELOPMENT
#defineSERVER_URL @"http://dev.server.com/api/"
#defineAPI_TOKEN @"DI2023409jf90ew"
#else
#defineSERVER_URL @"http://prod.server.com/api/"
#defineAPI_TOKEN @"71a629j0f090232"
#endif
在 Objective-C 當(dāng)中摊册,我們可以使用#if來對DEVELOPMENT的狀態(tài)進(jìn)行檢測,并且為 URL/API 設(shè)置對應(yīng)的值颊艳。
Swift:
#ifDDEVELOPMENT
letSERVER_URL="http://dev.server.com/api/"
letAPI_TOKEN="DI2023409jf90ew"
#else
letSERVER_URL="http://prod.server.com/api/"
letAPI_TOKEN="71a629j0f090232"
#endif
在 Swift 當(dāng)中茅特,我們依然可以使用#if來對構(gòu)建配置進(jìn)行動(dòng)態(tài)判斷。然而籽暇,我們不再使用#define來定義一個(gè)常量,而是簡單地使用let來定義一個(gè) Swift 中全局常量饭庞。
提示:一般情況下戒悠,我們會將上面的代碼放在 AppDelegate 當(dāng)中。不過最終取決于你想在哪里初始化應(yīng)用的配置舟山。
當(dāng)選擇 “todo Dev” 的 scheme 并運(yùn)行項(xiàng)目绸狐,我們就會創(chuàng)建一個(gè)開發(fā)版的構(gòu)建并自動(dòng)將服務(wù)器配置設(shè)置為開發(fā)版的環(huán)境。你現(xiàn)在可以放心地上傳這個(gè)開發(fā)版到 TestFlight 或者 HockeyApp 讓應(yīng)用的測試人員或者管理人員進(jìn)行測試了累盗。
以后如果需要?jiǎng)?chuàng)建一個(gè)生產(chǎn)版的構(gòu)建寒矿,只要簡單地選擇 “todo” 的 scheme。一行代碼都不需要改變若债。
關(guān)于管理多個(gè) Target 的一些注意事項(xiàng)
1.?當(dāng)為項(xiàng)目添加新文件時(shí)符相,不要忘記同時(shí)勾選多個(gè) target 來保持各個(gè) target 中的代碼一致。
2. ?如果你的項(xiàng)目中使用了 Cocoapods蠢琳,不要忘記在 podfile 中增加新的 target啊终。我們可以使用link_with來指定多個(gè) target“列耄可以在Cocoapods 文檔中找到更詳細(xì)的內(nèi)容±渡現(xiàn)在 Podfile 看起來應(yīng)該是這樣的:
source'https://github.com/CocoaPods/Specs.git'
platform:ios,'7.0'
workspace'todo'
link_with'todo','todo Dev'
pod'Mixpanel'
pod'AFNetworking'
如果使用了持續(xù)集成系統(tǒng),比如Travis CI或Jenkins泰讽,不要忘了每個(gè) target 的構(gòu)建和交付都要配置例衍。