目录

COCOAPODS技巧-创建私有仓库

https://anyanf-img-1256234566.cos.ap-beijing.myqcloud.com/2018/cocoapods-private-repo.png

本文说明制作Cocoapod私有库的过程。本文涉及到两个仓库 ‘官方仓库’、’私有仓库’。

  1. 官方仓库的作用代表 CocoaPods的 官方 podspec 存放地址。 具体可以参考:CocoaPods官方源Specs;
  2. 私有仓库的是私有 podspec 存放地址。在 git 中仓库名称是 YFRepo ,clone 到本地的别名也叫 YFRepo-master。后文中私有仓库–YFRepo指的 git 地址,YFRepo-master 指的是 clone 到本地的名称。

创建私有仓库

仓库(Spec Repo)是所有的 Pods 的一个索引,是所有公开\私有 Pods 的 podspec 文件仓库,其实就是一个部署在服务器的 Git 仓库,当你使用 CocoaPods 后它会被 Clone 到本地的 ~/.cocoapods/repos 目录下。

首先在 git 上创建一个私有远端仓库 YFRepo,步骤如下:

  1. 在 GitHub 上创建私有仓库 YFRepo,空的就可以。
  2. 将私有仓库 YFRepo 添加到 cocoapod 中,远端私有仓库 YFRepo 在本地中的别名是YFRepo-master,这样以后操作 YFRepo-master 就相当于操作YFRepo, 命令如下:
1
pod repo add 'YFRepo-master' 'https://github.com/anyanf/YFRepo.git'
注意:这个Git 仓库地址要换成你自己的创建的 Specs git 地址!!! 成功后会在 ~/.cocoapods/repos 目录下就能看到 YFRepo-master 了,至此,第一步就完成了创建私有仓库。

创建完成后,查看 ~/.cocoapods/repos 目录的变化:

1
2
3
4
5
# cd 到~/.cocoapods/repos 目录
cd ~/.cocoapods/repos

# 查看目录结构
tree -L 3

大概的文件目录如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.
├── YFRepo-master
│   └── README.md
└── master
 ├── CocoaPods-version.yml
 ├── README.md
 └── Specs
 ├── 0
 ├── 1
 ├── 2

其中 master 就是官方的 Sepc Repo,跟 master 同目录级别的 YFRepo-master 目录就是我自己创建的私有 Sepc Repo,目前 YFRepo-master下面是空的。

也可以使用 pod repo list  命令查看仓库信息,结果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
repos pod repo list

master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/kangan/.cocoapods/repos/master

YFRepo-master
- Type: git (master)
- URL: https://github.com/anyanf/YFRepo.git
- Path: /Users/kangan/.cocoapods/repos/YFRepo-master

2 repos

小结

上面讲解了私有仓库的创建方法。创建完成后,从两个方面描述私有仓库,进一步认识私有创库:文件目录、pod 命令。

创建 YFPodDemo 库

  1. 创建 YFPodDemo 库: 首先,在Git上创建一个YFYFPodDemo仓库,当然你也是可以在公司内网创建的。 创建方法使用Cocoapods提供的一个Using Pod Lib Create 工具,注意提前 cd 到你想要创建库的路径。
1
2
#pod lib create [项目名]
pod lib create YFPodDemo

该命令会在当前目录创建 YFPodDemo 项目,接着在 Terminal 控制台会输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Cloning `https://github.com/CocoaPods/pod-template.git` into `YFPodDemo`.
Configuring YFPodDemo template.

------------------------------

To get you started we need to ask a few questions, this should only take a minute.

2018-04-18 19:53:58.341 defaults[47928:27982286]
The domain/default pair of (org.cocoapods.pod-template, HasRunbefore) does not exist
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 click links to open in a browser. )

Press return to continue.

选择回车按钮,接着会出现一系列的问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
What platform do you want to use?? [ iOS / macOS ]
 > iOS

What language do you want to use?? [ Swift / ObjC ]
 > ObjC

Would you like to include a demo application with your library? [ Yes / No ]
 > Yes

Which testing frameworks will you use? [ Specta / Kiwi / None ]
 > Specta

Would you like to do view based testing? [ Yes / No ]
 > Yes

What is your class prefix?
 > YF

