美文网首页
组件化和CTMediator

组件化和CTMediator

作者: forping | 来源:发表于2020-12-24 10:30 被阅读0次

    之前已经聊过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 代码。

    有两种方案

    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
    

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

    相关文章

      网友评论

          本文标题:组件化和CTMediator

          本文链接:https://www.haomeiwen.com/subject/riwynktx.html