3篇文章带你学会组件化
1.iOS 创建远程cocoapods 私有库
2.iOS Pod 私有库创建(自定义的组件)
3.iOS CTMediator组件化实践
首先你要懂得组件化的整体思想,如下图,就是通过一个中间者传递信息,用来降低模块间的耦合度, 从而达到高内聚低耦合的目的.
提到的中间层就是CTMediator, GitHub上面介绍的非常详细, 还有使用文章推荐.
废话不多说, 开始实践:
- 目的: 项目的ViewController 的touchesBegan方法push 新界面(AViewController); 分解成通过CTMediator 和组件通信获取aVc , 来达到相同功能.
步骤详解
1. 创建组件A_Section, 可参考前一篇文章自定义组件
- GitHub 上创建A_Section组件远程仓库
- 创建本地项目 (pod lib create A_Section)
- 修改.podspec 文件中的homepage 和source 和version
#发版版本号,每更新一次代码就改变一次版本号
s.version = "0.0.1"
#你的 git 仓库首页的网页 url,注意并不是 https/ssh这种代码仓库地址
s.homepage = "https://xxxxxxxxx/A_Section"
#这里就是你 git 仓库的 https/ssh 地址了
s.source = { :git => "git@gitxxxxxxxx/A_Section.git", :tag => "#{s.version}" }
- 创建AViewController 和 Target_A 文件, 在选择文件夹的时候记得选classes
注
: 一个组件对应一个 Target_A, 这里要特殊记忆下Target_A
的A
和Action_viewController
的viewController
;
// Target_A.h文件
#import
NS_ASSUME_NONNULL_BEGIN
@interface Target_A : NSObject
/// 获取该组件的AViewController 的实例化对象
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
// Target_A.m 文件
#import "Target_A.h"
#import "AViewController.h"
@implementation Target_A
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
AViewController *viewController = [[AViewController alloc] init];
return viewController;
}
@end
- 这时的A_Section工程目录应该是这样的:
- 提交组件项目更新内容, 组件发版打tag, 将repo源添加到自己的私有repo索引库中.可参考自定义组件5~8步骤
2. 创建中间件A_Category(CTMediator 的Category)
- 同一业务模块使用同一中间件就可以, 这里只是文件夹名称, 可随意, 这里使用A_Category
- 创建远程仓库A_Category
- 修改.podspec 文件, 依赖CTMediator 第三方库
- cd /A_Section/Example 目录下, 更新(pod update --no-repo-update)
- 创建CTMediator 的分类CTMediator+A
- CTMediator (A)内部实现如下:
// CTMediator+A.h 内容
#import
@interface CTMediator (A)
- (UIViewController *)getAViewController;
@end
// CTMediator+A.m 内容
#import "CTMediator+A.h"
@implementation CTMediator (A)
- (UIViewController *)getAViewController
{
/*
AViewController *viewController = [[AViewController alloc] init];
*/
// 这个方法就牛逼了, CTMediator 会自动去找Target_A 的类, 并且去寻找是否存在viewController 方法, 存在就去实现
return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];
}
@end
注
: 还记得步骤一第四小步需要特殊记忆的Target_A
的A
和Action_viewController
的viewController
吗? 其实对应的就是performTarget:@"A"
中的A
和action:@"viewController"
中的viewController
, 名字都可以随便起, 但是必须是在对应的前提下;
- 配置之后的文件结构:
- 提交组件项目更新内容, 组件发版打tag, 将repo源添加到自己的私有repo索引库中.可参考自定义组件5~8步骤
3. 本地创建主工程MainProject
- 就是创建个名字为MainProject(名称可随意) 的Xcode项目, pod init -> pod install 操作完成即可.
- 尽量保证A_Section 和A_Category 和MainProject 在同一文件夹下或同级目录下, 方便后续操作.
4. 本地组件测试
- cd至MainProject目录下, 修改profile 文件:
target 'ManiProject' do
use_frameworks!
#组件化依赖
pod 'CTMediator'
#本地测试
pod 'A_Category', :path => '../A_Category'
pod 'A_Section', :path => '../A_Section'
end
注
: pod 'A_Category', :path => '../A_Category'
代表在当前的podfile目录下, (../)返回上一级目录, 找到A_Category文件, 拉取其中A_Category.podspec文件.
- 更新库文件( pod install)
- 测试组件是否能正常使用, MainProject中的ViewController 类中测试push 功能
// ViewController.m文件内容
#import "ViewController.h"
// 引入中间件头文件
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// aVc 其实就是AViewController
UIViewController *aVc = [[CTMediator sharedInstance] getAViewController];
[self.navigationController pushViewController:aVc animated:YES];
}
@end
注
: 引入A_Category
的头文件, 通过[[CTMediator sharedInstance] getAViewController]
获取组件提供的东西.
这个时候能正常实现push 功能, 就算本地的组价可以正常使用了.
4. 远程仓库的组件的使用测试
- 修改podfile 文件, 添加私有源repo索引库.不会的话可参考创建远程cocoapods 私有库
- 添加需要使用组件的依赖
podfile 文件大致为:
#这是GitHub的源索引库
source 'https://github.com/CocoaPods/Specs.git'
#这是我自己的组件的源索引库
source '[email protected]:LiHe0308/PrivateSpecs.git'
target 'ManiProject' do
use_frameworks!
#组件化依赖
pod 'CTMediator'
#组件
pod 'A_Category'
pod 'A_Section'
end
- 主工程pod install 更新库文件
- 测试push 功能, 代码一点不需要变
这时候功能正常, 就代表远程组件可以正常使用了.
总结注意点:
-
主工程、组件、之间的关系
1. 组件A_Section: 将相当于正常开发的一个文件夹, 但是多了一个Target_A类, Target_A中对外(CTMediator 的分类)开放整个组件的功能,所以整个组件的功能都写在Target_xName中.例:
- (UIViewController *)Action_viewController:(NSDictionary *)params;
2. A_Category: 主要是使用CTMediator 的分类,CTMediator (A), 重点就是命名, 一定要和组建中Target后面的名称相同, 也就是A.
通过方法return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];实现和组件通信, 获取自动寻找Target_A 文件中的viewController 来执行
然后封装方法 - (UIViewController *)getAViewController; 供外界使用.
3. 主工程ManiProject: 就是你所做的项目, 它只与CTMediator (A)通讯, 引入头文件,
通过分类中自定义的方法[[CTMediator sharedInstance] getAViewController]; 获取到组件
-
代码的提交顺序, 严格遵守!!!
1. 一定要先提交所有变更信息
git remote add origin [email protected]:xxx.git
git add .
git commit -a -m "提交变更信息"
git push -u origin HEAD
2. 修改xxxxx.podspec 文件中homepage 和 source, 一定要准确
s.homepage -> 你的 git 仓库首页的网页 url,注意并不是 https/ssh这种代码仓库地址
s.source -> 这里就是你 git 仓库的 https/ssh 地址了
3. 修改s.version
s.version = 0.2.0
4. 发版 -> 一定要与.podspec 文件中s.version 相同
git tag 0.2.0
git push origin 0.2.0
5. 将组建repo 地址推到私有源索引库中 (只做一次就可以, 且必须在.podspec同级目录下操作)
pod repo push [私有源名称] 组件名称.podspec --allow-warnings
-
关于 xib 和图片
组件中若用到了图片,或者 xib 资源,要指定资源的文件路径,否则不会把图片打包到你的组件中:
s.resource_bundles = {
'A_Section' => ['A_Section/AImages/**/*.{png}', 'A_Section/Classes/*.{xib}']
}
或者
s.resources = ['A_Section/AImages/**/*.{png}', 'A_Section/Classes/*.{xib}']
如果使用 Xocde 工程自带的 Assets 那个文件夹的话,图片也无法打包到组件中,最好自己重新创建一个新的文件夹用来存放图片资源;
然后代码中获取 xib, png 等 resource 时,bundle 重新设置,这样就保证了无论在组件中,还是在 MainProject 工程中,都可以配置到正确的 Bundle,如果你使用 s.resource_bundles={},配置了自定义的 bundle名称,那么 [bundle pathForResource:@"A_Section" ofType:@"bundle"]中就要替换成相应的名称.
//mainBundle
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *bundlePath = [bundle pathForResource:@"A_Section" ofType:@"bundle"];
if (bundlePath)
{
//组件资源所在的 bundle
bundle = [NSBundle bundleWithPath:bundlePath];
}
.End