Running pod install on your new library.

Analyzing dependencies
Fetching podspec for `YFPodDemo` from `../`
Downloading dependencies
Installing Expecta (1.0.6)
Installing Expecta+Snapshots (3.1.1)
Installing FBSnapshotTestCase (2.1.4)
Installing Specta (1.0.6)
Installing YFPodDemo (0.1.0)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `YFPodDemo.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There are 5 dependencies from the Podfile and 5 total pods installed.

[!] Automatically assigning platform ios with version 9.3 on target YFPodDemo_Example because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.

 Ace! you're ready to go!
 We will start you off by opening your project in Xcode
  open 'YFPodDemo/Example/YFPodDemo.xcworkspace'

To learn more about the template see `https://github.com/CocoaPods/pod-template.git`.
To learn more about creating a new pod, see `http://guides.cocoapods.org/making/making-a-cocoapod`.


回答完问题后,会创建出YFPodDemo项目。结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
.
├── Example
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   │   ├── Expecta
│   │   ├── Expecta+Snapshots
│   │   ├── FBSnapshotTestCase
│   │   ├── Headers
│   │   ├── Local\ Podspecs
│   │   ├── Manifest.lock
│   │   ├── Pods.xcodeproj
│   │   ├── Specta
│   │   └── Target\ Support\ Files
│   ├── Tests
│   │   ├── Tests-Info.plist
│   │   ├── Tests-Prefix.pch
│   │   ├── Tests.m
│   │   └── en.lproj
│   ├── YFPodDemo
│   │   ├── Base.lproj
│   │   ├── Images.xcassets
│   │   ├── YFAppDelegate.h
│   │   ├── YFAppDelegate.m
│   │   ├── YFPodDemo-Info.plist
│   │   ├── YFPodDemo-Prefix.pch
│   │   ├── YFViewController.h
│   │   ├── YFViewController.m
│   │   ├── en.lproj
│   │   └── main.m
│   ├── YFPodDemo.xcodeproj
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace
│   │   └── xcshareddata
│   └── YFPodDemo.xcworkspace
│   ├── contents.xcworkspacedata
│   ├── xcshareddata
│   └── xcuserdata
├── LICENSE
├── README.md
├── YFPodDemo
│   ├── Assets
│   └── Classes
│   └── ReplaceMe.m
├── YFPodDemo.podspec
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj

2.添加实现代码

1
2
3
4
5
├── YFPodDemo
│   ├── Assets
│   └── Classes
│   ├── YFLog.h
│   └── YFLog.m

在本教程中我在上面的 Classes 文件目录添加了 YFLog.[h、m] 两个文件。

3.开发模式下测试 pod 库的代码 打开 Example 工程目录 Podfile 文件:

1
2
pod 'YFPodDemo', :path => '../' # 指定路径
#pod 'YFPodDemo', :podspec => '../YFPodDemo.podspec'  # 指定podspec文件

然后在 Example 工程目录下执行 pod install 命令安装依赖,打开项目工程,可以看到库文件都被加载到 Pods 子项目中了,不过它们并没有在 Pods 目录下,而是跟测试项目一样存在于 Development Pods/YFPodDemo 中,这是因为我们是在本地测试,而没有把 podspec 文件添加到 Spec Repo 中的缘故。测试库文件没有问题,接着我们需要执行第4步。

4.提交 YFPodDemo 到 git上。 在 Terminal 中执行以下命令:

1
2
3
4
5
6
git add .
git commit -m '1.0.0'
git remote add origin https://github.com/anyanf/YFPodDemo.git
git push origin master #提交到远端仓库
git tag -m "v1.0.0" "v1.0.0" #打上标签,这个很重要
git push --tags #推送tag到远端仓库

到这里,成功提交到远程仓库— YFPodDemo ,以后就可以使用 git 上的 YFPodDemo 库了。

创建并提交 YFPodDemo 库的 podspec 文件到私有仓库 YFRepo-master

1.配置 YFPodDemo 库的 podspec 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
# Be sure to run `pod lib lint YFPodDemo.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
  s.name             = 'YFPodDemo'
  s.version          = '1.0.0'
  s.summary          = 'A short description of YFPodDemo.'

