iOS组件化集成和开发实现
iOS组件化的应用我们采用了CTMeditor
方式实现, 以下是CTMeditor
作者的博客地址:
iOS应用架构谈 组件化方案
在现有工程中实施基于CTMediator的组件化方案
现在介绍组件化参照CTMeditor
作者实现的步骤, 本文可参照作者实现组件化方案结合使用
创建私有仓库和准备工作
-
先去开一个repo,这个repo就是我们私有Pod源仓库
image.png -
pod repo add [私有Pod源仓库名字] [私有Pod源的repo地址]
-
创立一个文件夹,例如project。把我们的主工程文件夹放到project下:~/project/FengYingXin
image.png -
在~/project下clone快速配置私有源的脚本repo:git clone git@github.com:casatwy/ConfigPrivatePod.git
-
将ConfigPrivatePod的template文件夹下Podfile中source https://github.com/ModulizationDemo/PrivatePods.git改成第一步里面你自己的私有Pod源仓库的repo地址
-
将ConfigPrivatePod的template文件夹下upload.sh中PrivatePods改成第二步里面你自己的私有Pod源仓库的名字
一: 我们先创建JSIDCard Pod
- 新建Xcode工程,命名为JSIDCard,放到projects下
- 新建Repo,命名也为JSIDCard,新建好了之后网页不要关掉
然后cd到ConfigPrivatePod下,执行./config.sh
脚本来配置JSIDCard这个私有Pod。脚本会问你要一些信息,Project Name
就是JSIDCard,要跟你的JSIDCard工程的目录名一致。HTTPS Repo
、SSH Repo
网页上都有,Home Page URL就填你JSIDCard Repo网页的URL就好了。
同样的,我们再创建JSIDCard_Category,因为它也是个私有Pod,所以也照样子跑一下config.sh
脚本去配置一下就好了。
配置完成后, 你的项目应该如下所示
然后去JSIDCard_Category下,在Podfile中添加一行pod "CTMediator"
,在podspec文件的后面添加s.dependency "CTMediator"
,然后执行pod install --verbose
。
image.png
接下来打开JSIDCard_Category.xcworkspace
,把脚本生成的名为JSIDCard_Category的空目录拖放到Xcode对应的位置下,然后在这里新建基于CTMediator的Category:CTMediator+JSIDCard。
到这里为止,JSIDCard工程和JSIDCard_Category工程就准备好了。
image.png
二:在主工程中引入JSIDCard_Category工程,并让主工程编译通过
去主工程的Podfile下添加pod "JSIDCard_Category", :path => "../JSIDCard_Category"
来本地引用JSIDCard_Category。
然后编译一下,说找不到JSIDCardViewController的头文件。此时我们把头文件引用改成#import <JSIDCard_Category/CTMediator+JSIDCard.h>
。
然后继续编译,说找不到JSIDCardViewController这个类型。看一下这里是使用了JSIDCardViewController的地方,于是我们在Development Pods下找到CTMediator+JSIDCard.h,在里面添加一个方法:
- (UIViewController *)JSIDCard_aViewControllerWithParams:(NSDictionary *)params;
image.png
再去CTMediator+JSIDCard.m中,补上这个方法的实现:
然后我们把主工程调用JSIDCardViewController的地方改为基于CTMediator Category的实现:
UIViewController *viewController = [[CTMediator sharedInstance] JSIDCard_aViewControllerWithParams:dic];
[self.navigationController pushViewController:viewController animated:YES];
再编译一下,编译通过。
三:添加Target-Action,并让JSIDCard工程编译通过
我们在JSIDCard工程中创建一个文件夹:Targets,然后看到JSIDCard_Category里面有performTarget:@"JSIDCard",所以我们新建一个对象,叫做Target_JSIDCard。
然后又看到对应的Action是viewController,于是在Target_JSIDCard中新建一个方法:Action_JSIDCardViewController。这个Target对象是这样的:
头文件:
#import <UIKit/UIKit.h>
@interface Target_JSIDCard : NSObject
- (UIViewController *)Action_JSIDCardViewController:(NSDictionary *)params;
@end
实现文件:
#import "Target_JSIDCard.h"
#import "JSIDCardViewController.h"
@implementation Target_JSIDCard
- (UIViewController *)Action_JSIDCardViewController:(NSDictionary *)params;
{
JSIDCardViewController *viewController = [[JSIDCardViewController alloc] init];
return viewController;
}
@end
然后我们再继续编译JSIDCard工程,发现找不到JSCardInfoViewController。然后我们已同样的方式创建JSCardInfoViewController
创建JSCardInfo之后, 我们对应地在JSIDCard工程中修改头文件引用为#import <JSCardInfo_Category/CTMediator+JSCardInfo.h>
,并且把调用的代码改为:
UIViewController *viewController = [[CTMediator sharedInstance] JSCardInfo_viewControllerWithContentText:@"hello, world!"];
[self.navigationController pushViewController:viewController animated:YES];
此时再编译一下,编译通过了。
四:收尾工作、组件发版
此时还有一个收尾工作是我们给JSCardInfo业务线创建了Category,但没有创建Target-Action。所以我们要去主工程创建一个JSCardInfo业务线的Target-Action。创建的时候其实完全不需要动到JSCardInfo业务线的代码,只需要新增Target_JSCardInfo对象即可:
arget_JSCardInfo头文件:
#import <UIKit/UIKit.h>
@interface Target_JSCardInfo : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
Target_JSCardInfo实现文件:
#import "Target_JSCardInfo.h"
#import "JSCardInfoViewController.h"
@implementation Target_JSCardInfo
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
NSString *contentText = params[@"contentText"];
JSCardInfoViewController *viewController = [[JSCardInfoViewController alloc] initWithContentText:contentText];
return viewController;
}
@end
接下来给这三个私有Pod发版
发版过程就是几行命令:
git add .
git commit -m "版本号"
git tag 版本号
git push origin master --tags
./upload.sh
最后,所有的Pod发完版之后,我们再把Podfile里原来的本地引用改回正常引用,也就是把:path...
那一段从Podfile里面去掉就好了,改动之后记得commit并push。
组件化实施到此结束。
思路总结和说明
以下分为代码逻辑和操作逻辑来说明
代码逻辑:
组件化分开每个人员来看,其实步骤比较简单, 如果想理解并且单独完成组件化的过程就很容易晕, 下面对组件化的具体思路进行总结
在主项目中, JSIDCardViewController是一个要被拆分的模块, 那么他要被作为组件拆分出来, 就要在外面的创建一个JSIDCard_Category类以供外部调用, 和一个业务组件JSIDCard, JSIDCard_Category的具体内容实现
NSString * const kCTMediatorTargetJSIDCardCompleted = @"JSIDCard";
NSString * const kCTMediatorActionJSIDCardViewControllerr = @"JSIDCardViewController";
@implementation CTMediator (JSIDCard)
- (UIViewController *)JSIDCard_aViewControllerWithParams:(NSDictionary *)params{
return [self performTarget:kCTMediatorTargetJSIDCardCompleted action:kCTMediatorActionJSIDCardViewControllerr params:params shouldCacheTarget:NO];
}
其中 performTarget: action: 方法是 CTMediator 组件的方法, 他主要是通过这个方法找到对应的业务组件(JSIDCard)中的文件名也是类名(Target_JSIDCard)和方法名(Action_JSIDCardViewController)
kCTMediatorTargetJSIDCardCompleted
为类名, kCTMediatorActionJSIDCardViewControllerr
方法名
而JSIDCard组件的方法实现如下
#import "Target_JSIDCard.h"
#import "JSIDCardViewController.h"
@implementation Target_JSIDCard
- (UIViewController *)Action_JSIDCardViewController:(NSDictionary *)params{
JSIDCardViewController *idcardVC = [[JSIDCardViewController alloc] initWithUserChip:params[@"userChip"]];
return idcardVC;
}
所以以上可以看出, JSIDCard_Category中的传递的类名和方法名(即自定义的静态变量kCTMediatorTargetJSIDCardCompleted
和kCTMediatorActionJSIDCardViewControllerr
)必须与业务组件JSIDCard的类名和方法名完全一致
这里的一致是指下划线后面的名称
业务组件和分类组件的生成规则也是严格按照CTMediator组件的规则来创建, 即Target_AAA和CTMediator (AAA)的方式, 业务组件中的方法名称的生成规则为Action_BBB
CTMediator内部业务逻辑就会寻找调用Target_AAA的类和你传过来的方法名称BBB来寻找对应的业务组件和方法,而这个AAA一般就是你拆分组件的前缀名称, 例如示例中就是JSIDCard,
分类一般一个组件中JSIDCard_Category和业务组件JSIDCard为同一人所完成, 那么提供给同事时, 只要组件内部遵守CTMediator的命名规则确保分类能调用到业务组件中的方法, 只要把JSIDCard_Category中对外的方法提供给对接同事即可
操作逻辑:
因为
JSIDCard_Category采用Runtime形式来调用业务组件, 所以, JSIDCard_Category的Podfile文件中是不包含业务组件的, 只包含了CTMediator框架
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
source 'https://github.com/chenchangjian/PrivatePods.git'
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'JSIDCard_Category' do
pod "CTMediator"
end
主项目中则两个组件全部包含
# open source
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/chenchangjian/PrivatePods.git'
platform :ios ,'8.0'
def default_pods
pod "JSIDCard_Category"
pod "JSIDCard"
pod "JSCardInfo_Category"
pod "JSCardInfo"
end
target 'FengYingXin' do
default_pods
end
同理, 如果有其他模块需要拆分为JSCardInfo_Category和JSCardInfo组件, 在JSIDCard 业务组件中调用就要包含JSCardInfo_Category, 主项目中也要再包含JSCardInfo_Category组件和JSCardInfo组件
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
source 'https://github.com/chenchangjian/PrivatePods.git'
source 'https://github.com/CocoaPods/Specs.git'
# use_frameworks!
target 'JSIDCard' do
pod "JSCardInfo_Category"
end
总结
组件化开发是一个高效率工作的模式, 好处很多
其中包括同事之间配合互不影响, 没有代码冲突,少了SVN上解决代码冲突的可能性, 并且个人之间代码风格可按照个人最适应的风格开发,提高开发效率
测试可以减少测试的打包体积和测试人员快速测试, 可以单独打包某个组件供测试同事测试, 只有在最后上线前打包主干代码提测即可