前言
上一篇中我們對組件化的準(zhǔn)備工作做了介紹茸炒,這篇文章我們以SXNews為例進(jìn)行組件化愕乎,Demo地址在這里,殼工程獲取腳本在這里壁公,希望本文能給你帶來幫助感论。
一比肄、修改配置
根據(jù)上一篇文章所述湿硝,你應(yīng)該已經(jīng)有了ModularizationDemo
文件夾,此時(shí)該文件夾中只有config
和OldProject
兩個(gè)子文件夾示括。這時(shí)我們應(yīng)該針對項(xiàng)目情況,修改config內(nèi)容垛膝。
這里是SXNews
的Podfile
根據(jù)這個(gè)文件丁稀,我們可以得知該項(xiàng)目主要使用了RAC
等框架開發(fā)吼拥,因此為了方便起見,我們需要修改一下我們的config:
- 通過終端進(jìn)入config文件夾內(nèi)线衫,后面的路徑是你的文件夾路徑
cd ~/ModularizationDemo/config
- 修改config/templates/Podfile凿可,在
source 'https://github.com/CocoaPods/Specs.git'
后添加我們的私有pod源source 'git@github.com:ModularizationDemo/PodSpec.git'
:
- 復(fù)制config.sh文件為config_category.sh,復(fù)制templates文件夾為templates_category
cp config.sh config_category.sh
cp -r templates/ templates_category/
- 修改
config_category.sh
文件授账,這里我使用的是vi枯跑,用:
進(jìn)入命令模式,輸入以下代碼白热,將所有的templates改為templates_category
%s#templates#templates_category#g
- 進(jìn)入templates文件夾敛助,修改里面的Podfile這個(gè)文件,由于該項(xiàng)目的所有模塊都適用了RAC屋确、AFN纳击、MJExtension续扔、SDWebImage,因此我們添加給該模版添加這些方便后面處理(其中為了后面網(wǎng)絡(luò)請求代碼方便焕数,我使用了自己的HLNetworking纱昧,詳細(xì)的使用方法可以在該框架的主頁查閱):
pod 'ReactiveCocoa','2.5'
pod 'HLNetworking', '~> 2.0.0.beta'
pod 'MJExtension', '~> 2.0'
pod 'SDWebImage','~> 3.7'
- 進(jìn)入templates_category文件夾,同樣修改里面的Podfile這個(gè)文件堡赔,category類工程后面的作用主要是做中間件砌些,因此我們添加給該模版添加中間件框架Lothar,詳細(xì)的使用方法可以在該框架的主頁查閱):
pod 'Lothar', '~> 1.0.5'
至此加匈,配置文件已經(jīng)修改完成,這里有我寫好的配置文件仑荐,可以根據(jù)需求更改里面的參數(shù)雕拼。
二啥寇、創(chuàng)建組件模塊
SXNews
這個(gè)項(xiàng)目結(jié)構(gòu)很簡單辑甜,分為Search
磷醋、Detail
邓线、PhotoSet
、Weather
你雌、News
婿崭、Reply
逛球、Main
這7個(gè)業(yè)務(wù)組件和Tools
幸海、Other
這兩個(gè)公共文件夾物独。根據(jù)前兩篇文章的內(nèi)容挡篓,我們在這一節(jié)先將7個(gè)業(yè)務(wù)組件拆分出來官研。
先根據(jù)劃分好的業(yè)務(wù)組件建立組件倉庫:
- 首先在git上創(chuàng)建需要組件化的組件倉庫,例如
Search
始花,該倉庫為空倉庫即可酷宵,并記錄倉庫的https地址浇垦、ssh地址和項(xiàng)目主頁地址溜族,這里我們以github的例子為例:
HTTPS: https://github.com/ModularizationDemo/Search.git
SSH: git@github.com:ModularizationDemo/Search.git
HomePage: https://github.com/ModularizationDemo/Search
- 然后在終端中進(jìn)入文件夾
config
,然后執(zhí)行config.sh
文件腳本(組件使用config.sh腳本寡壮,組件的action使用config_category.sh腳本)
cd ~/ModularizationDemo/config
./config.sh
- 此時(shí)終端會(huì)顯示提示信息况既,根據(jù)提示信息輸入
作者
,項(xiàng)目名
莫其,組織名
乱陡,倉庫HTTPS URL
憨颠,倉庫SSH URL
养盗,主頁 URL
爪瓜,這些信息我們在上一步中就已經(jīng)獲得了,逐個(gè)填入即可:
- 完成后我們會(huì)發(fā)現(xiàn)
ModularizationDemo
目錄下已經(jīng)多了一個(gè)Search目錄,其目錄大概如下:
- 接著我們打開項(xiàng)目中的xcodeproj文件碍论,將原項(xiàng)目中
Search
相關(guān)的部分拖入新項(xiàng)目的Search
文件夾內(nèi)鳍悠,記得選上Copy items if needed
- 完成后的Search項(xiàng)目目錄應(yīng)該如下:
- 接著我們嘗試編譯一下概行,發(fā)現(xiàn)出現(xiàn)了一些警告凳忙,從警告中得知勤家,這里主要是缺少部分公共代碼(
UILabel+Wonderful.h
伐脖、NSString+Base64.h
)以及RAC相關(guān)的依賴:
- 編輯模版為我們生成好的
Podfile
断凶,根據(jù)錯(cuò)誤提示添加缺少的框架认烁,然后pod install
完成cocoapods配置 - 打開
Search.xcworkspace
却嗡,嘗試編譯窗价,發(fā)現(xiàn)還是缺少RAC撼港,查看原項(xiàng)目發(fā)現(xiàn)帝牡,原項(xiàng)目中使用了pch引入公共庫的頭文件靶溜,因此依次在需要引入RAC的類中逐個(gè)添加#import <ReactiveCocoa/ReactiveCocoa.h>
即可 - 再次編譯罩息,發(fā)現(xiàn)缺少
UILabel+Wonderful.h
、NSString+Base64.h
递宅、SXDetailPage.h
恐锣,對于這些項(xiàng)目內(nèi)的依賴我們暫且不管土榴,重復(fù)以上步驟拆分其他組件 - 到這里所有的組件都應(yīng)該拆分好了赫段,但是這樣的組件由于相互依賴糯笙,是無法獨(dú)立運(yùn)行的给涕,接下來我們就通過
Lothar
這個(gè)中間件去除組件依賴
三恭应、去除組件依賴
1. 提取公共代碼
本系列文章的前兩篇也說過昼榛,在項(xiàng)目開發(fā)中胆屿,難免會(huì)產(chǎn)生形如Common
或者Tools
這樣的公共代碼,在組件化中读宙,應(yīng)當(dāng)將這些代碼細(xì)分為各種二方庫;在本例中桦锄,由于這一塊代碼量很少结耀,因此直接將這部分所有代碼生成一個(gè)私有pod图甜,作為基礎(chǔ)庫供于其他組件使用。
- 首先依舊使用config生成項(xiàng)目模版矿瘦,并將Tools相關(guān)代碼放入項(xiàng)目中,步驟與拆分組件類似
- 編輯Podfile文件潮秘,只引入
HLNetworking
這個(gè)庫枕荞,并pod install
:
- 這里原項(xiàng)目使用的是自己編寫的對AFN的簡單封裝,我這里直接將其改為依賴于
HLNetworking
- 編輯Tools.podspec文件,增加依賴
s.dependency "HLNetworking"
:
- 提交并上傳代碼高诺,打tag虱而,驗(yàn)證pod是否可用,最后上傳至私有pod
git add .
git commit -m "Tools 1 init"
git push
git tag 1
pod lib lint // 如果這一步通過了就上傳tag
git push --tags
pod repo push myspec --allow-warnings // myspec是上一篇文章中準(zhǔn)備好的私有pod倉庫的名字惠呼,--allow-warnings是忽略警告剔蹋,pod提供了很多參數(shù),具體請查閱cocoapods.org
如果一切正常矫付,完成的結(jié)果在終端顯示如下:
這樣我們的公共代碼就提取好了技即。
2. 解除組件橫向依賴
接下來我們以Search
模塊為例而叼,介紹其解除于其他模塊耦合的方法液荸,限于篇幅娇钱,Lothar相關(guān)的方法就不寫說明了文搂,具體請查閱Lothar的項(xiàng)目介紹及注釋
第一步 創(chuàng)建組件的服務(wù)接口
首先將
SXSearchViewModel
中對AFN的依賴改為HLNetworking的調(diào)用,然后根據(jù)缺失的依賴添加基礎(chǔ)庫的import如果一切順利硝皂,我們會(huì)發(fā)現(xiàn)
SXSearchPage.m
中引入了Detail
模塊的SXDetailPage.h
(頂部有#import "SXDetailPage.h"
),我們先暫時(shí)將其注釋掉然后在舊工程中刪掉
Search
文件夾折欠,編譯咪奖,發(fā)現(xiàn)很多地方提示沒有SXSearchPage
,查看錯(cuò)誤代碼斤葱,發(fā)現(xiàn)Search
相關(guān)依賴主要是需要生成SXSearchPage
控制器-
根據(jù)該需求揍堕,我們創(chuàng)建
Search
模塊的Lothar擴(kuò)展,提供此服務(wù):- 在git中創(chuàng)建
Search-Category
項(xiàng)目 - 終端進(jìn)入config文件夾楞慈,輸入
./config_category.sh
配置項(xiàng)目模版饿悬,項(xiàng)目名為Search-Category
- 終端進(jìn)入Detail-Category文件夾狡恬,輸入
pod install
完成Lothar
的安裝,完成后打開Search-Category.xcworkspace
- 在
Search-Category
文件夾中創(chuàng)建Lothar
的category
- 由于
Search
需求的無非是跳轉(zhuǎn)頁面并將keyword傳值過去蝎宇,因此我們在Lothar+Search
中寫一個(gè)方法并實(shí)現(xiàn)它:
- 在git中創(chuàng)建
- (nullable UIViewController *)Search_aViewControllerWithKeyword:(nullable NSString *)keyword;
- (nullable UIViewController *)Search_aViewControllerWithKeyword:(nullable NSString *)keyword {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
if (keyword) {
dict[kSearchKeyword] = keyword;
}
return [self performTarget:@"Search" action:@"aViewController" params:[dict copy] shouldCacheTarget:YES];
}
}
其中target字符串是提供服務(wù)的組件名弟劲,action的字符串是
Search
中的Target的方法名去掉:
,@"keyword"
則是參數(shù)解包用的key姥芥,這里的兩個(gè)字符串即前兩篇說的硬編碼編譯一下兔乞,沒什么問題,這個(gè)服務(wù)的接口就OK了
-
接著在舊工程中的Podfile寫入
pod "Search-Category", :path => "../Search-Category"
撇眯,執(zhí)行pod install
报嵌,提示我們target所支持的development版本不對玄坦,我們暫時(shí)先將Podfile的platform :ios, '7.0'
改為platform :ios, '8.0'
,再次執(zhí)行pod install
完成后在
AppDelegate
及SXDetailPage
中修改錯(cuò)誤警告:
#import <Search-Category/Lothar+Search.h>
UIViewController *viewController = [[Lothar shared] Search_aViewControllerWithKeyword:sender.titleLabel.text];
[self.navigationController pushViewController:viewController animated:YES];
- 此時(shí)舊工程就應(yīng)該能正常編譯了表伦,但是
SXSearchPage
相關(guān)的代碼會(huì)沒有效果,接下來我們實(shí)現(xiàn)Search
模塊的服務(wù)
第二步 實(shí)現(xiàn)組件服務(wù)
接著我們在Search
模塊中支持這個(gè)服務(wù):
- 打開Search的workspace,創(chuàng)建
Target_Search
類,創(chuàng)建并實(shí)現(xiàn)接口方法:
- (UIViewController *)Action_aViewController:(NSDictionary *)params;
- (UIViewController *)Action_aViewController:(NSDictionary *)params {
NSString *keyword = params[@"keyword"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
SXSearchPage *sp = [sb instantiateViewControllerWithIdentifier:@"SXSearchPage"];
sp.keyword = keyword;
return sp;
}
- 我們發(fā)現(xiàn)該控制器是從舊工程一個(gè)公共的
Main.storyboard
中創(chuàng)建出來的羊苟,為了明確控制器歸屬凉倚,我們將這個(gè)storyboard拆分:- 在Search中創(chuàng)建一個(gè)叫做
SXSearchPage
的storyboard - 找到
Main
這個(gè)storyboard,將SXSearchPage
剪切蜀漆,復(fù)制到Search中SXSearchPage.storyboard
內(nèi) - 將
SXSearchPage.storyboard
中SXSearchPage
控制器設(shè)置為is Inital View Controller
- 修改
Target_Search
的實(shí)現(xiàn)為
- 在Search中創(chuàng)建一個(gè)叫做
- (UIViewController *)Action_aViewController:(NSDictionary *)params {
NSString *keyword = params[@"keyword"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"SXSearchPage" bundle:nil];
SXSearchPage *sp = sb.instantiateInitialViewController;
sp.keyword = keyword;
return sp;
}
- 然后嘗試編譯锨天,發(fā)現(xiàn)
SXSearchPage.m
中與SXDetailPage
相關(guān)代碼編譯不通過,接著回到舊工程查看相關(guān)代碼,該部分屬于Detail
模塊的范圍內(nèi)骂租,因此我們創(chuàng)建一個(gè)Detail-Category
互站,創(chuàng)建方式跟Search-Category
相同, -
Detail-Category
中創(chuàng)建以下方法提供服務(wù)接口:
// Lothar+Detail.h
- (nullable UIViewController *)Detail_aViewControllerWithDocid:(nonnull NSString *)docid;
// Lothar+Detail.m
- (UIViewController *)Detail_aViewControllerWithDocid:(NSString *)docid {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
if (docid) {
dict[@"docid"] = docid;
}
return [self performTarget:@"Detail" action:@"aViewController" params:[dict copy] shouldCacheTarget:YES];
}
- 在
Search
的Podfile中加入pod 'Detail-Category', :path => '../Detail-Category'
,以使用Detail
的服務(wù),將出錯(cuò)代碼修改為
UIViewController *viewController = [[Lothar shared] Detail_aViewControllerWithDocid:[self.searchListArray[indexPath.row] docid]];
[self.navigationController pushViewController:viewController animated:YES];
- 此時(shí)
Search
模塊應(yīng)該編譯通過了我擂,但此時(shí)Detail-Category
的服務(wù)接口并未實(shí)現(xiàn)互妓,且Detail-Category
尚未從舊工程中拆分出來珠闰,接下來我們先暫時(shí)在主工程中實(shí)現(xiàn)該服務(wù)
第三步 舊工程中實(shí)現(xiàn)組件服務(wù)
- 關(guān)閉所有的xcode窗口,找到
Detail
文件夾摩幔,創(chuàng)建Target
子文件夾 - 創(chuàng)建
Target_Detail
類坡疼,實(shí)現(xiàn)Detail-Category
提供的接口
// Target_Detail.h
- (UIViewController *)Action_aViewController:(NSDictionary *)params;
// Target_Detail.m
- (UIViewController *)Action_aViewController:(NSDictionary *)params {
SXNewsEntity *model = [[SXNewsEntity alloc]init];
model.docid = params[@"docid"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"News" bundle:nil];
SXDetailPage *devc = (SXDetailPage *)[sb instantiateViewControllerWithIdentifier:@"SXDetailPage"];
devc.newsModel = model;
return devc;
}
- 補(bǔ)全因從
Main.storyboard
和News.storyboard
拆分出來而失效的push操作 - 在舊工程的Podfile中加入以下倉庫藏姐,用于統(tǒng)一編譯,添加完后執(zhí)行
pod install
pod 'Tools', '1'
pod 'Search-Category', :path => '../Search-Category'
pod 'Search', :path => '../Search'
pod 'Detail-Category', :path => '../Detail-Category'
- 最后將
Search
模塊中使用的圖片從主工程中放入Search
的Assets.xcassets
里,Search
模塊直接編譯運(yùn)行,檢查UI - 此時(shí)
Search
模塊的拆分就全部完成了俗扇,其他模塊同理,按照Search
模塊逐步拆分即可持灰,最后舊工程應(yīng)該只剩下全局配置代碼检诗、AppDetegate
和main
了
3.提交并上傳倉庫
提示:如果出現(xiàn)
pod search
找不到私有倉庫的情況忙菠,可以先使用rm ~/Library/Caches/CocoaPods/search_index.json
命令清除pods的索引再搜索犹菱。
當(dāng)所有組件都拆分完畢并調(diào)試無誤之后腊脱,就可以給組件打tag并提交版本了浆兰,這里我們還是以Search
為例,具體方法如下:
- 首先先編輯
Search.podspec
锦亦,s.version
為版本號孽亲,s.dependency
為依賴的庫,這里主要改這兩個(gè)展父,有的組件會(huì)依賴一些動(dòng)態(tài)庫或者靜態(tài)庫返劲,文件模版里有示例玲昧,如果還是無法通過校驗(yàn),請參照cocoapods.org - 添加好依賴之后篮绿,終端進(jìn)入
Search
孵延,輸入pod lib lint --allow-warnings --sources=myspec,master
進(jìn)行校驗(yàn) - 如果校驗(yàn)通過,輸入
git add .
添加所有文件亲配,項(xiàng)目模版配置時(shí)已包含.gitignore
因此不會(huì)添加Pods相關(guān)文件 - 輸入
git commit -m "提交信息"
尘应,完成提交 - 輸入
git tag 1
給當(dāng)前的commit打上tag - 輸入
git push
和git push --tags
,push代碼和tag - 輸入
pod repo push myspec --allow-warnings --sources=myspec,master
吼虎,將podspec文件上傳至私有podspec倉庫 - 最后在主工程里將
Podfile
中的pod 'Search', :path => '../Search'
改為pod 'Search', '~> 1
犬钢,執(zhí)行pod install
完成所有操作
看完這篇文章,你應(yīng)該已經(jīng)基本完成項(xiàng)目的組件化思灰,下一篇將闡述如何優(yōu)化組件化后的代碼以及相應(yīng)的一些規(guī)范玷犹。
參考文章
Casa Taloyum-iOS應(yīng)用架構(gòu)談-組件化方案
Skyline75489-淺析iOS應(yīng)用組件化設(shè)計(jì)
philon-iOS組件化實(shí)踐方案-LDBusMediator練就