背景
之前写过一篇 iOS 组件化实现的一些思路总结 ,这篇作为续集,聊一聊使用 Cocoapods
在iOS平台组件化的实现和集成。
结果
本文中的两个例子可以在 YTThirdPlatformManager 项目中找到。
工具介绍
Cocoapods
是iOS/osx平台的开发库管理工具,简单的配置然后执行安装命令 Cocoapods
会自动去下载第三方库并且做好相应的配置,简化了引入第三方库的流程,让开发更简单高效,是iOS开发的必备工具,使用 Cocoapods
作为组件化的工具是一个不错的选择。
安装和设置
安装和设计可以参考这篇文章:
Cocopods安装和升级备忘录
实现
简单项目组件化
以一个测试模块解耦的场景实现简单项目的组件化,主要包含以下内容
- Pod库项目创建
- 最基础podspec文件的编写的解释
- 客户端集成
创建项目
使用 pod lib create
创建项目,会遇到几个需要输入的地方,具体的解释看代码段中的注释
➜ DevPods pod lib create PTTestKit
Cloning `https://github.com/CocoaPods/pod-template.git` into `PTTestKit`.
Configuring PTTestKit template.
------------------------------
To get you started we need to ask a few questions, this should only take a minute.
If this is your first time we recommend running through with the guide:
- http://guides.cocoapods.org/making/using-pod-lib-create.html
( hold cmd and double click links to open in a browser. )
# 使用的语言
What language do you want to use?? [ Swift / ObjC ]
> Objc
# 是否包好测试工程,指定YES用于模块化的解耦测试
Would you like to include a demo application with your library? [ Yes / No ]
>
yes
# 集成的测试模块,不需要指定None
Which testing frameworks will you use? [ Specta / Kiwi / None ]
> None
# UI测试模块,不需要指定No
Would you like to do view based testing? [ Yes / No ]
> No
# 指定类前缀
What is your class prefix?
> PT
Running pod install on your new library.
podspec 文件编写
一个简单的 podspec 文件如下,具体字段的解释查看代码中的注释即可
Pod::Spec.new do |s|
s.name = 'PTTestKit'
s.version = '0.1.0'
s.summary = 'Wow PTTestKit.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
# 长的描述信息
s.description = <<-DESC
Wow this is a amazing kit,
Enjoy yourself!
DESC
# 提交到git服务区的项目主页,没提交可以指定任意值,但需要保留这一项,否则会报错
# attributes: Missing required attribute `homepage`.
s.homepage = 'https://github.com/flypigrmvb/PTTestKit'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
# 授权文件
s.license = { :type => 'MIT', :file => 'LICENSE' }
# 用户信息
s.author = { 'flypigrmvb' => '862709539@qq.com' }
# 提交到git上的源码路径,没提交可以指定任意值,但需要保留这一项,否则会报错
# attributes: Missing required attribute `source`.
s.source = { :git => 'https://github.com/flypigrmvb/PTTestKit.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
# 指定最低的ios版本
s.ios.deployment_target = '8.0'
# 源文件的路径
s.source_files = 'PTTestKit/Classes/**/*'
# 公共的头文件,按需设置
s.public_header_files = 'PTTestKit/Classes/Public/**/*.h'
# 私有的头文件,按需设置
s.private_header_files = 'PTTestKit/Classes/Private/**/*.h'
# 依赖的系统Framework,按需设置
# s.frameworks = 'UIKit', 'MapKit'
# 依赖其他的pod库,按需设置
# s.dependency 'AFNetworking', '~> 2.3'
end
客户端集成
如果在创建Pod库项目的步骤集成了 Example 测试项目,在测试项目下的podfile默认包含了当前的Pod库项目
#use_frameworks!
target 'PTTestKit_Example' do
pod 'PTTestKit', :path => '../'
target 'PTTestKit_Tests' do
inherit! :search_paths
end
end
切换到测试项目目录下,运行 pod install
命令,完了之后测试项目集成了Pod项目。下面是在测试项目中使用Pod库项目中一些功能的简单例z
#import "PTViewController.h"
#import <PTTestKit/PublicFile.h>
// !!private header 可以导入
#import <PTTestKit/PrivateFile.h>
// !!报错
//#import <PTTestKit/ProjectFile.h>
@interface PTViewController ()
@end
@implementation PTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self addActionWithName:@"Test" callback:^{
NSLog(@"====");
}];
[self addActionWithName:@"PrivateFile" callback:^{
[PrivateFile test];
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[PrivateFile test];
}
@end
分模块组件化
以一个第三发组件集成的场景为例,实现分模块组件化,主要包含以下内容:
- 创建核心模块
- 创建子模块
- 客户端集成
pod库项目的创建流程和简单模块组件步骤一致,不在赘述
podspec 文件编写
该文件创建了一个Core模块,用于存放抽象的接口、基类以及一些公用的工具类和头文件,以及几个子模块用于具体的第三方平台的实现。具体的内容可以查看以下代码中的注释内容
Pod::Spec.new do |s|
s.name = 'PTThirdPlatformKit'
s.version = '0.1.0'
s.summary = 'A short description of PTThirdPlatformKit.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/flypigrmvb/PTThirdPlatformKit'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'flypigrmvb' => '862709539@qq.com' }
s.source = { :git => 'https://github.com/flypigrmvb/PTThirdPlatformKit.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '8.0'
# 设置默认的模块,如果在pod文件中导入pod项目没有指定子模块,导入的是这里指定的模块
s.default_subspec = 'Core'
# 定义一个核心模块,用户存放抽象的接口、基类以及一些公用的工具类和头文件
s.subspec 'Core' do |subspec|
# 源代码
subspec.source_files = 'PTThirdPlatformKit/Classes/**/*'
# 配置系统Framework
subspec.frameworks = 'CoreMotion'
subspec.dependency 'SDWebImage'
# 添加依赖的系统静态库
subspec.libraries = 'xml2', 'z', 'c++', 'stdc++.6', 'sqlite3'
end
# 支付宝模块
s.subspec 'AlipayManager' do |subspec|
# 源代码
subspec.source_files = 'PTThirdPlatformKit/AlipayManager/**/*'
# 添加资源文件
subspec.resource = 'PTThirdPlatformKit/AlipayManager/**/*.bundle'
# 添加依赖第三方的framework
subspec.vendored_frameworks = 'PTThirdPlatformKit/AlipayManager/**/*.framework'
# 添加依赖系统的framework
subspec.frameworks = 'CoreTelephony', 'SystemConfiguration'
# 依赖的核心模块
subspec.dependency 'PTThirdPlatformKit/Core'
end
# QQ模块
s.subspec 'TencentManager' do |subspec|
# 源代码
subspec.source_files = 'PTThirdPlatformKit/TencentManager/**/*'
# 添加资源文件
subspec.resource = 'PTThirdPlatformKit/TencentManager/**/*.bundle'
# 添加依赖第三方的framework
subspec.vendored_frameworks = 'PTThirdPlatformKit/TencentManager/**/*.framework'
# 添加依赖系统的framework
subspec.frameworks = 'SystemConfiguration'
# 依赖的核心模块
subspec.dependency 'PTThirdPlatformKit/Core'
end
# 微博模块
s.subspec 'WeiboManager' do |subspec|
# 源代码
subspec.source_files = 'PTThirdPlatformKit/WeiboManager/**/*'
# 依赖的微博pod库
subspec.dependency 'WeiboSDK'
subspec.dependency 'PTThirdPlatformKit/Core'
end
# 微信模块
s.subspec 'WXManager' do |subspec|
# 源代码
subspec.source_files = 'PTThirdPlatformKit/WXManager/**/*'
# 依赖的微信pod库
subspec.dependency 'WechatOpenSDK'
subspec.dependency 'PTThirdPlatformKit/Core'
end
end
客户端集成
podfile文件如下,可以导入主模块和任意的子模块的组合
#use_frameworks!
platform :ios, '8.0'
target 'PTThirdPlatformKit_Example' do
pod 'PTTestKit', :path => '../../PTTestKit'
# 主模块
pod 'PTThirdPlatformKit', :path => '../'
# 子模块快
pod 'PTThirdPlatformKit/AlipayManager', :path => '../'
pod 'PTThirdPlatformKit/TencentManager', :path => '../'
pod 'PTThirdPlatformKit/WeiboManager', :path => '../'
pod 'PTThirdPlatformKit/WXManager', :path => '../'
end
target 'PTThirdPlatformKit_Example_Developer' do
pod 'PTTestKit', :path => '../../PTTestKit'
pod 'PTThirdPlatformKit', :path => '../'
pod 'PTThirdPlatformKit/AlipayManager', :path => '../'
pod 'PTThirdPlatformKit/TencentManager', :path => '../'
pod 'PTThirdPlatformKit/WeiboManager', :path => '../'
pod 'PTThirdPlatformKit/WXManager', :path => '../'
end
遇到问题
开发中的Pod库依赖另一个Pod库的处理
参考: https://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks
podspec文件添加添加
s.vendored_frameworks = 'PTDataModule/Frameworks/*.framework'
ARC环境中配置非ARC文件
需要用到podspec规则中的subspec和requires_arc
参考:
https://guides.cocoapods.org/syntax/podspec.html#requires_arc
https://guides.cocoapods.org/syntax/podspec.html#subspec
s.requires_arc = true
# no arc files rules
non_arc_files = 'PTDataModule/Classes/**/JSONKit.{h,m}'
s.exclude_files = non_arc_files
s.subspec 'no-arc' do |sna|
sna.requires_arc = false
sna.source_files = non_arc_files
end
PrefixHeader
参考:
https://guides.cocoapods.org/syntax/podspec.html#prefix_header_file
podspec指定 PTDataModule-prefixheader.pch 文件
s.prefix_header_file = 'PTDataModule/SupportFiles/PTDataModule-prefixheader.pch'
PTDataModule-prefixheader.pch 文件内容
#import "UtilMacro.h"
#import "DebugConfig.h"
#import "TIMAdapter.h"
有个坑,使用pod install 之后自定义的pch文件在项目中找不到了,但是内容会添加到PTDataModule-prefix.pch文件中
可以使用这种方式,直接把需要导入的头文件使用参数的方式指定
参考:
https://guides.cocoapods.org/syntax/podspec.html#prefix_header_contents
spec.prefix_header_contents = '#import <UIKit/UIKit.h>', '#import <Foundation/Foundation.h>'
Pod 更新不成功问题
[!] The 'Pods-PTThirdPlatformKit_Example' target has transitive dependencies that include static binaries: (/Users/aron/PuTaoWorkSpace/Plush_devpods_developer/DevPods/PTThirdPlatformKit/Example/Pods/WechatOpenSDK/OpenSDK1.8.0/libWeChatSDK.a and /Users/aron/PuTaoWorkSpace/Plush_devpods_developer/DevPods/PTThirdPlatformKit/Example/Pods/WeiboSDK/libWeiboSDK/libWeiboSDK.a)
解决办法:
use_frameworks! (注释这个)
添加.a静态库和对应的头文件 PTBehaviorStat
s.public_header_files = 'PTBehaviorStat/Classes/**/*.h', 'PTBehaviorStat/vendor/**/*.h'
s.vendored_library = 'PTBehaviorStat/**/*.a'
pod lib create 创建库拉取GitHub模板数据慢的问题
--template 参数指定本地的 模板数据
Pod开发库依赖本地开发库的问题
参考:
https://stackoverflow.com/questions/16905112/cocoapods-dependency-in-pod-spec-not-working
比如Pod开发库A依赖Pod开发库B,依赖的信息填写如下即可,不需要指定路径获取其他信息
s.dependency 'DevLibB'
在测试工程或者客户端工程的的podfile文件中需要显示的导入DevLibB,这样即可
pod 'DevLibB', :path => '../../DevLibB'
源文件没有配置对会找不到
subspec.source_files = 'PTThirdPlatformKit/WeiboManager/**/*'
如果配置(如下所示)是错误的路径,在客户端是找不到这个类的,需要检查源文件配置的是否正确
subspec.source_files = 'PTThirdPlatformKit/WeiboManager111/**/*'
总结
以上是我在使用cocoapods做组件化集成实践的一些总结和遇到的一些问题,希望对有需要的朋友有帮助。