第二篇主要講以下幾點(diǎn):
1怀泊、創(chuàng)建私有庫瞪醋。
2柳琢、對(duì)主工程進(jìn)行組件化改造(主工程的邏輯有viewController->A->B)
3凡恍、將主工程的A頁面做成A組件,解耦主工程和A頁面
4房待、使用CTMediator方案(target-action)進(jìn)行組件化搭建(核心在于每個(gè)組件的category可以對(duì)組件和主功能之間的解耦操作)
5邢羔、最終的效果是完成主工程與A組件的通信驼抹,A組件和主工程中B頁面的通信
tips:當(dāng)前組件化方案純oc版本,swift還多幾個(gè)工程文件张抄,這一期不涉及砂蔽;另外github使用ssh的話洼怔,需要設(shè)置一個(gè)sshkey,這一期不涉及署惯,直接百度就好,或者我有時(shí)間了也寫一下镣隶;
一极谊、準(zhǔn)備工作
1、首先要搞清楚為什么創(chuàng)建私有庫安岂?
創(chuàng)建私有庫的目的是為了讓團(tuán)隊(duì)成員可以共享私有庫組件轻猖,項(xiàng)目用到該組件的時(shí)候可以直接pod下來組件及其category,下面以github為例域那,創(chuàng)建私有庫咙边,并且對(duì)不同工程間的關(guān)系進(jìn)行說明
2、如何創(chuàng)建私有庫
然后點(diǎn)擊下面的CreateRepository即可次员。
3败许、打開終端,本地建立一個(gè)私有倉庫并關(guān)聯(lián)github上的私有倉庫LSDRepo淑蔚,
pod repo add [私有Pod源倉庫名字] [私有Pod源的repo地址]
添加完成之后不會(huì)有任何返回市殷,我們可以查看添加好的私有庫
說明已經(jīng)添加完成。
4刹衫、創(chuàng)立一個(gè)文件夾醋寝,例如Project。
(1)把我們的主工程文件夾放到Project下:~/Project/MainProject
(2)在~/Project下clone快速配置私有源的腳本repo:git clone git@github.com:casatwy/ConfigPrivatePod.git
(3) 將ConfigPrivatePod的templates_objc文件夾下Podfile中source '[git@pkg.poizon.com:duapp/iOS/DUSpecs.git]'
改成第一步里面你自己的私有Pod源倉庫的repo地址git@github.com:DemoComponentization/LSDRepo.git
(tips:因?yàn)槲冶緳C(jī)的cocoaPods源是清華的带迟,所以我把source 'https://github.com/CocoaPods/Specs.git'
改成了source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
)音羞,此時(shí)podfile文件內(nèi)容大致如下
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
source 'git@github.com:DemoComponentization/LSDRepo.git'
use_frameworks!
use_modular_headers!
target '__ProjectName__' do
pod "CTMediator"
# private pods
pod "HandyFrame"
end
(4)將ConfigPrivatePod的templates_objc文件夾下upload.sh中所有DUSpecs
改成第二步里面你自己的私有Pod源倉庫的名字(LSDRepo
),我當(dāng)時(shí)修改的位置為第13仓犬,14嗅绰,62行,三處婶肩。
至此办陷,文件目錄結(jié)構(gòu)應(yīng)當(dāng)如下
Project
├── ConfigPrivatePod
└── MainProject
如果沒有問題,那說明準(zhǔn)備工作已經(jīng)完成律歼。
二民镜、明確業(yè)務(wù)拆分
MainProject是一個(gè)非常簡單的應(yīng)用,一共就三個(gè)頁面险毁。首頁push了AViewController制圈,AViewController里又push了BViewController们童。我們可以理解成這個(gè)工程由三個(gè)業(yè)務(wù)組成:首頁、A業(yè)務(wù)鲸鹦、B業(yè)務(wù)慧库,我們這一次組件化的實(shí)施目標(biāo)就是把A業(yè)務(wù)組件化出來,首頁和B業(yè)務(wù)都還放在主工程馋嗜。所以齐板,我們需要為此做兩個(gè)私有Pod:A業(yè)務(wù)Pod(以后簡稱A Pod)、方便其他人調(diào)用A業(yè)務(wù)的CTMediator category的Pod(以后簡稱A_Category Pod)葛菇。所有的組件都有自己的Category甘磨,工程和組件之間,組件和工程之間通信都是通過Category眯停。
1济舆、新建Xcode工程,命名為A莺债,放到Projects下
此時(shí)文件目錄結(jié)構(gòu)如下:
Project
├── ConfigPrivatePod
├── MainProject
└── A
2滋觉、github上新建Repository,命名也為A齐邦,新建好了之后網(wǎng)頁不要關(guān)掉
然后cd到ConfigPrivatePod下椎侠,執(zhí)行./config_objc.sh
腳本來配置A這個(gè)私有Pod。腳本會(huì)問你要一些信息侄旬,Project Name就是A肺蔚,要跟你的A工程的目錄名一致。HTTPS Repo儡羔、SSH Repository網(wǎng)頁上都有宣羊,Home Page URL就填你A Repository網(wǎng)頁的URL。
說明創(chuàng)建成功汰蜘,并且關(guān)聯(lián)好了私有庫的一些參數(shù)仇冯。
3、修改podspec文件
在A工程中族操,將主工程中的AViewController的.h和.m文件移動(dòng)到Source文件夾下苛坚,修改A.podspec
文件中s.source_files
的內(nèi)容為s.source_files = "A/Source/**/*.{h,m}"
,大概在98行色难。這樣做的目的是將來pod組件的時(shí)候會(huì)把Source文件夾下的所有文件pod到你的項(xiàng)目里泼舱,所以你可以把將來需要pod的文件都放到Source文件夾下。
現(xiàn)在A工程的文件目錄結(jié)構(gòu)大致如下:
A
|── A
| ├── Category
| ├── Source
| │ ├── AViewController.h
| │ └── AViewController.m
| ├── AppDelegate.h
| ├── AppDelegate.m
| ├── ViewController.h
| ├── ViewController.m
| └── main.m
└── A.xcodeproj
4枷莉、重復(fù)步驟1娇昙,2,3笤妙,完成A_Category的準(zhǔn)備工作
創(chuàng)建A的Category=>A_Category冒掌,在Project文件夾下創(chuàng)建工程A_Category噪裕,github上創(chuàng)建相同名字的Repository,然后cd到ConfigPrivatePod文件夾下股毫,執(zhí)行./config_objc.sh
腳本來配置A_Category這個(gè)私有Pod膳音,上述步驟準(zhǔn)備就緒之后,A_Category的目錄結(jié)構(gòu)大致如下:
A_Category
├── A_Category
│ ├── Category
│ ├── Source
│ │ └── Target
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Info.plist
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── A_Category.podspec
├── A_Category.xcodeproj
├── FILE_LICENSE
├── Podfile
├── readme.md
└── upload.sh
然后在A_Category文件夾下铃诬,在Podfile中添加一行pod "CTMediator"(如果有祭陷,就不用管了),在A_Category.podspec
文件的后面打開或者添加s.dependency "CTMediator"
,修改s.source_files = "A_Category/Source/*.{h,m}"
氧急,然后執(zhí)行pod install --verbose颗胡。完成之后打開A_Category工程毫深,在Source文件夾下創(chuàng)建基于CTMediator的Category:CTMediator+A吩坝,刪除無用文件,最終目錄結(jié)構(gòu)大致如下:
A_Category
├── A_Category
│ ├── Source
│ │ ├── CTMediator+A.h
│ │ └── CTMediator+A.m
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Info.plist
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── A_Category.podspec
├── A_Category.xcodeproj
├── FILE_LICENSE
├── Podfile
├── readme.md
└── upload.sh
到此哑蔫,A和A_Category的準(zhǔn)備工作就做好了钉寝。
3、完善代碼邏輯使得每個(gè)部分都可以獨(dú)立運(yùn)行
1闸迷、完善主工程及其A_Category
打開主工程嵌纲,在Podfile下添加pod "A_Category", :path => "../A_Category"
來本地引用A_Category,終端執(zhí)行pod install。然后編譯一下腥沽,說找不到AViewController的頭文件逮走。此時(shí)我們把AViewController.h
引用改成#import <A_Category/CTMediator+A.h>
。然后繼續(xù)編譯今阳,說找不到AViewController這個(gè)類型师溅。看一下這里是使用了AViewController的地方盾舌,于是我們?cè)贒evelopment Pods下找到CTMediator+A.h墓臭,在里面添加一個(gè)方法(本質(zhì)修改的是A_Category工程中Source文件夾下的CTMediator+A.h和CTMediator+A.m):
- (UIViewController *)A_aViewController;
再去CTMediator+A.m中,補(bǔ)上這個(gè)方法的實(shí)現(xiàn)妖谴,把主工程中調(diào)用的語句作為注釋放進(jìn)去窿锉,將來寫Target-Action要用:
- (UIViewController *)A_aViewController
{
/*
AViewController *a = [[AViewController alloc] init];
*/
return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];
}
補(bǔ)充說明一下,performTarget:@"A"中給到的@"A"其實(shí)是Target對(duì)象的名字膝舅。一般來說嗡载,一個(gè)業(yè)務(wù)Pod只需要有一個(gè)Target就夠了,但一個(gè)Target下可以有很多個(gè)Action仍稀。Action的名字也是可以隨意命名的洼滚,只要到時(shí)候Target對(duì)象中能夠給到對(duì)應(yīng)的Action就可以了。至此琳轿,完成了Category的代碼編寫判沟,后面也不需要再進(jìn)行改動(dòng)耿芹。接下來把主工程調(diào)用AViewController的地方改為基于CTMediator Category的實(shí)現(xiàn):
UIViewController *a = [[CTMediator sharedInstance] A_aViewController];
[self.navigationController pushViewController:a animated:YES];
現(xiàn)在的話,編譯就沒問題了挪哄。這時(shí)候點(diǎn)擊按鈕沒有反應(yīng)吧秕,因?yàn)槲覀冞€沒實(shí)現(xiàn)A工程的Target-Action。
2迹炼、實(shí)現(xiàn)A工程的代碼邏輯砸彬,使得編譯通過
打開兩個(gè)工程:A_Category工程和A工程。
我們?cè)贏工程中的Source文件夾下創(chuàng)建一個(gè)文件夾:Targets斯入,然后看到A_Category里面有performTarget:@"A"砂碉,所以我們?cè)赥argets文件夾下新建一個(gè)對(duì)象,叫做Target_A刻两。然后又看到對(duì)應(yīng)的Action是viewController增蹭,于是在Target_A中新建一個(gè)方法:Action_viewController。這個(gè)Target對(duì)象是這樣的:
//A.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface A : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
//A.m
#import "A.h"
#import "AViewController.h"
@implementation A
- (UIViewController *)Action_viewController:(NSDictionary *)params
{//寫實(shí)現(xiàn)的時(shí)候磅摹,對(duì)照著之前在A_Category里面的注釋去寫就可以了
AViewController *a = [[AViewController alloc] init];
return viewController;
}
@end
另外補(bǔ)充一點(diǎn)滋迈,Target對(duì)象的Action設(shè)計(jì)出來也不是僅僅用于返回ViewController實(shí)例的,它可以用來執(zhí)行各種屬于業(yè)務(wù)線本身的任務(wù)户誓。例如上傳文件饼灿,轉(zhuǎn)碼等等各種任務(wù)其實(shí)都可以作為一個(gè)Action來給外部調(diào)用,Action完成這些任務(wù)的時(shí)候帝美,業(yè)務(wù)邏輯是可以寫在Action方法里面的(Action具備調(diào)度業(yè)務(wù)線提供的任何對(duì)象和方法來完成自己的任務(wù)的能力碍彭。它的本質(zhì)就是對(duì)外業(yè)務(wù)的一層服務(wù)化封裝。)悼潭。
現(xiàn)在重新編譯庇忌,發(fā)現(xiàn)找不到BViewController。由于我們這次組件化實(shí)施的目的僅僅是將A業(yè)務(wù)線抽出來女责,BViewController是屬于B業(yè)務(wù)線的漆枚,所以我們沒必要把B業(yè)務(wù)也從主工程里面抽出來。但為了能夠讓A工程編譯通過抵知,我們需要提供一個(gè)B_Category來使得A工程可以調(diào)度到B墙基,同時(shí)也能夠編譯通過。
B_Category的創(chuàng)建步驟跟A_Category是一樣的刷喜,創(chuàng)建好B_Category(注意B_Category.spec文件里同樣要設(shè)置依賴項(xiàng)和A_Category一樣)之后残制,Source文件夾下的兩個(gè)類如下:
//CTMediator+B.h
#import <CTMediator/CTMediator.h>
#import <UIKit/UIKit.h>
@interface CTMediator (B)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText;
@end
//CTMediator+B.m
#import "CTMediator+B.h"
@implementation CTMediator (B)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"contentText"] = contentText;
return [self performTarget:@"B" action:@"viewController" params:params shouldCacheTarget:NO];
}
@end
我們?cè)贏工程的Podfile文件中pod "B_Category",:path=>"../B_Category"
,執(zhí)行pod install,編譯之前把涉及到BViewController的頭文件和代碼實(shí)現(xiàn)用B_Category及其方法替換掖疮,修改如下:
UIViewController *viewController = [[CTMediator sharedInstance] B_viewControllerWithContentText:@"hello, world!"];
[self.navigationController pushViewController:viewController animated:YES];
現(xiàn)在編譯初茶,就沒有問題了,但是需要注意的是浊闪,A組件的依賴項(xiàng)需要添加一個(gè)B_Category恼布,所以A.podspec文件的依賴項(xiàng)修改如下:
s.dependency "B_Category"
s.dependency "CTMediator"
現(xiàn)在組件A被完全剝離出主工程螺戳,成為了獨(dú)立的組件,A和主工程折汞,A和B之間完全解耦倔幼,唯一的問題是,點(diǎn)擊A頁面不會(huì)push到B頁面爽待,因?yàn)锽的Action-target還沒寫损同。所以我們要去主工程創(chuàng)建一個(gè)B業(yè)務(wù)線的Target-Action。創(chuàng)建的時(shí)候其實(shí)完全不需要?jiǎng)拥紹業(yè)務(wù)線的代碼鸟款,只需要新增Target_B對(duì)象即可:需要注意的是膏燃,主工程的Podfile文件中現(xiàn)在可以添加pod "A",:path=>"../A"
,然后pod install何什。
//Target_B.h:
#import <UIKit/UIKit.h>
@interface Target_B : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
//Target_B.m:
#import "Target_B.h"
#import "BViewController.h"
@implementation Target_B
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
NSString *contentText = params[@"contentText"];
BViewController *viewController = [[BViewController alloc] init];
return viewController;
}
@end
Target_B對(duì)象在主工程內(nèi)不存在任何侵入性组哩,將來如果B要獨(dú)立成一個(gè)組件的話,把這個(gè)Target對(duì)象帶上就可以了富俄〗矗總結(jié)一下:
1、target類是和組件捆綁在一起的霍比,它對(duì)象命名規(guī)則和方法的命名規(guī)則是根據(jù)組件的Category中performTarget Action方法確定的
2、組件的Category中Source文件夾下的Category用來給組件進(jìn)行通信
3暴备、組件的target是用來代替組件接受消息的悠瞬,這里接受數(shù)據(jù),然后對(duì)組件進(jìn)行操作涯捻,因?yàn)檫@個(gè)工程里面可以引用到當(dāng)前組件類浅妆。
至此,本地的組件化方案就已經(jīng)完成了障癌,接下來我們要做的事情就是給這三個(gè)私有Pod(A凌外、A_Category、B_Category)發(fā)版涛浙,發(fā)版之前去podspec里面確認(rèn)一下版本號(hào)和dependency康辑。
4、發(fā)版
發(fā)版前需要強(qiáng)調(diào)一點(diǎn)轿亮,編譯運(yùn)行主工程能后實(shí)現(xiàn)組件化之前同樣的功能并且所有邏輯都沒問題之后再發(fā)版疮薇。
發(fā)版主要用的幾個(gè)命令:
git add .
git commit -m "版本號(hào)"
git tag 版本號(hào)
git push origin master --tags
./upload.sh
命令行cd進(jìn)入到對(duì)應(yīng)的項(xiàng)目中,然后執(zhí)行以上命令就可以了我注。需要注意的是這里的版本號(hào)要和podspec文件中的s.version給到的版本號(hào)一致按咒。upload.sh是配置私有Pod的腳本生成的,如果你這邊沒有upload.sh這個(gè)文件但骨,說明這個(gè)私有Pod你還沒用腳本配置過励七。當(dāng)然智袭,組件化流程詳解(一)中介紹了從創(chuàng)建倉庫到發(fā)版的全過程,不熟悉這個(gè)腳本特性的掠抬,也可以按我那個(gè)教程操作补履。
最后,所有的Pod發(fā)完版之后剿另,我們?cè)侔裀odfile里原來的本地引用改回正常引用箫锤,也就是把:path...那一段從Podfile里面去掉就好了,改動(dòng)之后記得commit并push雨女。
最后感謝下教我文檔和基友DC