组件化实践

最近想了解一些组件化的知识,去看了Casa写的 iOS应用架构谈 组件化方案 这篇文章,Casa在文中针对蘑菇街的组件化方案提出了一些不同的观点,陈述了自己的组件化方案。

大神们讨论具体的实施方案,是对理论的描述,在架构层面来分析利弊,我看过之后感觉还是有点晦涩,具体的方案异同之处我们先不说,今天我们先从 应用 着手,在自己当前的工程实施组件化。

当然了,我们选择使用的方案是Casa的 CTMediator

准备

首先我们得先了解组件化这个概念,其实通俗的讲,就是把我们的项目拆解成一个一个的小组件分别管理。我们平时使用cocoapods继承的三方的库,可以理解成是一个公有的组件。我们项目中,也可以把一些模块拆解出来,使用cocoapods来集成。这样拆解成一个个的组件的好处有很多,比如说业务模块之间解耦,复用模块,节省编译时间等等。

所以我们要先学会创建cocoapods私有库。

这里多说一句,Casa的组件化方案在实施的时候,每独立出来一个组件,就会相应的创建一个Category工程,作为中间的调度,所以说,我们每做一个组件,就要创建两个私有的pod工程。

我们结合Casa这篇 在现有工程中实施基于CTMediator的组件化方案 ,来做一下补充或者说是注解吧,本文中的流程取自于上文。

创建私有Pod工程

1. 先去开一个repo,这个repo就是我们私有Pod源仓库
2. pod repo add [私有Pod源仓库名字] [私有Pod源的repo地址]
3. 创立一个文件夹,例如Project。把我们的主工程文件夹放到Project下:~/Project/MainProject
4. 在~/Project下clone快速配置私有源的脚本repo:git clone git@github.com:casatwy/ConfigPrivatePod.git
5. 将ConfigPrivatePod的template文件夹下Podfile中source 'https://github.com/ModulizationDemo/PrivatePods.git'改成第一步里面你自己的私有Pod源仓库的repo地址
6. 将ConfigPrivatePod的template文件夹下upload.sh中PrivatePods改成第二步里面你自己的私有Pod源仓库的名字

首先我们先创建一个名为Project的文件,然后把我们项目的主程序,我们叫做MainProject放到Project路径下,然后在Project路径下clone出我们需要的脚本(Casa提供)

在~/Project下clone快速配置私有源的脚本:
git clone git@github.com:casatwy/ConfigPrivatePod.git

现在我们的文件目录结构是这样的。

Project
├── ConfigPrivatePod(脚本文件)
└── MainProject

在Project路径下创建我们的组件工程(一个普通的iOS工程),我们把这个工程名字叫PayComponents (模拟抽取项目中的支付模块)。

当前目录结构

Project
├── ConfigPrivatePod
├── MainProject
└── PayComponents

有了本地的工程之后,我们现在需要创建一个repo,作为我们的私有pod源仓库。也就是在github,或者gitee(码云)上面创建一个项目,放我们的项目代码,命名PayComponents。

然后呢,我们还需要创建一个东西,就是私有Pod源仓库名字。

pod repo add [私有Pod源仓库名字] [私有Pod源的repo地址]

落实到我们这个项目中,我们应该这样写。

pod repo add PayComponents https://gitee.com/LittleBin/PayComponents.git

那这到底代表着我们创建了什么?

我们打开finder->前往->前往文件夹,然后输入 ~/.cocoapods/repos

可以看到目录是这样子的

repos路径下面有一个master,一个payComponents。这两个文件夹我们可以粗略的认为他和pod search还有install有关。

打个比方就拿search来说,我们查询一个库的时候会用下面这个命令

pod search AFNetworking

然后会从master路径下找到AFNetworking,然后列出来他有哪些版本什么的。我们有的时候会发现一个库其实已经跟新到2.x.x版本,但是我们search出来只有1.x.x,这也可能是我们的cocoapods没有更新,我们的master路径下没有新的版本。

