在上一篇中对基础组件进行了拆分,接下来会拆分业务组件。业务组件最简单的理解(比如,有3个tabbar专题、作者、商城三个模块,就可以理解成3个业务组件)。功能组件也很好理解,比如我们项目中用到的轮播器、播放器、图片浏览器等都可以单独抽出功能组件。
在拆分业务组件之前,需要了解组件间通信的概念?笔者这里要说的是业务组件间的通信。比如专题组件A、作者组件B、点击专题A组件中cell的作者头像图标跳转到B组件中“作者详情”的界面。这就是简单的组件间通信,假如不允许直接import引入依赖,该怎么进行跳转?
关于组件间的通信的几种方式以及利弊《这篇文章》已经说的很详细了。笔者采用的就是文章中提出的基于Mediator模式和Target-Action模式组件化方案,中间采用了runtime来完成调用。基于Mediator模式和Target-Action模式组件化方案如下图所示,会用到开源库CTMediator。
由上图可见,专题A组件和作者B组件都对外提供了一个Target的API类(Target_A和Target_B), 同时需要为A组件和B组件创建对应的A_category组件和B_category组件(Category组件依赖CTMediator库),Category组件需要提供A_CTM分类、和B_CTM分类,A_CTM分类、和B_CTM分类里的方法对应了这个Target下所有可能的调用场景,这样调用者在包含mediator的时候,自动获得了所有可用的Target-action,无论是调用还是参数传递,都非常方便。为什么是Category而不是其他 。
以下内容摘自《iOS应用架构谈 组件化方案》
1. category本身就是一种组合模式,根据不同的分类提供不同的方法,此时每一个组件就是一个分类,因此把每个组件可以支持的调用用category封装是很合理的。
2. 在category的方法中可以做到参数的验证,在架构中对于保证参数安全是很有必要的。当参数不对时,category就提供了补救的入口。
3. category可以很轻松地做请求转发,如果不采用category,请求转发逻辑就非常难做了。
4. category统一了所有的组件间调用入口,因此无论是在调试还是源码阅读上,都为工程师提供了极大的方便。
5. 由于category统一了所有的调用入口,使得在跨模块调用时,对于param的hardcode在整个App中的作用域仅存在于category中,在这种场景下的hardcode就已经变成和调用宏或者调用声明没有任何区别了,因此是可以接受的。
接下来一步步实现业务组件拆分
1. 首先远程和本地创建专题组件A和作者组件B。
2. 通过pod lib create在本地创建相应的代码模版.同样把special、author文件夹拖入到对应的FFSpecialKit、FFAuthorKit的classes路径下面。
3. 修改.podspec索引文文件和Podfile文件后执行pod install,修改ReactiveCocoa和Result的UseLegacySwiftLanguage Version 设置成YES。
4. 编译此时会报错各种文件找不到。新建一个FFSpecialKit.h的文件专门引入头文件。然后将原来#import “public.h” 替换成#import “FFSpecialKit.h"
5. 再次编译会报下面的文件找不到,然后先将“FFAuthorDetailController”,“FFAuthorListReformer”这几个文件相关的代码注释了.再次编译会报下面的错误,然后同样将相关代码注释掉编译就OK了。
6. FFSpecialKit组件编译通过之后,按照同样的方式处理FFAuthorKit组件并使其编译通过.接下来为组件FFSpecialKit 和FFAuthorKit创建对应的Target_Special和Target_Author文件(创建完之后执行一次pod insall)
```
#import"Target_Author.h"
#import"FFAuthorDetailController.h"
#import"FFAuthorListReformer.h"
#import"AuthorAPIRequest.h"
@implementationTarget_Author
- (UIViewController*)Action_authorDetailViewController:(NSDictionary*)params {
return[[FFAuthorDetailControlleralloc]init];
}
- (NSDictionary*)Action_authorReformerWithOriginData:(NSDictionary*)params {
FFAuthorListReformer*reformer = [[FFAuthorListReformeralloc]init];
returnparams ? [reformerreformData:params] :nil;
}
- (NSDictionary *)Action_authorReformer:(NSDictionary*)params {
return[[FFAuthorListReformeralloc]init];
}
- (APIRequest*)Action_authorAPIRequest:(NSDictionary*)params {
return[[AuthorAPIRequestalloc]init];
}
@end
```
```
#import
#import"FFSpecialKit.h"
@interfaceTarget_Special :NSObject
- (UIViewController*)Action_specialDetailViewController:(NSDictionary*)params;
- (NSDictionary*)Action_specialReformerWithOriginData:(NSDictionary*)params;
- (NSDictionary *)Action_specialReformer:(NSDictionary*)params;
- (APIRequest*)Action_specialAPIRequest:(NSDictionary*)params;
@end
```
```
#import"Target_Special.h"
#import"FFSpecialDetailController.h"
#import"FFSpecialListReformer.h"
#import"SpecialAPIRequest.h"
@implementationTarget_Special
- (UIViewController*)Action_specialDetailViewController:(NSDictionary*)params {
return[[FFSpecialDetailControlleralloc]init];
}
- (NSDictionary*)Action_specialReformerWithOriginData:(NSDictionary*)params {
FFSpecialListReformer*reformer = [[FFSpecialListReformeralloc]init];
returnparams ? [reformerreformData:params] :nil;
}
- (NSDictionary *)Action_specialReformer:(NSDictionary*)params {
return[[FFSpecialListReformeralloc]init];
}
- (APIRequest*)Action_specialAPIRequest:(NSDictionary*)params {
return[[SpecialAPIRequestalloc]init];
}
@end
```
7. 接下来就该创建对应的FFSpecialKit_Category 和FFAuthorKit_Category组件了。
8. 修改FFSpecialKit_Category对应的podspec文件和Podfile文件.
9. 创建对应的CTMediator+Special分类(创建分类时的存储路径需要注意一下)
```
#import
#import
#import
#import
@interfaceCTMediator (Special)
- (UIViewController*)specialDetailController;
- (NSDictionary*)specialReformerWithOriginData:(NSDictionary*)data;
- (NSDictionary *)specialReformer;
- (APIRequest*)specialAPIRequest;
@end
```
```
#import"CTMediator+Special.h"
@implementationCTMediator (Special)
- (UIViewController*)specialDetailController {
return[selfperformTarget:@"Special"action:@"specialDetailViewController"params:nilshouldCacheTarget:NO];
}
- (NSDictionary*)specialReformerWithOriginData:(NSDictionary*)data {
return[selfperformTarget:@"Special"action:@"specialReformerWithOriginData"params:datashouldCacheTarget:NO];
}
- (NSDictionary *)specialReformer {
return[selfperformTarget:@"Special"action:@"specialReformer"params:nilshouldCacheTarget:NO];
}
- (APIRequest*)specialAPIRequest {
return[selfperformTarget:@"Special"action:@"specialAPIRequest"params:nilshouldCacheTarget:NO];
}
@end
```
10. 编译通过,按照同样的方式修改FFAuhtorKit_Category的podspec文件和Podfile文件,创建对应的CTMediator+Ahthor分类(创建完之后记得先pod install一下);然后编写分类分类内容如下.
```
#import
#import
#import
@interfaceCTMediator (Author)
- (UIViewController*)authorDetailViewController;
- (NSDictionary*)authorReformerWithOriginData:(NSDictionary*)data;
- (NSDictionary *)authorReformer;
- (APIRequest*)authorAPIRequest;
@end
```
```
#import"CTMediator+Author.h"
@implementationCTMediator (Author)
- (UIViewController*)authorDetailViewController {
return[selfperformTarget:@"Author"action:@"authorDetailViewController"params:nilshouldCacheTarget:NO];
}
- (NSDictionary*)authorReformerWithOriginData:(NSDictionary*)data {
return[selfperformTarget:@"Author"action:@"authorReformerWithOriginData"params:datashouldCacheTarget:NO];
}
- (NSDictionary *)authorReformer {
return[selfperformTarget:@"Author"action:@"authorReformer"params:nilshouldCacheTarget:NO];
}
- (APIRequest*)authorAPIRequest {
return[selfperformTarget:@"Author"action:@"authorAPIRequest"params:nilshouldCacheTarget:NO];
}
@end
```
11. 到此都创建完毕之后就可以提交FFSpecialKit_Category和FFAuhtorKit_Category组件了.还是通过下面步骤提交组件.
- git add .
- git commit -m “xxx"
- git remote add origin 远程代码仓库地址
- git push origin master
- git tag 版本号 (注:这里的版本号必须和podspec里写的版本号一致)
- git push --tags
12. 通过pod spec lintFFSpecialKit.podspec - -verbose - -allow-warnings 验证索引文件的有效性,这里需要说明一些细节问题,验证时会报下面的错误.
报这个错的原因是校验podspec文件时默认只会到官方specs库(https://github.com/CocoaPods/Specs.git)去校验,需要同时指定自己创建的远程索引库地址库校验。
解决如下:pod spec lint FFSpecialKit.podspec --verbose --allow-warnings --sources='https://github.com/FFComponent/FFSpecs.git,https://github.com/CocoaPods/Specs.git’
继续验证报下面的错误
解决办法:在终端输入**`echo "2.3" > .swift-version` **
继续通过上面的命令验证,报下面的错误.
解决办法:
第一步:回到FFSpecialKit.h 导入到文件
第二步: 将原来**#import “FFSpecialListReformerKeys.h"** 的地方注释掉,替换成**#import“FFSpecialKit.h"**
第三步:编译一下组件,编译通过后,需要重新修改一下**FFSpecialKit.podspec**文件中的版本号,重新提交,重新打tag(上一次tag是1,这次tag累加为2) (因为代码做了修改,所以需要重新提交)
第四步:提交并打好tag后,再次进行验证就ok了.
第五步:最后,通过pod repo push ...提交索引文件就完成了。
按照上面的方式提交组件FFAuthorKit,都提交成功之后回到项目FlowerField_Component,然后修改Podfile文件,然后执行pod install.
安装完成之后,编译运行.
中篇到这里就结束了,最后需要说明一下,因为点击专题cell时跳转过去的详情界面,我特意用了XIB。这里点击cell进行跳转操作时会奔溃,具体如何去处理组件化过程中XIB以及一些图片资源,我将在下一篇中详细说明。再次感谢您的阅读。欢迎大家评论交流。