iOS CTMediator组件化实践

3篇文章带你学会组件化
1.iOS 创建远程cocoapods 私有库
2.iOS Pod 私有库创建(自定义的组件)
3.iOS CTMediator组件化实践

首先你要懂得组件化的整体思想,如下图,就是通过一个中间者传递信息,用来降低模块间的耦合度, 从而达到高内聚低耦合的目的.

组件化思想

提到的中间层就是CTMediator, GitHub上面介绍的非常详细, 还有使用文章推荐.

废话不多说, 开始实践:

  • 目的: 项目的ViewController 的touchesBegan方法push 新界面(AViewController); 分解成通过CTMediator 和组件通信获取aVc , 来达到相同功能.
    普通->组件

步骤详解

1. 创建组件A_Section, 可参考前一篇文章自定义组件

  1. GitHub 上创建A_Section组件远程仓库
  2. 创建本地项目 (pod lib create A_Section)
  3. 修改.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}" }
  1. 创建AViewController 和 Target_A 文件, 在选择文件夹的时候记得选classes
    组件内添加内容

    : 一个组件对应一个 Target_A, 这里要特殊记忆下Target_AAAction_viewControllerviewController ;
// 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
  1. 这时的A_Section工程目录应该是这样的:
    当前工程目录
  2. 提交组件项目更新内容, 组件发版打tag, 将repo源添加到自己的私有repo索引库中.可参考自定义组件5~8步骤

2. 创建中间件A_Category(CTMediator 的Category)

  1. 同一业务模块使用同一中间件就可以, 这里只是文件夹名称, 可随意, 这里使用A_Category
  2. 创建远程仓库A_Category
  3. 修改.podspec 文件, 依赖CTMediator 第三方库
  4. cd /A_Section/Example 目录下, 更新(pod update --no-repo-update)
  5. 创建CTMediator 的分类CTMediator+A
  6. 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_AAAction_viewControllerviewController吗? 其实对应的就是performTarget:@"A"中的Aaction:@"viewController"中的viewController , 名字都可以随便起, 但是必须是在对应的前提下;

  1. 配置之后的文件结构:
    A_Category结构目录
  2. 提交组件项目更新内容, 组件发版打tag, 将repo源添加到自己的私有repo索引库中.可参考自定义组件5~8步骤

3. 本地创建主工程MainProject

  1. 就是创建个名字为MainProject(名称可随意) 的Xcode项目, pod init -> pod install 操作完成即可.
  2. 尽量保证A_Section 和A_Category 和MainProject 在同一文件夹下或同级目录下, 方便后续操作.
    我的文件位置, 都在桌面上了

4. 本地组件测试

  1. 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文件.

  1. 更新库文件( pod install)
  2. 测试组件是否能正常使用, 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. 远程仓库的组件的使用测试

  1. 修改podfile 文件, 添加私有源repo索引库.不会的话可参考创建远程cocoapods 私有库
  2. 添加需要使用组件的依赖
    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
  1. 主工程pod install 更新库文件
  2. 测试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

你可能感兴趣的:(iOS CTMediator组件化实践)