CocoaPods 集成私有库

前言

Private Spec Repo, 我把它叫做私有库。有更好的叫法,请一定告知我,不胜感谢。

CocoaPods version: 1.2.1.

什么时候用

CocoaPods is a great tool not only for adding open source code to your project, but also for sharing components across projects. You can use a private Spec Repo to do this.

官方说法,想要要在不同的项目中分享组件,这时你该创建私有库了。

怎么做

官方文档写的真是简洁,本以为实际操作也会如此! 是我太年轻了。

按照文档上说就三步:

  • Create a Private Spec Repo
  • Add your Private Repo to your CocoaPods installation
  • Add your Podspec to your repo

创建一个 repo, 添加 repo 到你的 CocoaPods 里, 然后推送 podspec 到 repo 里。

当然,这只是概述。会有各种细节在等着你,让你无所事事,不得寸进。

会在下面来说下最近一段时间我的历程和爬过的坑。

私有库和公有库的异同

Podfile 里继承 AFNetworking: pod 'AFNetworking'

当我们执行 pod install 后,CocoaPods 做了什么?

CocoaPods 会去 ~/.cocoapods/repos/master/Specs/ 目录下寻找是否有 AFNetworking 的 podspec?如果没有,则退出,并给出提示无法找到;如果有,则按照 podspec 中记录的第三方库的 GitHub 地址去下载并继承到工程里。

其中,master/Specs 相当于一个索引文件,里面记录的都是各个第三方库的url,然后当我们需要安装的时候,再去通过这些 url 去下载具体的第三方库。

而 master/Specs 是一个 CocoaPods 的 github repo,专门用来负责存储第三方库 url 的索引文件。

我们平时发布的库的操作有2个步骤。一个是创建库并封装功能上去,然后将封装好的库的url告知CocoaPods。这样,其他人就能获得我们发布的三方库了。

所以,我们明晰了想要一个第三方库生效的2个条件:

  • 必定有一个 repo 存储在服务器上,他人可以获得.
  • 有一个中心化的 Specs repo 用来存储第三方库的索引文件 podspec.

针对上面,公有库和私有库的区别就很明显了。

Item 公有库 私有库
库 repo github - 公开项目 随意(想放哪就放哪)
索引中心 Specs github - CocoaPods/Specs Specs (想放哪就放哪)

私库库 repo 的位置不定,可以在任何位置,只要能够索引到就行。有的时候也可以不是 repo,而是可下载的文件。私有/Specs 代替 CocoaPods/Specs 用来作为私有库的中心.

创建私有库具体过程

下面将要交大家如何一步步去创建私有库。已 gitlab 为事例服务器。当然你也可以在别的git服务器上创建,如:coding.net,或者公司内部的git服务器。

1.gitlab 上创建 lib repo 和 Specs repo (Specs 是用来管理私有 lib repo 的)

私有的 lib repo :

cocoapodsDemo1.png

Specs repo :

specs repo

这里已经有内容了,其实就是建立一个名字为 Specs 的空 repo.

2.创建 pod lib repo

$ pod lib create PrivateLibraryRepo

添加功能实现文件

在 PrivateLibraryRepo/Classes 下添加功能类 Foo. 目录结构如下:

├── PrivateLibraryRepo
│   ├── Assets
│   └── Classes
│       ├── Foo.h
│       ├── Foo.m
│       └── ReplaceMe.m

进入到 Example 木下,执行:pod install 安装刚刚我们添加的帮助类。

Foo 内容如下:

#import "Foo.h"

@implementation Foo
- (void)foo { NSLog(@"Foo say hello!"); }
@end

在 PrivateLibraryRepo.xcworkspace 的 PZViewController 里,引入Foo并调用。

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    Foo *f = [Foo new];
    [f foo];
}

输出结果:Foo say hello!

说明Foo库函数没有问题。可以进行下一步了。

**修改 podspec **

找到 PrivateLibraryRepo.podspec 并修改。podspec 语法参见 Podspec Syntax Reference.

因为是私有库,所以我们要指定私有库的url地址,有可能在任何地方。然后,可能根据个人心意去修改版本号。有的时候还要修改源文件目录或者添加资源文件夹等等。根据个人情况更改。

下面是改好的,有备注。

