组件化和CTMediator
之前已经聊过CTMediator, 和中间者架构
这次借助cocoapods
简单实现一下中间者架构和组件化
首先借一张图,描绘cocoapods私有仓库Repo,和私有 Pod的关系
组件化操作流程:
- 添加私有 repo 源
- 创建主工程
- 创建 A_section 的 Xcode 工程和对应的私有pods
- 创建 A_Category 的 Xode 工程和对应的私有pods
- 解决主工程编译不通过的问题
- 为 A_section 工程创建 Target(一个组件对应一个 Target-Action)
- 解决A_section工程编译不通过的问题
- 准备发版 pod
添加私有repo源
本文使用的是 codeing.net
代码托管平台
pod repo add [私有Repo源仓库名字] [repo地址]
创建主工程
示例
在桌面新建文件夹 Project,新建Xcode项目工程 Project/MainProject,作为我们的主工程,
简单实现 push 一个 A界面,然后在 A 界面 push 到 B 界面。
创建 A_section 的 Xcode 工程和对应的私有 pods
新建私有的 A_section Pods
- 在
coding .net
上创建一个私有的代码仓库,起名 A_section,用来存放 A_section 代码。
有两种方案
- 是创建好工程,再创建spec文件,之后编辑spec文件
- 直接使用命令创建模板文件,把代码放到模板里,编辑spec文件
方案一
新建A_section Xcode 工程
把 A 业务组件化出来,也就是MainProject 中的 A界面。
把代码上传到pods库.有两种方案
- 把 A_section 的私有仓库 clone 下来(刚创建,所以是空仓库),然后把 A_section 的 Xcode 工程文件全部放进去,然后 push 到仓库,
- 使用 git remote add origin 命令,最终都是达到一个目的 。
git remote add origin git@e.coding.net:forping/a_section/A_Section.git
git push -u origin master
把 A_section 配置成私有 Pod
cd到 A_section 文件夹下
pod spec create A_section git@e.coding.net:forping/a_section/A_Section.git
方案二
创建一个模板
pod lib create A_section
填写信息
hat platform do you want to use?? [ iOS / macOS ]
> iOS
What language do you want to use?? [ Swift / ObjC ]
> ObjC
Would you like to include a demo application with your library? [ Yes / No ]
> No
Which testing frameworks will you use? [ Specta / Kiwi / None ]
> None
Would you like to do view based testing? [ Yes / No ]
> No
What is your class prefix?
> FP
编辑spec文件
编辑完A_section.podspec,把 A_section 的业务代码,移动到 s.source_files 指定的文件夹中,以保证 pod 发版的时候,这些文件能被发布出去。
spec文件
Pod::Spec.new do |s|
# 名字
s.name = "A_section"
#发版版本号,每更新一次代码就改变一次版本号
s.version = "0.0.1"
#一个简单的总结,随便写
s.summary = "A short description of A_section."
#描述,随便写 但是要比 s.summary 长度长
s.description = <<-DESC
short description of A_section short description of A_section
DESC
#你的 git 仓库首页的网页 url,注意并不是 https/ssh这种代码仓库地址
s.homepage = "https://coding.net/u/xxxx/p/A_section"
# 屏幕快照
# spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
#许可,可以直接写 MIT
s.license = "MIT"
# spec.license = { :type => "MIT", :file => "FILE_LICENSE" }
#作者
s.author = { "" => "" }
#或者直接
# spec.author = "aiyanbo"
# 个人主页
# spec.social_media_url = "https://twitter.com/aiyanbo"
#一定要写上,不写的话,执行 pod lib lint 验证项目的时候会报找不到 UIKIT 等框架错误
s.platform = :ios, "8.0"
# 当用在多个平台上
# spec.ios.deployment_target = "5.0"
# spec.osx.deployment_target = "10.7"
# spec.watchos.deployment_target = "2.0"
# spec.tvos.deployment_target = "9.0"
#git 仓库的 https/ssh 地址了
s.source = { :git => "https://git.coding.net/xxxx/A_section.git", :tag => "#{s.version}" }
#这里的文件夹下的内容就是这个 pods 被pod install 的时候会被下载下来的文件,不在这个文件夹,将不会被引用
# Classes 目录和.podspec 目录是平级的。
#你可以随便指定文件夹名称,只要这个文件夹是真实存在的
#Classes/**/*.{h,m},表示 Classes 文件夹及其文件夹下的所有.h,.m 文件。
spec.source_files = "Classes", "Classes/**/*.{h,m}"
# 不包含的文件夹
spec.exclude_files = "Classes/Exclude"
#公开的头文件
# spec.public_header_files = "Classes/**/*.h"
#资源文件地址,下面的所有.png资源都被打包成 s.name.bundle
# s.resource = ['Images/*.png','Sounds/*']
# spec.resource = "icon.png"
# spec.resources = "Resources/*.png"
#指定公有头文件,如果没有写,那么所有 pod 中的头文件都默认公有,可以被 import。如果指定了某些头文件,那么只有这些被指定的头文件才可以被 import。
s.public_header_files = 'Classes/Public/*.h'
#资源文件地址,和 resource 的区别是,这个属性可以指定 bundle 的名字,下面的所有.png文件都会被打包成 ABC_section.bundle
s.resource_bundle = {
'ABC_section' => ['Classes/ABCImage/*png']
}
# 与其他框架或库链接起来。库不包含其名称的lib前缀。
# spec.framework = "SomeFramework"
# spec.frameworks = "SomeFramework", "AnotherFramework"
# spec.library = "iconv"
# spec.libraries = "iconv", "xml2"
#这个 pods 还依赖于其他哪些 pods
s.dependency "B_Category"
s.dependency "HandyFrame"
创建 A_Category 的 Xode 工程和对应的私有 Repo
按照创建 A_section的流程,我们创建 A_Category 的 Xode 工程和对应的私有 Pods,同样让你的 A_Category工程托管到你的 A_Category 私有仓库上。
编辑A_Category.podspec文件,
然后去A_Category下,在Podfile中添加一行pod "CTMediator",执行pod install。
然后在s.source_file对应的文件夹中新建基于CTMediator的Category:CTMediator+A。
CTMediator+A.h,在里面添加一个方法:
@interface CTMediator (A)
- (UIViewController *)A_aViewController;
@end
@implementation CTMediator (A)
- (UIViewController *)A_aViewController
{
/*
AViewController *viewController = [[AViewController alloc] init];
*/
return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];
}
@end
解决主工程编译不通过的问题
去主工程的Podfile下添加,然后执行 pod install
pod "CTMediator"
pod "A_Category", :path => "../A_Category"
把项目中头文件引#import AViewController.h用改成#import <A_Category/CTMediator+A.h>。
再把主工程调用AViewController的地方改为基于CTMediator Category A的实现
到此为止主工程就改完了,现在跑主工程点击这个按钮跳不到A页面是正常的,因为我们还没有在MainProject工程中引入 A_section 组件,也没有在A_section工程中实现Target-Action。
此时主工程中关于A业务的改动就全部结束了,后面的组件化实施过程中,就不会再有针对A业务线对主工程的改动了。
为 A_section 工程创建 Target(一个组件对应一个 Target-Action)
我们在A_section工程中创建一个文件夹:Target,然后看到A_Category里面有performTarget:@"A",所以我们新建一个对象,叫做Target_A。
然后又看到对应的Action是viewController,于是在Target_A中新建一个方法:Action_viewController。
#import <UIKit/UIKit.h>
@interface Target_A : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
#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工程编译不通过的问题
为了能够让A_section工程编译通过,我们需要提供一个B_Category来使得A_section工程可以调度到B,同时也能够编译通过。
新建 B_Category Xcode 工程, 托管到对应的私有远程仓库, 配置.podspec 文件,配置B_Category.podspec 文件的时候,在最后加上 s.dependency "CTMediator"(这样是因为下一步 A_section 从本地 pod B_Category 的时候也可以pod 到CTMediator) ,pod "CTMediator", 创建CTMediator+B 分类代码,
@interface CTMediator (B_Category)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText;
@end
@implementation CTMediator (B_Category)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText
{
/*
BViewController *viewController = [[BViewController alloc] initWithContentText:@"hello, world!"];
*/
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"contentText"] = contentText;
return [self performTarget:@"B" action:@"viewController" params:params shouldCacheTarget:NO];
}
@end
B_Category添加好后,我们在A_section工程的Podfile中本地指过去
pod "B_Category", :path => "../B_Category"
然后我们对应地在A_section工程中修改头文件引用为#import <B_Category/CTMediator+B.h>,并且把调用的代码改为:
UIViewController *viewController = [[CTMediator sharedInstance] B_viewControllerWithContentText:@"hello, world!"];
[self.navigationController pushViewController:viewController animated:YES];
此时 A业务线跟B业务线就已经完全解耦了,跟主工程就也已经完全解耦了。
由于我们给B业务线创建了Category,但没有创建Target-Action。所以我们要去MainProject创建一个B业务线的Target-Action。创建的时候其实完全不需要动到B业务线的代码,只需要新增Target_B对象即可:
#import <UIKit/UIKit.h>
@interface Target_B : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
#import "Target_B.h"
#import "BViewController.h"
@implementation Target_B
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
NSString *contentText = params[@"contentText"];
BViewController *viewController = [[BViewController alloc] initWithContentText:contentText];
return viewController;
}
@end
此时,组件化的基本工作已经完成.
发布 pod
1.先在本地测试
接下来我们就要为发版 pod 做准备,我们先在本地实验 pod 可用不可用,此时我们的 Project 文件夹中应该有四个文件夹
.
├── A_Category
├── A_section
├── B_Category
└── MainProject
修改MainProject 中的 podfile 文件
target 'MainProject' do
# Uncomment this line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Private Pods
pod "CTMediator"
pod "A_Category", :path => "../A_Category"
pod "A_section", :path => "../A_section"
pod "B_Category", :path => "../B_Category"
end
然后执行pod install,目的是测试在本地情pod 是否有问题。没问题的话,此时你运行 MainProject 达到的效果应该是和没有组件化之前是完全一样的。
2.发布组件
发布pod之前要检查依赖是否正确
A_section的依赖
s.dependency "B_Category"
A_Category 的依赖
s.dependency "CTMediator"
B_Category 的依赖
s.dependency "CTMediator"
之后就可以把这三个私有 pods 放到私有Repo.
//1. 把代码push 到 git 上
git add .
git commit -m "initial pod"
git push
// 2. 为这版的代码打上 tag 号,tag 号一定要和.podspec 文件的 s.version 号一致
git tag 0.0.1
git push --tags
// 3.本地验证和远程验证
// 同时依赖私库和公共库,直接本地校验会出错,
pod lib lint
// 校验podspec文件时会到远程podspec库查找相关依赖,默认只会到官方specs库校验,此时需要指定远程specs库去校验。----sources指定源
// pod spec lint [spec文件] --sources='https://私库.git,https://github.com/CocoaPods/Specs.git'
pod spec lint
// 4. 发布 pod 到私有 Repo
pod repo push PrivatePodRepo A_section.podspec --verbose --allow-warnings
然后修改 MainProject 的 Podfile 文件
#添加上你的私有 Pods 的地址
source 'https://git.coding.net/xxxxx/PrivatePodRepo.git'
source 'https://github.com/CocoaPods/Specs.git'
target 'MainProject' do
pod "CTMediator"
pod "A_Category"
pod "A_section"
end
此时组件化和中间者架构的重构就完成了.