iOS組件化實(shí)踐(三):實(shí)施

前言

上一篇中我們對組件化的準(zhǔn)備工作做了介紹茸炒,這篇文章我們以SXNews為例進(jìn)行組件化愕乎,Demo地址在這里,殼工程獲取腳本在這里壁公,希望本文能給你帶來幫助感论。

一比肄、修改配置

根據(jù)上一篇文章所述湿硝,你應(yīng)該已經(jīng)有了ModularizationDemo文件夾,此時(shí)該文件夾中只有configOldProject兩個(gè)子文件夾示括。這時(shí)我們應(yīng)該針對項(xiàng)目情況,修改config內(nèi)容垛膝。

這里是SXNewsPodfile

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邓线、PhotoSetWeather你雌、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.hNSString+Base64.h递宅、SXDetailPage.h恐锣,對于這些項(xiàng)目內(nèi)的依賴我們暫且不管土榴,重復(fù)以上步驟拆分其他組件
  • 到這里所有的組件都應(yīng)該拆分好了赫段,但是這樣的組件由于相互依賴糯笙,是無法獨(dú)立運(yùn)行的给涕,接下來我們就通過Lothar這個(gè)中間件去除組件依賴
拆分好之后的項(xiàng)目目錄

三恭应、去除組件依賴

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

SXAdManager修改前

SXAdManager修改后
  • 編輯Tools.podspec文件,增加依賴s.dependency "HLNetworking"
編輯Tools.podspec文件
  • 提交并上傳代碼高诺,打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é)果在終端顯示如下:

完成結(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)它:
- (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

  • 完成后在AppDelegateSXDetailPage中修改錯(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.storyboardSXSearchPage控制器設(shè)置為is Inital View Controller
    • 修改Target_Search的實(shí)現(xiàn)為
- (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.storyboardNews.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模塊中使用的圖片從主工程中放入SearchAssets.xcassets里,Search模塊直接編譯運(yùn)行,檢查UI
  • 此時(shí)Search模塊的拆分就全部完成了俗扇,其他模塊同理,按照Search模塊逐步拆分即可持灰,最后舊工程應(yīng)該只剩下全局配置代碼检诗、AppDetegatemain

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 pushgit 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ī)范玷犹。

參考文章

念紀(jì)-模塊化與解耦

limboy-蘑菇街組件化之路

limboy-蘑菇街組件化之路-續(xù)

Casa Taloyum-iOS應(yīng)用架構(gòu)談-組件化方案

Skyline75489-淺析iOS應(yīng)用組件化設(shè)計(jì)

philon-iOS組件化思路-大神博客研讀和思考

philon-iOS組件化實(shí)踐方案-LDBusMediator練就

bang-iOS組件化方案探索

攜程移動(dòng)端架構(gòu)演進(jìn)與優(yōu)化之路

iOS-組件化架構(gòu)漫談

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洒疚,隨后出現(xiàn)的幾起案子歹颓,更是在濱河造成了極大的恐慌,老刑警劉巖油湖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巍扛,死亡現(xiàn)場離奇詭異,居然都是意外死亡乏德,警方通過查閱死者的電腦和手機(jī)撤奸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喊括,“玉大人寂呛,你說我怎么就攤上這事●危” “怎么了贷痪?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蹦误。 經(jīng)常有香客問我劫拢,道長,這世上最難降的妖魔是什么强胰? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任舱沧,我火速辦了婚禮,結(jié)果婚禮上偶洋,老公的妹妹穿的比我還像新娘熟吏。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布牵寺。 她就那樣靜靜地躺著悍引,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帽氓。 梳的紋絲不亂的頭發(fā)上趣斤,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音黎休,去河邊找鬼浓领。 笑死,一個(gè)胖子當(dāng)著我的面吹牛势腮,可吹牛的內(nèi)容都是我干的联贩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼捎拯,長吁一口氣:“原來是場噩夢啊……” “哼泪幌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起玄渗,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤座菠,失蹤者是張志新(化名)和其女友劉穎狸眼,沒想到半個(gè)月后藤树,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拓萌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年岁钓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片微王。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屡限,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炕倘,到底是詐尸還是另有隱情钧大,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布罩旋,位于F島的核電站啊央,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涨醋。R本人自食惡果不足惜瓜饥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浴骂。 院中可真熱鬧乓土,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拦键,卻和暖如春谣光,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芬为。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工萄金, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人媚朦。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓氧敢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親询张。 傳聞我的和親對象是個(gè)殘疾皇子孙乖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容