Pod::Spec.new do |s|
  s.name             = 'PrivateLibraryRepo'
  #版本号,默认从0.1.0开始
  s.version          = '0.1.0'
  s.summary          = 'A short description of PrivateLibraryRepo.'

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC
  s.homepage         = 'https://gitlab.com/zlanchun/PrivateLibraryRepo'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'feimengchang' => 'zlanchun@gmail.com' }
  #source 地址,当前私有库的git地址                                              
  s.source           = { 
                        :git => 'https://zlanchun@gitlab.com/zlanchun/PrivateLibraryRepo.git', 
                        :tag => s.version.to_s 
                       }

  s.ios.deployment_target = '7.0'
  s.platform     = :ios, '7.0'
  s.source_files = 'PrivateLibraryRepo/Classes/**/*'
end

3.验证 pod lib repo

lib repo 验证

在目录 PrivateLibraryRepo 根下执行命令。

$ pod lib lint --allow-warnings

--allow-warnings 的意思是忽略所有警告,并通过校验。

pod lib lint 用于检测本地 lib repo 的有效性。如果无效,则会有报错 error 出现。有过有效,就会给出提示信息,如:PrivateLibraryRepo passed validation.

spec 验证

索引文件 podspec 验证。注意这个时候,因为我们的库文件都在本地,而podspec 验证是需要去和服务器通信的,所以此步骤会出错是正常的。

$ pod spec lint 

报错:

fatal: Remote branch 0.1.0 not found in upstream origin

留待我们后面解决。

4.上传 lib repo 到服务器

$ git remote add origin git@gitlab.com:zlanchun/PrivateLibraryRepo.git
$ git add .
$ git commit -m "Initial commit"
$ git push -u origin master
//tag 值要和podspec中的version一致
$ git tag 0.1.0
//推送tag到服务器上
$ git push --tags

这里除了tag 指定版本外,还可以新建一个分支,分支名字和version值一致,同样能起到作用。如果什么都没有,则会报错。报错也是这样的:

fatal: Remote branch 0.1.0 not found in upstream origin

5.podspec 再次验证

$ pod spec lint --allow-warnings

这一次会验证通过:pod spec lint --allow-warnings。

6.发布 podspec

指定管理 lib repo 的 Specs repo 的 url

$ pod repo add NAME URL [BRANCH]

如:

$ pod repo add PrivateLibraryRepo https://zlanchun@gitlab.com/zlanchun/Specs.git

推送 podspec 到 Specs repo

$ pod repo push REPO [NAME.podspec] --sources=xxx,yyy

注意:

--sources 用来指定 Specs 的 repo 地址。默认是推送的github上。因为这里我们是私库,用的是gitlab,所以需要设定这个值。

如:

$ pod repo push PrivateLibraryRepo PrivateLibraryRepo.podspec --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git --allow-warnings

验证

新建一个工程,在其Podfile 里,加入如下内容:


source 'https://zlanchun@gitlab.com/zlanchun/Specs.git'
source 'https://github.com/CocoaPods/Specs.git'

target 'Test' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!
pod 'PrivateLibraryRepo'

end

然后,执行 pod install:

cocoapodsDemo2.png

注意:

Podfile 里头部的2个source。pod 会按照上下顺序去搜索,如果搜索到lib则进行下一步动作,如果没找到,则切换到下一个source url继续搜索。

这里我们指定先搜索我们自己的 Specs repo,然后再去搜索 CocoaPods的 Specs.

私有库依赖私有库

当我们有2个私有库,其中一个依赖另一个的时候,就会有各种问题出来阻挠你,说多了都是泪。

Podspec 语法:dependency

podspec 的 dependency 用来指定依赖关系。

eg: spec.dependency 'AFNetworking', '~> 1.0'

~> 1.0.1 is equivalent to >= 1.0.1 combined with < 1.1. Similarly, ~> 1.0 will match 1.0, 1.0.1, 1.1, but will not upgrade to 2.0.

Command-line 语法:pod lib lint --sources