# 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/anyanf/YFPodDemo' # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' s.license = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'anyanf' => 'ankang1943@qq.com' }
  s.source           = { :git => 'https://github.com/anyanf/YFPodDemo.git', :tag => '1.0.0' }
  # s.social_media_url = 'https://twitter.com/'

  s.ios.deployment_target = '8.0'

  s.source_files = 'YFPodDemo/Classes/**/*'
  
  # s.resource_bundles = {
  #   'YFPodDemo' => ['YFPodDemo/Assets/*.png']
  # }

  # s.public_header_files = 'Pod/Classes/**/*.h'
  # s.frameworks = 'UIKit', 'MapKit'
  # s.dependency 'AFNetworking', '~> 2.3'
end

podspec 更多配置请参考:官方文档

2.验证 YFPodDemo.podspec

编辑完 YFPodDemo.podspec 文件后,需要验证一下这个 YFPodDemo.podspec 文件是否可用 ,在 Terminal 中执行 cd 进入 YFPodDemo.podspec 项目根目录然后,执行以下命令(后面添加 –verbose 参数可以获取详情):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
pod spec lint --allow-warnings

#有用的命令选项
--allow-warnings                                  允许警告存在
--subspec=NAME                                    只验证指定的 子spec
--fail-fast                                       第一次失败就停止 libraries 库
--use-libraries                                   验证时,pod自动加载系统 
--sources=https://github.com/artsy/Specs,master   指定私有 spec 库 ,如果要提交的库里依赖别的私有仓库中的库,需要指定
--verbose                                         显示编译详细日志
--help                                            查看帮助

当你看到 Terminal 中输出:

1
2
3
4
5
6
-> YFPodDemo (1.0.0)
- WARN | summary: The summary is not meaningful.

Analyzed 1 podspec.

YFPodDemo.podspec passed validation.

表示这个 YFPodDemo.podspec 验证通过,是一个符合 CocoaPods 规则的配置文件。

3.本地测试 YFPodDemo.podspec 文件

打开Example工程目录Podfile文件修改下pod 的引用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#pod 'YFPodDemo', :path => '../' # 指定路径
pod 'YFPodDemo', :podspec => '../YFPodDemo.podspec'  # 指定podspec文件

#有用的命令选项
--allow-warnings                                  允许警告存在
--use-libraries                                   验证时,pod自动加载系统 
--sources=https://github.com/artsy/Specs,master   依赖的其它源
--local-only                                      只推送到本地,不推送到远程
--commit-message="Fix bug in pod"                 注释
--use-json                                        源中使用 JSON 格式保存
--verbose                                         显示详细信息
--help                                            帮助

然后在 Example 工程目录下执行 pod install 命令安装依赖,打开项目工程,现在可以看到库文件都被加载到 Pods 子项目中了。

4.向 Spec Repo 提交 podspec

测试库文件没有问题我们就把 YFPodDemo.podspec 提交到远程 Spec Repo 仓库中,就是本文开头说的官方仓库或者私有仓库,在 Terminal中 执行 cd 进入 YFPodDemo 项目根目录然后,执行以下命令:

1
2
# pod repo push [Repo名] [podspec 文件名字]
pod repo push YFRepo-master ./YFPodDemo.podspec --allow-warnings

如果提交成功,在Terminal会输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Validating spec
 -> YFPodDemo (1.0.0)
    - WARN  | summary: The summary is not meaningful.

Updating the `YFRepo-master' repo

Already up to date.

Adding the spec to the `YFRepo-master' repo

 - [Add] YFPodDemo (1.0.0)

