本篇文章主要針對iOS應(yīng)用開發(fā)中, 針對需要創(chuàng)建許多相似的應(yīng)用App提出一種新穎的解決方案。
關(guān)于如何創(chuàng)建大量相似的App,iOS大神@唐巧曾在他的博文《猿題庫iOS客戶端的技術(shù)細(xì)節(jié)(一):使用多target來構(gòu)建大量相似App》提出了一種可行性非常高的解決方案。我本人也將該實現(xiàn)方案應(yīng)用到了某二手車應(yīng)用開發(fā)中, 通過創(chuàng)建多個target的方式創(chuàng)建了N個某某拍的應(yīng)用度帮。但是這種方案真的適用于所有場景么? 除了使用這種方案能否有其它的方式去解決這個問題呢?
基于多Target的應(yīng)用實踐
我剛開始接觸到開發(fā)多個相似App應(yīng)用的需求的時候, 也采用了多個target的解決方案腺办。主要做了以下工作:
- 建立多個Target (通過Duplicate行為)
- 為每一個Target指定LaunchImage和IconImage, LauchImage和IconImage由同一個image assert管理
- 為每一個Target指定了Info.Plist和InfoPlist.strings, InfoPlist.strings的作用僅僅是為了指定CFBundleDisplayName
- 為每一個Target創(chuàng)建了一個用于配置應(yīng)用特征的JSON描述文件, 用于對每個Target的特征進(jìn)行配置修改。
- 部署自動化打包平臺孽拷,防止有N個Target就手動打N次包。
在上述工作中, 1半抱、2脓恕、3均和配置項有關(guān), 5與項目開發(fā)無關(guān), 4是和具體的開發(fā)業(yè)務(wù)相關(guān)的膜宋。每一項的配置都沒有什么技術(shù)深度和難度, 4的實現(xiàn)和具體需求相關(guān), 對于極度相似的應(yīng)用更多的行為是換膚和換key。
這里稍微提以下關(guān)于InfoPlist.strings的指定, 每一個Target只能識別一個InfoPlist.strings, 而且還不能重命名炼幔。需要為每一個Target創(chuàng)建一個物理文件夾, 然后在對應(yīng)的文件夾下放置InfoPlist.strings防止命名沖突, 每一個InfoPlist.strings只能指定唯一識別的Target對象激蹲。(原理我還沒有找到, 找到我就更新下博文哈~)
差異性較大的Target處理
什么? 差異性大你還放在一個工程里? 架構(gòu)就有問題。是的, 差異性較大的工程就應(yīng)該拆分成不同的工程, 然后共享的代碼通過framework以及靜態(tài)庫引用的方式抽離出去江掩。<font color='orange'>但是, 時間是道坎!</font> 假如你時間很緊怎么辦? 本文給出一種時間很緊時候的<font color='red'>臨時</font>解決方案(注意: 決必是臨時的, 時間是海綿, 需要去擠的!)
在時間非常緊的情況下, 可以通過拆分AppDelegate來實現(xiàn)(代價其實非常沉重, 會link好多無用的類)学辱。拆分AppDelegate其實就要在main.m里面賦值不同的AppDelegate即可實現(xiàn)。main函數(shù)中argv包含了app的名字, 可以通過該名字去鑒別載入的AppDelegate环形。
#import <UIKit/UIKit.h>
#import "STAppDelegate.h"
#import "STPAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
char demoStr[] = "/stdemo.app"; // 檢查stdemo target
char *p= strstr(*argv, demoStr);
if(NULL != p){
return UIApplicationMain(argc, argv, nil, NSStringFromClass([STAppDelegate class]));
}else{
return UIApplicationMain(argc, argv, nil, NSStringFromClass([STPAppDelegate class]));
}
}
}
PS: 切記, 臨時解決方案, 如需根治, <font style="font-size:1.5em">拆分工程</font>!
基于多Target實現(xiàn)的好處
-
直觀
一目了然, 可以看到所有已創(chuàng)建的Target醒目的列在Build列表中策泣。每一個Target都有對應(yīng)的Tagret配置界面可以看到每一個項目配置圖標(biāo)以及Info.plist對應(yīng)信息。
-
靈活性高
可以根據(jù)項目需要Link需要的類, 根據(jù)Target來指定鏈接不同的類和資源文件, 而不用一口氣全部都Link進(jìn)來抬吟。
基于多Target遇到的坑
如果沒有遇到坑, 那就不會去重新尋找一個更好的解決方案了萨咕。基于多Target的方式去創(chuàng)建大量相似的App的坑主要提現(xiàn)在多人協(xié)作上火本。
個人之前在實現(xiàn)多Target項目的時候遇到的問題不多, 但是隨著時間推移, 維護(hù)開發(fā)遇到了兩個比較明顯的問題:
-
類的Target指定遺漏
在多個Target的環(huán)境下, 我們每新建一個類文件都要給類文件指定對應(yīng)的Target, 如果不小心忘記指定對應(yīng)的Target, 則會會在編譯階段報錯危队。
-
配置文件描述龐大, 難以修改
多個Target會導(dǎo)致項目的pbxproj臃腫, 因為pbxproj文件維護(hù)了項目的所有文件id和group層級關(guān)系, 多一個target就幾乎多了一倍的描述信息, 可想而知, 這個pbxporj文件是有多龐大。
光文件龐大頂多引起Xcode項目的配置文件加載慢, 但是遇到?jīng)_突的時候可就頭疼了, 幾萬行的描述文件钙畔。
配置文件修改不同步
配置文件修改不同步是基于已創(chuàng)建N個Target的前提下, 因為項目的推進(jìn), 需要對每一個項目文件進(jìn)行固定的修改, 但是存在修改遺漏的情況茫陆。
對于這種場景, 有一種比較好的方案是自己動手寫腳本來替換編譯配置項, 保證每一個Target的配置項目均被替換。Mac開發(fā)工具中自帶的PlistBuddy在處理配置項目替換上絕對是個神器擎析。
重新思考
雖然在項目中遇到了不少坑簿盅,但是解決這些坑并不需要大量的時間(那是因為時間被打散了, 組合起來估計也不少了),所以我個人并沒有去重新思考怎么去解決遺漏Target編譯報錯以及項目配置文件不斷沖突的問題揍魂。
觸發(fā)我重新思考是一次機(jī)緣, 經(jīng)過花瓣網(wǎng)某iOS研發(fā)高手(我不知道他名字哇)提點, 他問我基于Cocoapods能否有更好的辦法去創(chuàng)建大量相似的App桨醋。基于Cocoapods本身就是基于Hook, Hook本身就是動態(tài)修改項目配置項, 換言之, <font color='red'>能否通過動態(tài)修改Target的項目配置項去創(chuàng)建大量相似的App呢</font>?
回到文章前面的基于多Target的應(yīng)用實踐
的5個步驟, 逐一用替換項目的配置文件(pbcproj)的方式去重新審視现斋。
- 不需要建立多個Target, 只維護(hù)一個Target
- 主要是icon和launch image的修改, 有兩種方案:
- 在image.assert預(yù)先放置多個不同名字的資源, 通過修改pbxproj來指定不同的圖片資源
- 所有的icon和launch image都是用相同名字, 通過腳本動態(tài)替換image.assert中的資源文件(推薦)
- 主要針對info.plist和InfoPlist.strings的修改, InfoPlist.string可以通過
sed
命令去動態(tài)替換, info.plist也可以采取兩種方案來實現(xiàn):- 預(yù)先防止多個Info.plist文件, 通過修改pbxcproj來指定不同的info.plist文件
- target永遠(yuǎn)指定一個Info.plist, 通過腳本動態(tài)替換修改Info.plist(推薦)
- 通過JSON描述特性的文件可以單獨防止在工程里, 通過腳本拷貝替換, 也可以利用
cocoapods-keys
等工具進(jìn)行外部注入 - 前面的4個步驟都是依賴于基本動態(tài)替換, 自動化構(gòu)建平臺通過將指定Target的方式, 修改為在編譯器執(zhí)行對應(yīng)的任務(wù)腳本即可完成喜最。
進(jìn)一步優(yōu)化
重新思考<font color='black'>通過外部修改配置項目和資源文件的方式來實現(xiàn)多個類似應(yīng)用功能</font>, 省去了維護(hù)多個target產(chǎn)生的沖突和配置過大的問題。但是, 外部腳本本身也是一個實現(xiàn)成本, 這里針對替換外部腳本提出一個優(yōu)化策略(不一定最優(yōu))庄蹋。
-
維護(hù)每個項目的文件夾
每一個項目就是指原來的每一個target, 文件夾可以保持和原先的target名字保持同名瞬内。該目錄文件夾不參與項目引用, 即不在pbxcproj文件中被描述。該目錄文件夾純粹是提供給外部腳本使用, 與邏輯工程保持獨立蔓肯。
-
在第一步的文件夾中抽離變化項目到同一個JSON文件中
該json文件中描述了所有需要替換的內(nèi)容, 包含image.assert的替換規(guī)則以及info.plist替換規(guī)則等等遂鹊。
-
在第一步的文件夾中抽離資源文件
在該文件夾中防止所有可變化的資源文件, 包含
.png
振乏、info.plist
等等所有可變化差異的項目蔗包。
在前面三步的基礎(chǔ)下, 主要是為了一個目的, 一行腳本替換所有可變信息。(實際上就是提前將變化項維護(hù)在獨立的文件夾中了)
## 動態(tài)變化 demo1 Target
./st_muti_target st_demo1/muti_target.json
## 動態(tài)變化 demo2 Target
./st_muti_target st_demo2/muti_target.json
想要st_muti_target.sh
的源碼? 這個自己寫吧慧邮。调限。每個項目都不一樣的舟陆。
總結(jié)
基于建立多個相似App的需求, 和本人實際在項目應(yīng)用中遇到的坑, 提出了一種基于腳本不斷替換配置項目和資源文件的解決方案。該方案主要解決了多Target所帶來的配置文件過大和容易沖突的問題, 但是同時又引入了腳本的維護(hù)成本耻矮。本文也提供了一種降低腳本使用成本和項目耦合的一種方案, 但是仍需要進(jìn)一步優(yōu)化, 并不是最終的解決方案版本秦躯。
多一種方案多一種選擇么, 對于擅長書寫腳本的童鞋們, 用這種方式做大量類似的App(換膚App)可能會是更好的一種選擇喔~
水平有限, 有錯誤之處或者有什么地方?jīng)]有描述清楚, 請大家及時指出哇~
參考文件: