[JiaJung]&iOS组件化(中篇)-拆分业务组件
在上一篇中对基础组件进行了拆分,接下来会拆分业务组件。业务组件最简单的理解(比如,有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 <UIKit/UIKit.h>
#import <CTMediator/CTMediator.h>
#import <FFAPIsKit/APIRequest.h>
#import <FFAPIsKit/FFReformProtocol.h>
@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 <UIKit/UIKit.h>
#import <CTMediator/CTMediator.h>
#import <FFAPIsKit/APIRequest.h>
@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以及一些图片资源,我将在下一篇中详细说明。再次感谢您的阅读。欢迎大家评论交流。
小礼物走一走,来简书关注我
楼主你好,在验证FFAuthorKit组件的spec文件时,老是报一个错误,如下:
FFAuthorKit (0.1.5)
- ERROR | [iOS] xcodebuild: Returned an unsuccessful exit code.
- NOTE | xcodebuild: note: Using new build system
- NOTE | [iOS] xcodebuild: note: Planning build
- NOTE | [iOS] xcodebuild: note: Constructing build description
- NOTE | [iOS] xcodebuild: warning: The iOS Simulator deployment
target is set to 4.3, but the range of supported deployment target
versions for this platform is 8.0 to 12.1. (in target 'FMDB')
没找到解决方法,求指导!
楼主你好,能不能讲一讲主工程,或者出个主工程的demo。
“FFAuthorDetailController”,“FFAuthorListReformer”这几个文件相关的代码在制作组件时注释掉了,为什么没有作者取消注释,如果不取消注释,岂不是业务上要有问题?
有个问题:就是不通的业务组件都依赖同一个基础组件时都会引入同一个基础组件,那样同一个基础组件岂不是引入了两遍。
您好,看了您的文章收获很多,现在也在按照您的思路做组件化,现在想请教一个问题:组件中的文件层及目录显示您是怎么解决的呢?我现在是所有的文件都罗列在一个文件夹下们很难看
建议用markdown写,不然阅读性不是很好
188道iOS中高级核心面试题,我只能帮你在这里了!
在2018年底,小编混迹在各种iOS交流群中,整理出了将近两百道大厂最喜欢在面试问到的问题,今天在这里分享给大家[免费获取方式在最后]! 小编就不在这里全部列举出来了,可以在前面的看到,文档里面包括了目前比较受欢迎的所有面试题!有需要获取的话,可以加QQ群:67988454...
iOS持续集成—Jenkins(最新最全)
搭建Jenkins前,请确认mac系统上已经搭建好了Java环境。从零开始一步一步构建,遇到了很多坑,好在最终success了。 一、 搭建Jenkins 1. 安装Jenkins 从官网上下载pkg安装包 这里以jenkis-2.73.3.pkg包为例 安装完成后,Saf...
iOS大厂面试题总结
1. OC语法 1. OC中对象的结构(腾讯一面) Instance对象如果是NSObject对象,对象中只有一个isa指针,在64位中占16个字节(可以通过malloc_size函数获得),但实际只用到了8个字节(可以通过class_getInstanceSize函数获得...
深入iOS系统底层之映像操作API介绍
绿树阴浓夏日长,楼台倒影入池塘。--《唐高骈·山亭夏日》 mach-o文件和进程的映像(image) iOS系统生成的可执行程序或者动态库文件的存储布局格式被称之为mach-o格式。文件中存放着程序的代码和数据,而程序运行时系统会为其建立一个进程,以及分配虚拟内存空间。同时...
鹅厂,iOS高级开发精选面试题!
前言 为防止背题,大部分题目不设标准答案,重点考察面试者的基础知识和思维逻辑,答案的提示见后面。 正文 题目1、举例两个遇到过印象深刻的外网Crash,并介绍如何发现、定位、解决;题目2、举例两个性能问题的优化,并介绍如何发现、定位、解决以及原理;题目3、介绍Objecti...
今天在微信群里看到有人吵架,然后又提到言论自由的问题。想想真是搞笑,怎么会有和谐的言论自由呢,每个人都有发表自已想法的权利这没错,但如果别人不同意呢,要不要辩护还是个问题呢。如果辩护,那问题来了,嘴上说说,支言片语,一定会演变成吵架,不要幻想什么和谐的辩论环境,人性的弱点不...
概述 UIView类通过定义一个在屏幕和界面上的矩形区域来管理这块区域的内容。在运行时,视图对象处理其区域内的任何内容渲染,还处理与该内容的任何相互作用。 进入正题 一、初始化视图对象 现在开始测试: 先构建一个自定义View: TestInitView, 并添加两个初始化...
T










浙公网安备 33010602011771号