一、前言
上篇文章主要介绍了如何在App中使用CocoaPods
引入第三方库,本篇文章将介绍怎样使用CocoaPods
进行lib
库的开发(lib
库指静态库或动态库)。
二、CocoaPods lib 创建
如果还未进行开发,需要利用CocoaPods
从零开始创建,那么恭喜你,CocoaPods
会为你搭建好整个开发环境。利用pod lib create MyLibrary
命令,通过一些向导后,会自动帮你生成lib工程及demo工程。
生成的工程目录如下:
MyLibrary.xcworkspace中的结构如下:
通过一个小小的命令,我们的工程框架就搭好了,可以在Pods工程里添加代码和文件,来实现我们的功能,然后在MyLibrary工程里可以添加对lib库的测试代码。
三、利用已有工程搭建CocoaPods lib工程
有时候,我们的lib
库已经开发很久了,也有相应的demo
,只不过想升级一下逼格或xxx的原因,想通过CocoaPods
来管理,而不是传统的.xcodeproj
或.xcworkspace
。
这种情况下,我们可以直接模仿上面的工程目录,通过编写Podfile
和podspec
,就可以了。
四、podspec
-
podspec简介
podspec
是pod specification
的缩写。通过上面的展示,已经可以知道podspec
是对lib
库的配置文件,Podfile
会根据该文件进行文件的加载,所以,podspec
的编写也是非常重要的。
-
podspec创建
podspec的创建跟Podfile一样,可以自己手动新建一个文件,然后后缀改为.podspec
。也可以命令行执行pod spec create MyLibrary
。
-
podspec编写
像Podfile
一样,podspec
也有一套自己的语法(官网介绍)。
#一个podspec文件包含一个Spec和若干个subspec,podfile可以引入整个podspec或subspec
Pod::Spec.new do |s|
#Pod的名称,必填,如Podfile中pod 'AFNetworking',AFNetworking就是name
s.name = "MyLibrary" s.version = "0.0.1" #版本,必填
#简介,必填
s.summary = "A short description of library."
#详细的描述,支持多行字符串,必填
s.description = <<-DESC
Add long description of the pod here.
DESC
#主页,必填
s.homepage = "http://EXAMPLE/MyLibrary"
#遵守的开源协议,必填。注意GPL可能给公司带来很大风险,不要轻易使用
s.license = "MIT"
#pod库作者,必填
s.author = { "wangbingwf" => "wangbingwf@163.com" }
#平台版本信息,这里表示支持iOS,7.0及以上系统
s.platform = :ios, "7.0"
#当支持多平台时,使用deployment_target替代platform
#s.osx.deployment_target = '8.0'
#代码路径,这里虽然填写的是git仓库的路径,但Podfile中使用path方式引入podspec时,并不会再从git上下载代码,而是使用本地的代码,所以就可以在这种方式下开发lib库。
#这里支持git,svn,http等。并且可以设置tag或version等信息
s.source = { :git => "https://git.coding.net/yourgit/MyLibrary.git", :tag => s.version.to_s}
# ———— Subspecs ————————————————————————————————————#
# ———— MyLibraryCFiles ———————————————————————————————#
#创建一个subspec
s.subspec 'MyLibraryCFiles' do |subcfiles|
#subspec包含的代码文件,上面source是路径,这里source_files是具体要包含哪些文件
#其中**表示包含子目录,*表示当前目录下的所有文件
#下面表示当前subspec包含MyLibrary/cfiles目录及其子目录中的所有.h和.c文件;以及MyLibrary/log目录下的所有.h和.c文件
subcfiles.source_files = ["MyLibrary/cfiles/**/*.{h,c}",
"MyLibrary/log/*.{h,c}"]
#不包含的文件
subcfiles.exclude_files = ["MyLibrary/jni/**/*",
"MyLibrary/profile/unit_test/*"]
#加入到pod库中,被一起编译
#这里通常使用私有第三方库时,需要依赖某个lib或framework时使用。
#添加如下选项后,会将.a添加到工程中,并且添加LIBRARY_SEARCH_PATHS路径
#但是需要注意的是,如果使用pod package对该pod库进行打包,这个.a并不会打进去。
#比如说使用pod package对MyLibrary打包成MyLibrary.a,inner.a并不会被编译进MyLibrary.a。
#此时,如果如果对外提供MyLibrary.a,inner.a也同样需要提供出去
subcfiles.vendored_libraries = "MyLibrary/lib/ios/inner.a"
#pod工程的配置
#对于HEADER_SEARCH_PATHS,对将设置的字符串直接拷贝到xcode中,不会像上面source_files这样使用相对路径。
#所以,我在这里先获取当前路径,再设置进去。最后加**表示recursive,即循环查找子目录的意思
$dir = File.dirname(__FILE__)
$dir = $dir + "/MyLibrary/cfiles/**" #$dir:/Users/wangbing/TempCode/MyLibrary/cfiles/**
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => $dir}
#demo工程的配置,上面是对pod工程的设置,当需要对demo工程设置时,使用user_target_xcconfig,这里就不做介绍了
#相对于public_headers,这些文件不会被公开给Demo
subcfiles.private_header_files = "MyLibrary/cfiles/**/*.h"
#保护目录结构不变,如果不设置,所有头文件都将被放到同一个目录下
subcfiles.header_mappings_dir = "MyLibrary/cfiles/**"
end
# ———— MyLibraryMain ———————————————————————————————#
#创建一个subspec
s.subspec 'MyLibraryMain' do |submain|
#引入代码文件
submain.source_files = "MyLibrary/main/**/*.{h,m}"
#设置公开头文件
submain.public_header_files = "MyLibrary/main/public.h"
#设置资源文件
submain.resource = "MyLibrary/resources/configFiles.bundle"
#设置MyLibraryMain模块依赖的系统库,注意,这里加的是系统库
submain.frameworks = "SystemConfiguration"
#设置依赖,这里可以依赖当前spec中的subspec,也可以依赖github上公开的开源库,如'AFNetworking'。
submain.dependency "MyLibrary/MyLibraryCFiles"
end
end
-
podspec坑
-
坑——HEADER_SEARCH_PATHS
在我实际项目使用过程中,C
文件的存在给我编写spec
带来了很大问题。
故事是这样的,我们项目中要引用另一个团队编写的.a
库,暂且称为inner.a
。这个库是C
写的,同时,头文件中存在多层嵌套。
这要求在使用该静态库时,要在HEADER_SEARCH_PATHS
中添加头文件的路径。
于是我一开始在podspec中是这么写的
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" =>"MyLibrary/cfiles/**"}
于是,编译出错了,找不到头文件,因为这个设置在xcode中是这样的:
Shit!为什么不像上面似的,给加相对路径呢,毕竟是路径的设置嘛。但是也可以解决的,解决方案有三:
- 如果知道Demo工程和lib库之间的路径关系,可以通过
${PODS_ROOT}/../../MyLibrary/cfiles/**
来解决。 - 通过设置绝对路径来解决:
在podspec
中使用ruby
脚本,获取当前路径,而podspec
和lib
库的位置一般不会变,所以最终使用了这种方式来设置HEADER_SEARCH_PATHS
。
$dir = File.dirname(__FILE__)
$dir = $dir + "/MyLibrary/cfiles/**" #$dir:/Users/wangbing/TempCode/MyLibrary/cfiles/**
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => $dir}
- 利用
CocoaPods
总会添加的两个默认路径,设置header_mappings_dir保护目录结构不发生变化。
#设置cfiles及子目录结构保持不变
subcfiles.header_mappings_dir = "MyLibrary/cfiles/**"
#将这些文件设置为private_file或public_file
subcfiles.private_header_files = "MyLibrary/cfiles/**/*.h"
#因为我的C头文件有嵌套,需要查找子目录,所以需要将non-recursive改为recursive
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "${PODS_ROOT}/Headers/Private/**"}
通过方法3同样使用了${PODS_ROOT}
,与方法1有什么区别呢?区别就是,如果按照方法3,可以忽略Demo工程与lib库的路径关系。因为在执行pod install
后,系统会在Pods
工程目录下生成Private
和Public
目录,就不用考虑原来cfiles
的路径了,如下图。
五、CocoaPods打包静态库
如果你开发的SDK并不想开源,那么在通过CocoaPods管理并开发之后,还可以通过CocoaPods进行打包。生成framework
或.a
。打包命令是pod package
。
像上面的例子中,我的打包命令为:pod package MyLibrary.podspec --force --no-mangle --embedded --subspecs= MyLibraryMain
。然后将生成的framework和inner.a共同交给使用者使用即可。
这里需要注意:
- 打包命令会根据
podspec
文件下载代码到一个临时文件中进行打包,并不是使用的本地文件,所以记得要提交并打tag。 - 上面的
HEADER_SEARCH_PATHS
问题,使用方法1和方法2同样会导致pod package
失败。因为你不知道cfiles
的路径,无法添加到HEADER_SEARCH_PATHS
。但却可以使用方法3! -
podspec
文件中使用vendor
标识的framework
或libraries
,并不会真正编译到最终的framework
中。这真的很让我受伤,我查了好久。。。好久。。。好久。。。也正因为这个问题,我决定放弃pod package
方式。准备尝试自己写个脚本进行打包,还在准备中。。。
六、总结
研究CocoaPods
的初衷是为了组件化开发,目前算是有了一点初步的认识。
我们把不同的功能模块放在不同的文件夹中,通过podspec
进行配置,通过podfile
进行引用,通过pod package
进行打包。一切是很方便的。
不过在我的实际应用中,目前在打包和public_headers
方面还有一些问题,I'm working on it!
最后,大家有疑惑的可以留言,写的有问题的欢迎纠正!