Pushing the `YFRepo-master' repo

表示提交成功了!这个组件库就添加到我们的私有 Spec Repo 中了,可以进入到 ~/.cocoapods/repos/YFRepo-master 目录下查看

1
2
cd ~/.cocoapods/repos/
tree -L 3 YFRepo-master/

结果如下:

1
2
3
4
5
6
7
YFRepo-master/
├── README.md
└── YFPodDemo
    └── 1.0.0
        └── YFPodDemo.podspec

2 directories, 2 files

使用制作好的Pod

在完成这一系列步骤之后,我们就可以在正式项目中使用这个私有的 Pod 了只需要在项目的 Podfile 里增加以下一行代码即可, 在正式项目的 Podfile 里添加私有 Spec Repo。

1
2
3
4
5
6
#私有Spec Repo
source 'https://github.com/anyanf/YFRepo.git' 

source 'git@github.com:CocoaPods/Specs.git'

pod 'YFPodDemo', '~> 1.0.0'

然后执行 pod install,安装依赖,然后打开项目可以看到,我们自己的库文件已经出现在 Pods 子项目中的 Pods 子目录下了,而不再是 Development Pods。

将YFPodDemo发布到官方仓库中

注册CocoaPods

首先使用 pod trunk me 查看自己是否注册过:如果有下面类似的内容输出,则表示没有注册过。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[!] You need to register a session first.

Usage:

    $ pod trunk me COMMAND

      Includes information about your registration, followed by all your
      sessions.

      These are your current session, other valid sessions, unverified sessions,
      and expired sessions.

Commands:

    + clean-sessions   Remove sessions

Options:

    --silent           Show nothing
    --verbose          Show more debugging information
    --no-ansi          Show output without ANSI codes
    --help             Show help banner of specified command

使用 pod trunk register 命令注册。

1
pod trunk register ankang1943@qq.com 'anyanf' --verbose

注册完成后,使用下面的命令,将 YFPodDemo 库的 spec 推送到官方仓库中。

1
pod trunk push  --allow-warnings

推送完成后,可以使用pod search 查看。

Tip

pod 的命令如果不知道怎么用,可以使用 pod –help 命令查看使用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
pod --help
Usage:

    $ pod COMMAND

      CocoaPods, the Cocoa library package manager.

Commands:

    + cache         Manipulate the CocoaPods cache
    + deintegrate   Deintegrate CocoaPods from your project
    + env           Display pod environment
    + init          Generate a Podfile for the current directory
    + install       Install project dependencies according to versions from a
                    Podfile.lock
    + ipc           Inter-process communication
    + lib           Develop pods
    + list          List pods
    + outdated      Show outdated project dependencies
    + plugins       Show available CocoaPods plugins
    + repo          Manage spec-repositories
    + search        Search for pods
    + setup         Setup the CocoaPods environment
    + spec          Manage pod specs
    + trunk         Interact with the CocoaPods API (e.g. publishing new specs)
    + try           Try a Pod!
    + update        Update outdated project dependencies and create new
                    Podfile.lock

Options:

    --silent        Show nothing
    --version       Show the version of the tool
    --verbose       Show more debugging information
    --no-ansi       Show output without ANSI codes
    --help          Show help banner of specified command

通过上面可以看到 pod 的所有命令。常用的有 pod init、pod install、 pod update、pod lib、pod repo 等等,如果对 pod repo 不了解,可以使用 pod repo –-help 进一步查看使用方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
pod repo --help
Usage:

    $ pod repo [COMMAND]

      Manage spec-repositories

Commands:

    + add       Add a spec repo
    + lint      Validates all specs in a repo
    > list      List repos
    + push      Push new specifications to a spec-repo
    + remove    Remove a spec repo
    + update    Update a spec repo

Options:

    --silent    Show nothing
    --verbose   Show more debugging information
    --no-ansi   Show output without ANSI codes
    --help      Show help banner of specified command

可以看出,pod repo add 、pod repo list 等命令,如果对 pod repo list 命令不知道如何使用,可以使用 pod repo list –help 命令进一步查看使用方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
pod repo list --help
Usage:

    $ pod repo list

      List the repos from the local spec-repos directory at
      `~/.cocoapods/repos/.`

Options:

    --count-only   Show the total number of repos
    --silent       Show nothing
    --verbose      Show more debugging information
    --no-ansi      Show output without ANSI codes
    --help         Show help banner of specified command

上面的方法对任何的 pod 命令都使用,通过上面的方法我们可以学习会 pod 命令的使用方法。

遇到的问题解决方案

pod repo push 的时候可能会遇到下面这样的错误:

1
[!] The repo `MySpecs` at `../../../.cocoapods/repos/YFRepo-master` is not clean

执行如下命令即可:

1
cd ~/.cocoapods/repos/YFRepo-master,git clean -f

参考资料:创建私有仓库

如何在私有组件中包含*.xcassets,使用图片

.xcassets文件放至目录/Assets/*.xcassets下。

需要在*.podspec文件中加入如下代码:

1
2
3
s.resource_bundle = {
  'boudle名' => ['私有库名/Assets/*.xcassets'] #boudle名下面要用
}

在需要引入*.xcassets中的图片资源时,调用如下方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

UIImage *img = [self getImageWithBoudleName:@"boudle名" imgName:@"图片名称"];


- (UIImage *)getImageWithBoudleName:(NSString *)boudleName imgName:(NSString *)imgName {
    NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"Frameworks" withExtension:nil];
    bundleURL = [bundleURL URLByAppendingPathComponent:boudleName];
    bundleURL = [bundleURL URLByAppendingPathExtension:@"framework"];
    bundleURL = [bundleURL URLByAppendingPathComponent:boudleName];
    bundleURL = [bundleURL URLByAppendingPathExtension:@"bundle"];

    NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];

    UIImage *image = [UIImage imageNamed:imgName
                                inBundle:bundle
           compatibleWithTraitCollection:nil];
    
//    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
//    NSURL *url = [bundle URLForResource:boudleName withExtension:@"bundle"];
//    NSBundle *targetBundle = [NSBundle bundleWithURL:url];
//    UIImage *image = [UIImage imageNamed:imgName
//                                inBundle:targetBundle
//           compatibleWithTraitCollection:nil];
    return image;
}

关于pod之间的引用问题

private_header_files

善用spec中的private_header_files,可以解决很多引用的问题。

可以将需要导入的第三方头文件单独放到一个.h文件中,然后在pod库中的导入这个.h文件,将该.h 设置私有,尤其是当pod库中含有OC和Swift混编时,该操作极其重要。否则,你可能遇到比如Include of non-modular header inside framework module的错误,该错误就是你在头文件中引用了其他库的头文件,这是不推荐的,建议放到.m中去导入,但是这样就要在每个.m中引用了,如果想方便的话,还是写在一个统一的.h中,然后弄成’private_header_files’。

还有就是如果是动态库的模式(use_frameworks!),在别的类Foo里,只有引用了pod中某个类,那么类Foo会把pod所有的.h都引用到,这样耦合度也是横高的。比如pod中有C++依赖的Bar.h,虽然类Foo并没有引用Bar.h,还是会报错让该类兼容C++。这时候把Bar.h弄成private,就可以不引用这个Bar.h了,从而解决类Foo不用c++的类编译错误。

OC与Swift混编

主工程中的OC文件引用swift文件时使用#import "YourTargetName-Swift.h"来引用,在XCode 10.2.1,Swift 5.0中使用@import YourPodName;,例如:

1
#import <Foo/Foo-Swift.h>

Xcode 10.2.1,swift 5.0使用:

1
@import Foo;

而swift引用OC文件时,不需要使用桥接文件,pod会自动生成YourPodName-umbrella.h直接使用即可,例如:

1
import UIKit

OC项目中使用swift pod

如果在pod库中使用了Swift,那么在主工程中一定要新建一个Swift文件,空白的也可以。否则编译器会报错:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ld: warning: Could not find auto-linked library 'swiftCoreGraphics'
ld: warning: Could not find auto-linked library 'swiftFoundation'
ld: warning: Could not find auto-linked library 'swiftMetal'
ld: warning: Could not find auto-linked library 'swiftDarwin'
ld: warning: Could not find auto-linked library 'swiftUIKit'
ld: warning: Could not find auto-linked library 'swiftObjectiveC'
ld: warning: Could not find auto-linked library 'swiftCoreFoundation'
ld: warning: Could not find auto-linked library 'swiftDispatch'
ld: warning: Could not find auto-linked library 'swiftCoreImage'
ld: warning: Could not find auto-linked library 'swiftQuartzCore'
ld: warning: Could not find auto-linked library 'swiftCore'
ld: warning: Could not find auto-linked library 'swiftSwiftOnoneSupport'
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

如果没有这个swift文件,工程会认为项目中没有swift代码,就不会查找swift的库,会导致使用的swift静态库找不到swift运行环境,所以需要这个文件保证工程链接到swift的各种库。