这个PayComponents文件夹,就代表我们本地有一个私有的pod库。我们search的时候,也会查这些本地的私有库。

下面把远程的这个repo和我们本地创建的项目关联到一起,这个工作Casa给我们提供的脚本就可以完成,顺便还会帮我们生成.podspec的文件,具体这个文件的作用我们后面再说,还会初始化podfile。

我们进到ConfigPrivatePod文件中,执行config.sh脚本,然后在终端根据提示输入就行了。

[localhost:ConfigPrivatePod sunxiaobin$ ./config.sh 


Enter Project Name: PayComponents
Enter HTTPS Repo URL: https://gitee.com/LittleBin/PayComponents.git
Enter SSH Repo URL: git@gitee.com:LittleBin/PayComponents.git
Enter Home Page URL: https://gitee.com/LittleBin/PayComponents

================================================
  Project Name  :  PayComponents
  HTTPS Repo    :  https://gitee.com/LittleBin/PayComponents.git
  SSH Repo      :  git@gitee.com:LittleBin/PayComponents.git
  Home Page URL :  https://gitee.com/LittleBin/PayComponents
================================================

confirm? (y/n):y
copy to ../PayComponents/FILE_LICENSE
copy to ../PayComponents/.gitignore
copy to ../PayComponents/PayComponents.podspec
copy to ../PayComponents/readme.md
copy to ../PayComponents/upload.sh
copy to ../PayComponents/Podfile
editing...
edit finished
cleaning...
Initialized empty Git repository in /Users/fmb/Documents/LEARN/Project_test/PayComponents/.git/
clean finished
finished
localhost:ConfigPrivatePod sunxiaobin$

Enter Project Name:的时候,后面的名字一定要跟我们创建的PayComponents工程一样,要不然脚本找不到文件,就配置不了。

脚本跑完之后PayComponents里面变成下面这样子。

我们要修改一下Podfile文件和upload.sh。下面是生成的Podfile文件里面的内容

# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'

source 'https://github.com/ModulizationDemo/PrivatePods.git'
source 'https://github.com/CocoaPods/Specs.git'

use_frameworks!

target 'PayComponents' do
 
end

因为脚本都是照着模板来生成的这些文件,上面是podfile文件里面的内容,我们要把第一个source后面的 'https://github.com/ModulizationDemo/PrivatePods.git' 改成我们这个库的地址, https://gitee.com/LittleBin/PayComponents.git

然后在upload.sh中,最后一行

pod repo push PrivatePods PayComponents.podspec --verbose --allow-warnings --use-libraries --use-modular-headers

把PrivatePods替换为我们上面的私有pod库名称。

也就是我们之前执行的下面这个命令中的PayComponents。

//不用重复执行
pod repo add PayComponents https://gitee.com/LittleBin/PayComponents.git

我们在看一下PayComponents项目文件的结构,脚本给我们生成了一个空的PayComponents文件。

我们需要把这个文件拖拽到我们的项目中

然后我们这个PayComponents组件的文件就全部放到这个路径下。我们的私有库暂时就先写到这里。下面创建我们的Category工程

创建Category工程

因为篇幅原因,这里我把创建Category工程略过一下,不一一展示了。

其实按道理说在实际项目中,这个Category工程也要做成私有库的,就像我们上面说的步骤,先创建本地工程,创建远程的repo,然后创建本地私有库(pod repo add ...),在用脚本关联本地和远程的库,然后修改Podfile和upload.sh等等这一整套步骤。

最终Category项目的结构是下面这样的。(包含为CTMediator添加的分类)

我们调用PayComponents组件里面的vc都是通过这个category来调用的。所以说我们的这个Pay_category工程应该在Podfile文件中加上 pod 'CTMediator' ,然后执行 pod install

我们之前有遇到过这种情况,我们在使用A库的时候,pod install之后,会自动帮我们导入A库依赖的B库,回到我们的项目也就是说当我们在主工程里面集成Pay_Category这一组件的时候,应该默认帮我们把CTMediator库和PayComponents组件也集成进工程。这就要修改Pay_Category.podspec文件。

在文件的最底部,end之前加上下面两句

s.dependency "CTMediator"
s.dependency "PayComponents"

说回我们新建的这个CTMediator+pay文件,我们要在主工程MainProject跳转到PayComponents里面的VC,所以CTMediator+pay应该提供一个返回vc的方法,如下:

- (UIViewController *)PayViewController {
  
    return [self performTarget:@"PayManager" action:@"pay" params:nil shouldCacheTarget:NO];
}

我们想不去管return后面的代码是什么意思,只要他是返回了vc给我们就可以。

然后在MainProject里面的跳转代码就如下:

// 导入CTMediator+pay头文件
UIViewController * viewController = [[CTMediator sharedInstance] PayViewController];
[self.navigationController pushViewController:viewController animated:YES];

下面我们再来说 return [self performTarget:@"PayManager" action:@"pay" params:nil shouldCacheTarget:NO]; 是什么意思。

我们可以从CTMediator的源码中,看一下 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget 这个方法。

那这里我就不绕圈子里,直接说结论,其实在上述的方法中是找到了一个叫Target_PayManager的类,调用了它里面的Action_pay方法,这个前缀Target_和Action_是在方法内部拼接的,所以说我们可以得出,CTMediator是通过反射拿到类名和方法名,然后调用,得到目标vc的。所以说在PayComponents工程中,我们还得创建Taregt文件来做间接的调用。

这里的Target_PayManager文件的文件名后半部分PayManager要跟 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget 里面的targetName对应起来,然后类里面的方法名字也得对应。(PayViewController就是我们的目标文件)

- (UIViewController *)Action_pay:(NSDictionary *)param {
    
    PayViewController *viewController = [[PayViewController alloc] init];
    viewController.param = param;
    return viewController;

}

最后我们在MainProject的Podfile文件中引入Pay_Category库就行了。这个时候我们的私有库可能还没有完全创建成,所以我们可以用这种导入本地的方法

pod "Pay_Category", :path => "../Pay_Category"

当然如果私有库做好了,就只需要 pod "Pay_Category" 就行了。

完善私有库

在Casa的博客中说到,可以使用他的upload.sh脚本来更新私有库代码,我用过它的脚本,总是有错无,而且库的版本号也是依次+1的形式,版本号我喜欢x.x.x这种,所以我选择自己提交代码,更新私有库。

针对于每个工程来说,首先是一些基本的提交代码操作,

git add .

git commit -m “新版本号“

git tag 新版本号

git push origin master —tags

要更新私有库,我们拿PayComponents这个项目来说,执行下面的指令

//私有库升级
pod repo push PayComponents  PayComponents.podspec

这个PayComponents就是我们pod repo add时候起得名字,后面是.podspec文件,这操作等于把我们的私有库更新推到我们本地的库里面。 注意.podspec里面的版本号要记得更新,与tag一致。

那这种私有库我们项目的其他成员,还是拿PayComponents这个来说,首先在代码管理库上面得给他们下载代码的权限,然后执行下面:

//待核实~ 
pod repo add PayComponents https://gitee.com/LittleBin/PayComponents.git

公有库

补充一点,那如果想做成公有库,让大家都可以使用要怎么搞?

执行下面操作:

pod trunk push PayComponents.podspec

就可以把组件推到Cocoapods主仓库。别人就可以通过pod search来查找你的库了。

pod trunk push 可能失败,因为首次使用trunk需要注册自己的电脑。

pod trunk register [E-mail] [User Name]

执行完成之后,会受到一封验证邮件,按邮件提示完成验证即可。

全部都完成了之后pod search也可能会搜不到自己的库,这时候可以尝试把缓存删掉

使用命令:rm ~/Library/Caches/CocoaPods/search_index.json

清除后,再重新搜索,此时CocoaPod会重新创建搜索索引。

这只是一些可能的原因,具体的问题需要具体针对解决。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章