The sources from which to pull dependent pods (defaults to https://github.com/CocoaPods/Specs.git). Multiple sources must be comma-delimited..

sources 用于指定 pods 地址,默认指向 master ,如果有多个源用逗号分隔。下面会用到。

具体实现

我们新建一个私库 PrivateLibraryDependency:

$ pod lib create PrivateLibraryDependency

PrivateLibraryDependency 依赖2个库,一个私库PrivateLibraryRepo,一个SDWebImage。

更改 PrivateLibraryDependency.podspec

  • 修改 repo 地址
  • 添加依赖
    • s.dependency 'SDWebImage', '~> 4.0.0'
    • s.dependency 'PrivateLibraryRepo', '~> 0.1.0'

在 Example 目录下的 Podfile 添加source ,然后执行 pod install。

#Podfile
source 'https://zlanchun@gitlab.com/zlanchun/Specs.git'
source 'https://github.com/CocoaPods/Specs.git'
cocoapodsDemo3.png

根目录下验证lib,这里要添加--sources指定私库Specs地址,因为我们的依赖里有私库,所以要指定。否则会报错。

$ pod lib lint --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git

上传库到服务器,打tag。然后验证spec,同样需要指定--sources

$ pod spec lint --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git

发布到私Specs repo里,注意这里的source值是 private,master url.因为,我们依赖的既有私有库也有公有库。

$ pod repo add PrivateLibraryDependency https://zlanchun@gitlab.com/zlanchun/Specs.git
$ pod repo push PrivateLibraryDependency PrivateLibraryDependency.podspec --sources=https://zlanchun@gitlab.com/zlanchun/Specs.git,https://github.com/CocoaPods/Specs.git
cocoapodsDemo4.png

subspec 依赖 subspec

subspec 用来指定子模块的。

在 PrivateLibraryDependency 目录下新建 ModuleA 和 ModuleB 目录。然后创建 ModuleA.{h,m} 和 ModuleB.{h,m}。

目录结构:

├── PrivateLibraryDependency
│   ├── Assets
│   ├── Classes
│   │   └── ReplaceMe.m
│   ├── ModuleA
│   │   ├── ModuleB.h
│   │   └── ModuleB.m
│   └── ModuleB
│       ├── ModuleB.h
│       └── ModuleB.m

这里,我们假定 子模块 ModuleA 依赖 ModuleB , podspec 如下:


s.subspec 'ModuleA' do |a|
  a.source_files = 'PrivateLibraryDependency/ModuleA/**/*.{h,m}'
  a.dependency 'PrivateLibraryDependency/ModuleB'
end

s.subspec 'ModuleB' do |b|
  b.source_files = 'PrivateLibraryDependency/ModuleB/**/*.{h,m}'
end

注意:子模块依赖的时候,依赖里一定要加上当前库前缀。如:a.dependency 'PrivateLibraryDependency/ModuleB'

参考 PrivateLibraryDependency 我已经把 ModuleA 和 ModuleB 加进去了。

集成静态库和动态库(.a 和 framework)

集成 framework 到工程里

集成第三方的 framework (vendored_frameworks):

framework 文件要单独集成到一个repo里,同时这个repo里不能包含别的代码文件。

这里以支付宝SDK为例,提取支付宝SDK的 AlipaySDK.bundle 和 AlipaySDK.framework 放到单独的 repo 里。

podspec 相应内容如下:

s.resources = "**/*.bundle"
s.vendored_frameworks = "**/*.framework"

整个 repo 目录结构:

.
├── AlipaySDK.bundle
├── AlipaySDK.framework
├── AlipaySDKIniOS.podspec
├── LICENSE
└── README.md

集成系统自带的framework (frameworks / libraries):


# 系统的
s.frameworks = 'SystemConfiguration', 'CoreTelephony', 'QuartzCore', 'CoreText'  , 'CoreGraphics', 'UIKit', 'Foundation', 'CFNetwork', 'CoreMotion'
s.libraries = 'c++', 'z'

注意:libc++.tbd 集成的时候,要去掉 lib 前缀。

参考这里 AlipaySDKIniOS

集成 .a 库到工程里

.a 集成的时候,稍微麻烦些!需要同时修改 podspec 和 Podfile 文件。

以集成 openssl 为例,podspec 中需要更改如下内容:

s.subspec 'OpenSSL' do |openssl|
  openssl.source_files = 'AlipayWrapper/Openssl/**/*.h'
  openssl.public_header_files = 'AlipayWrapper/Openssl/**/*.h'
  openssl.ios.preserve_paths      = 'AlipayWrapper/StaticLibrary/libcrypto.a', 'AlipayWrapper/StaticLibrary/libssl.a'
  //指定 .a 文件为 vendored_libraries
  openssl.ios.vendored_libraries  = 'AlipayWrapper/StaticLibrary/libcrypto.a', 'AlipayWrapper/StaticLibrary/libssl.a'
  //需要指定为库名字
  openssl.libraries = 'ssl', 'crypto'
  //需要配置 HEADER_SEARCH_PATHS
  openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/AlipayWrapper/Openssl/**" }
end

Podfile 里需要添加如下内容,否则会报错:

pre_install do |installer|
  # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
  def installer.verify_no_static_framework_transitive_dependencies; end
end

参考AlipayWrapper,我把 AlipaySDK Demo 的帮助类集成到 pods 了,这样就省去了直接导入工程添加依赖库的步骤了。

各种错误集合

Generated duplicate UUIDs

[!] [Xcodeproj] Generated duplicate UUIDs:

PBXBuildFile -- /targets/buildConfigurationList:buildConfigurations:baseConfigurationReference:|,buildSettings:|,display

解决办法:

这时一个无害的报错,可以在 Podfile 头部加入如下内容修复:

install! 'cocoapods', :deterministic_uuids => false

2. <PBXResourcesBuildPhase UUID=2E1007321C278F4300BCFC15>` attempted to initialize an object with an unknown UUID

[!] `<PBXResourcesBuildPhase UUID=`2E1007321C278F4300BCFC15`>` attempted to initialize an object with an unknown UUID. `70CED53B1EC996BB008DF9D5` for attribute: `files`. This can be the result of a merge and  the unknown UUID is being discarded.

解决办法:

General -> Linked Frameworks and Libraries 删除 libPods-xxx.a

然后,重新执行 pod install

3.Unable to find a specification for depended upon by

[!] Unable to find a specification for `TESTUtilitis (~> 1.0.0)` depended upon by `TESTVendors`

这个问题是更新库之后,没有更新本地的 repo,所以执行:

$ pod repo update TESTUtilitis

4.target has transitive dependencies that include static binaries

[!] The 'Pods-AlipayWrapper_Example' target has transitive dependencies that include static binaries: (/Users/z/Desktop/github/Alipay/AlipaySDK/AlipayWrapper/Example/Pods/AlipaySDKIniOS/AlipaySDK.framework)

这个问题是当我们要用 cocoapods 集成 xxx.a 文件的时候出现的。需要改2个地方:podspec 和 Podfile.

podspec

 s.dependency 'AlipaySDKIniOS', '~> 15.2.0'
//添加 target_xconfig
  s.pod_target_xcconfig = {
    'FRAMEWORK_SEARCH_PATHS' => '$(inherited) $(PODS_ROOT)/AlipaySDKIniOS',
    'OTHER_LDFLAGS'          => '$(inherited) -undefined dynamic_lookup'
  }

Podfile

#在最后面添加如下内容:
pre_install do |installer|
    # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
    def installer.verify_no_static_framework_transitive_dependencies; end
end

详情看这里:https://github.com/CocoaPods/CocoaPods/issues/3289

5. Use the $(inherited) flag

[!] The `TEST [Debug]` target overrides the `FRAMEWORK_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-TEST/Pods-TEST.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

解决:

Go into the Build Settings for "Framework Search Paths" and change the value for your target to be "$(inherited)"

参考

6. [!] Unable to find a pod with name, author, summary, or description matching AlipaySDKIniOS

问题:trunk 成功后,无法搜索到。同时已经 pod repo udpate 到最新了,还是无法搜索到。

原因是:

~/Library/Caches/CocoaPods/search_index.json pod setup生成的索引文件没有更新。

解决:

删除后,从新 search 就好了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • 项目组件化、平台化是技术公司的共同目标,越来越多的技术公司推崇使用pod管理第三方库以及私有组件,一方面使项目架构...
    swu_luo阅读 20,564评论 0 39
  • 前言 CocoaPods是一个程序依赖管理工具,使用CocoaPods可以节省设置和更新第三方开源库的时间,同样的...
    Dennis_me阅读 1,972评论 0 4
  • Cocoapods是非常好用的一个iOS依赖管理工具,使用它可以方便的管理和更新项目中所使用到的第三方库,以及将自...
    Nash33阅读 2,054评论 0 50
  • 为什么要使用Cocoapods私有库 在项目开发的时候常常会积累很多自己的框架及工具包,而如果需要创建新项目,就不...
    纳木那咔阅读 942评论 0 2
  • 感觉太琐碎的事不愿意记了,然后就对不上账了,不知道17号那天自己干嘛了,看来真的不能相信自己的记忆,还得天天唠叨
    八月微央阅读 163评论 0 0