组件化和CTMediator

2020-12-24  本文已影响0人  forping

之前已经聊过CTMediator, 和中间者架构

这次借助cocoapods简单实现一下中间者架构和组件化
首先借一张图,描绘cocoapods私有仓库Repo,和私有 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

有两种方案

  1. 是创建好工程,再创建spec文件,之后编辑spec文件
  2. 直接使用命令创建模板文件,把代码放到模板里,编辑spec文件

方案一

新建A_section Xcode 工程

把 A 业务组件化出来,也就是MainProject 中的 A界面。
把代码上传到pods库.有两种方案

  1. 把 A_section 的私有仓库 clone 下来(刚创建,所以是空仓库),然后把 A_section 的 Xcode 工程文件全部放进去,然后 push 到仓库,
  2. 使用 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

此时组件化和中间者架构的重构就完成了.

上一篇下一篇

猜你喜欢

热点阅读