CocoaPods 入门指北

3,428 阅读18分钟

cover

CocoaPodsmacOSiOS 应用开发中的「包管理工具」。利用 CocoaPods 可以非常方便地管理三方库和发布自己的库。

CocoaPods 致力于解决以下两个问题:一、简化了引入第三方代码涉及到的许多配置,能够自动配置编译选项;二、可以很方便地查找到适合优秀的第三方库,以此来缩短软件的开发周期和提升软件的质量。

阅读本文,你将了解以下内容

  • 如何安装 CocoaPods
  • 怎么使用 CocoaPods 搜索感兴趣的库
  • 使用 CocoaPods 管理你的三方库依赖
  • 熟悉 CocoaPods 大致的运行机制
  • 了解 CocoaPods 文件系统
  • 熟悉 CocoaPods 缓存机制
  • 开始制作私有库
  • 发布私有库到私有源
  • 相关问题和解决方案

小试牛刀

安装 CocoaPods

你可能需要「科学上网」才能顺利安装。有时你可能会遇到权限问题或团队成员间版本不一致问题,使用 bundle 来管理 CocoaPods 是一个选择,请自行了解 bundlebundler.io)。

通过 RubyGems 安装:终端键入命令 sudo gem install cocoapods

初始化项目

1、创建一个名称为 PodsDemoiOS 工程

目录结构如下:

.
├── PodsDemo
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets
│   │   ├── AccentColor.colorset
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset
│   │   │   └── Contents.json
│   │   └── Contents.json
│   ├── Base.lproj
│   │   ├── LaunchScreen.storyboard
│   │   └── Main.storyboard
│   ├── Info.plist
│   ├── SceneDelegate.h
│   ├── SceneDelegate.m
│   ├── ViewController.h
│   ├── ViewController.m
│   └── main.m
└── PodsDemo.xcodeproj
    ├── project.pbxproj
    ├── project.xcworkspace
    │   ├── contents.xcworkspacedata
    │   ├── xcshareddata
    │   │   └── IDEWorkspaceChecks.plist
    │   └── xcuserdata
    │       └── username.xcuserdatad
    │           └── UserInterfaceState.xcuserstate
    └── xcuserdata
        └── username.xcuserdatad
            └── xcschemes
                └── xcschememanagement.plist

2、初始化 pod

初始化 pod 时,会根据 projectName.xcodeproj 在根目录创建一个 Podfile 文件。

命令:pod init

此时目录结构:

├── Podfile // 新增
├── PodsDemo
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets
│   │   ├── AccentColor.colorset
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset
│   │   │   └── Contents.json
│   │   └── Contents.json
│   ├── Base.lproj
│   │   ├── LaunchScreen.storyboard
│   │   └── Main.storyboard
│   ├── Info.plist
│   ├── SceneDelegate.h
│   ├── SceneDelegate.m
│   ├── ViewController.h
│   ├── ViewController.m
│   └── main.m
└── PodsDemo.xcodeproj
    ├── project.pbxproj
    ├── project.xcworkspace
    │   ├── contents.xcworkspacedata
    │   ├── xcshareddata
    │   │   └── IDEWorkspaceChecks.plist
    │   └── xcuserdata
    │       └── username.xcuserdatad
    │           └── UserInterfaceState.xcuserstate
    └── xcuserdata
        └── username.xcuserdatad
            └── xcschemes
                └── xcschememanagement.plist

3、安装依赖

分析依赖、安装依赖、创建必要文件。

命令: pod install

此时目录结构:

├── Podfile // 新增
├── Podfile.lock // 新增
├── Pods // 新增
│   ├── Headers
│   ├── Local Podspecs
│   ├── Manifest.lock
│   ├── Pods.xcodeproj
│   │   ├── project.pbxproj
│   │   └── xcuserdata
│   │       └── username.xcuserdatad
│   │           └── xcschemes
│   │               ├── Pods-PodsDemo.xcscheme
│   │               └── xcschememanagement.plist
│   └── Target Support Files
│       └── Pods-PodsDemo
│           ├── Pods-PodsDemo-Info.plist
│           ├── Pods-PodsDemo-acknowledgements.markdown
│           ├── Pods-PodsDemo-acknowledgements.plist
│           ├── Pods-PodsDemo-dummy.m
│           ├── Pods-PodsDemo-umbrella.h
│           ├── Pods-PodsDemo.debug.xcconfig
│           ├── Pods-PodsDemo.modulemap
│           └── Pods-PodsDemo.release.xcconfig
├── PodsDemo
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets
│   │   ├── AccentColor.colorset
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset
│   │   │   └── Contents.json
│   │   └── Contents.json
│   ├── Base.lproj
│   │   ├── LaunchScreen.storyboard
│   │   └── Main.storyboard
│   ├── Info.plist
│   ├── SceneDelegate.h
│   ├── SceneDelegate.m
│   ├── ViewController.h
│   ├── ViewController.m
│   └── main.m
├── PodsDemo.xcodeproj
│   ├── project.pbxproj
│   ├── project.xcworkspace
│   │   ├── contents.xcworkspacedata
│   │   ├── xcshareddata
│   │   │   └── IDEWorkspaceChecks.plist
│   │   └── xcuserdata
│   │       └── username.xcuserdatad
│   │           └── UserInterfaceState.xcuserstate
│   └── xcuserdata
│       └── username.xcuserdatad
│           └── xcschemes
│               └── xcschememanagement.plist
└── PodsDemo.xcworkspace // 新增
    └── contents.xcworkspacedata

4、打开工程

打开 PodsDemo.xcworkspace 文件而不是 PodsDemo.xcodeproj

我们发现新生成了以下四个文件和文件夹:

  • PodsDemo.xcworkspace:用来管理工程,它管理着所有 target,以后要通过这个文件来打开工程。

  • Podfile:这是 CocoaPods 使用者接触的最重要的文件,我们需要直接配置它,让 CocoaPods 理解我们对三方库的依赖意图。在这里也支持对工程进行简单配置。

  • Podfile.lock:这也是一个很重要的文件,不过有时会被很多开发者忽略,强烈建议将该文件纳入版本管理,因为 pod 允许以语义化的形式约束三方库的版本,而提交 Podfile.lock 文件能避免因团队开发人员之间、或开发人员与打包机之间三方库版本不一致导致的事故。

  • Podspod install 命令会自动生成一个工程,用以管理所有依赖库,那就是 Pods.xcodeproj,平时开发时你完全可以忽视它。另外依赖的三方库也会放在该目录下。

更新「源」(索引库)

三方库发布版本后,更新「源」后才能更新到新版本,建议经常更新。

更新指定源pod repo update sourceName

更新所有源pod repo update

查找你感兴趣的三方库

在业务快速迭代过程中,快速搜索到一个合适的库能提高工作效率和代码质量,当然这不是鼓励不劳而获而是建议不要重复造轮子。

1、pod search Alamofire: 查找知道名称的库。

-> Alamofire (5.4.3)
   Elegant HTTP Networking in Swift
   pod 'Alamofire', '~> 5.4.3'
   - Homepage: https://github.com/Alamofire/Alamofire
   - Source:   https://github.com/Alamofire/Alamofire.git
   - Versions: 5.4.3, 5.4.2, 5.4.1, 5.4.0, 5.3.0, 5.2.2, 5.2.1, 5.2.0, 5.1.0,
   5.0.5, 5.0.4, 5.0.3, 5.0.2, 5.0.1, 5.0.0, 5.0.0-rc.3, 5.0.0-rc.2, 5.0.0-rc.1,
   5.0.0-beta.7, 5.0.0-beta.6, 5.0.0-beta.5, 5.0.0-beta.4, 5.0.0-beta.3,
   5.0.0-beta.2, 5.0.0.beta.1, 4.9.1, 4.9.0, 4.8.2, 4.8.1, 4.8.0, 4.7.3, 4.7.2,
   4.7.1, 4.7.0, 4.6.0, 4.5.1, 4.5.0, 4.4.0, 4.3.0, 4.2.0, 4.1.0, 4.0.1, 4.0.0,
   3.5.1, 3.5.0, 3.4.2, 3.4.1, 3.4.0, 3.3.1, 3.3.0, 3.2.1, 3.2.0, 3.1.5, 3.1.4,
   3.1.3, 3.1.2, 3.1.1, 3.1.0, 3.0.1, 3.0.0, 3.0.0-beta.3, 3.0.0-beta.2,
   3.0.0-beta.1, 2.0.2, 2.0.1, 2.0.0, 2.0.0-beta.4, 2.0.0-beta.3, 2.0.0-beta.2,
   2.0.0-beta.1, 1.3.1, 1.3.0, 1.2.3, 1.2.2, 1.2.1, 1.2.0, 1.1.5, 1.1.4, 1.1.3
   [bb-cocoapods-specs repo]
   ...

2、pod search HTTP Networking in Swift: 根据库的描述查找

3、当然你也可以通过 CocoaPods官网cocoapods.org)查找。

pod-search

管理你的依赖

编辑 Podfile 配置你的依赖

Podfile

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

platform :ios, '9.0'

target 'PodsDemo' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!
  # Pods for PodsDemo
  pod 'AFNetworking', '3.2.1'
end

source:存放三方库 .podspec 文件的索引库,pod 搜索三方库就是从 source 仓库里搜索的,CocoaPods 支持添加多个源,当你要发布自己的私有库时这很有用,如果你经常为 pod repo update 太慢而苦恼,你可以为 CocoaPods 官方 source 做一个镜像,托管在你的 GitLab 上并定期同步,这样你的开发小伙伴不用翻墙也能快速更新了。

target:指定以下的依赖配置是针对哪个 target 的,Podfile 允许你对 workspace 下不同 target 进行不同的依赖配置。

pod:新增依赖项。

Ruby 语法

你可能觉得这种写法有点奇怪但又很自然,也许这正是你开始学习 Ruby 的绝佳时机。

Ruby 方法sourcetargetpod 这些都是 Ruby 方法,Ruby 语法里方法调用可以不带圆括号,这种场景使用就很合适。

Ruby 闭包:

 target 'PodDemo' do
 ...
 end

 post_install do |installer|
 ...
 end

Ruby 枚举:iosRuby 的枚举。

Podfile 使用 基于 Ruby 的 DSL 语法,像是在配置而不是写代码,让人觉得很舒服。

CocoaPods 的文件系统

了解 CocoaPod 的文件系统能让你面对 CocoaPods 异常时从容不迫,因为了解了它们确实能解决很多问题。

Pods 目录

CocoaPods 将三方库存放在 ./Pods 文件夹下,这里的源代码是不建议改的,即使改了,下次 pod install 改动也会被覆盖。

pods-directory

索引库 specs(源)

索引库其实就是存放三方库 .podspec 文件的 git 仓库,就是我们在 Podfile 里通过 source 添加的那些源,当然可以通过 git pull 来更新索引库,不过往往会使用命令 pod repo update 来更新索引库。

索引库的位置在这里:~/.cocoapods/repos/,可以终端键入 ls ~/.cocoapods/repos/ 查看下自己的索引库。

Yao:~ username$ ls ~/.cocoapods/repos/
Spec_Lock		bb-cocoapods-specs	cocoapods
bb-bp_repos		trunk

缓存

CocoaPods 会缓存三方库源码和 podspec 文件,如果某个库的某个版本被下载过则下次 pod install 时会直接从缓存 copy 一份到工作目录(即使是另一个项目依赖这个库也会直接利用缓存而不再重新下载),这也是当你改动源码而不修改版本号时别人拉不到新代码的原因。

缓存的位置在这里: ~/Library/Caches/CocoaPods/,可以终端键入 open ~/Library/Caches/CocoaPods/ 查看自己的缓存。

源码缓存:

Yao:~ username$ ls ~/Library/Caches/CocoaPods/Pods/Release/Alamofire/4.9.1-85e8a/Source/
AFError.swift				ResponseSerialization.swift
Alamofire.swift				Result.swift
DispatchQueue+Alamofire.swift		ServerTrustPolicy.swift
MultipartFormData.swift			SessionDelegate.swift
NetworkReachabilityManager.swift	SessionManager.swift
Notifications.swift			TaskDelegate.swift
ParameterEncoding.swift			Timeline.swift
Request.swift				Validation.swift
Response.swift

podspec 缓存:

Yao:~ username$ ls ~/Library/Caches/CocoaPods/Pods/Specs/Release/Alamofire/
4.9.podspec.json	5.1.podspec.json

执行 pod install 后都发生了什么

看完以上内容你可能已经会使用 CocoaPods 查找三方库并引入自己的项目了,现在你也许会好奇 pod intall 都做了什么,下面就来分析下。

想了解发生了什么其实很简单,只需在 pod install 时加上 --verbose 参数。

终端键入 pod install --verbose 查看详情。

Yao:PodsDemo username$ pod install --verbose
  Preparing

Analyzing dependencies

Inspecting targets to integrate
  Using `ARCHS` setting to build architectures of target `Pods-PodsDemo`: (``)

Finding Podfile changes

Resolving dependencies of `Podfile`

Comparing resolved specification to the sandbox manifest

Downloading dependencies
  - Running pre install hooks

Generating Pods project
  - Creating Pods project
  - Installing files into Pods project
    - Adding source files
    - Adding frameworks
    - Adding libraries
    - Adding resources
    - Linking headers
  - Installing Pod Targets
  - Installing Aggregate Targets
    - Installing target `Pods-PodsDemo` iOS 9.0
      - Generating Info.plist file at `Pods/Target Support
      Files/Pods-PodsDemo/Pods-PodsDemo-Info.plist`
      - Generating module map file at `Pods/Target Support
      Files/Pods-PodsDemo/Pods-PodsDemo.modulemap`
      - Generating umbrella header at `Pods/Target Support
      Files/Pods-PodsDemo/Pods-PodsDemo-umbrella.h`
      - Generating dummy source at `Pods/Target Support
      Files/Pods-PodsDemo/Pods-PodsDemo-dummy.m`
  - Generating deterministic UUIDs
  - Stabilizing target UUIDs
  - Running post install hooks
  - Writing Xcode project file to `Pods/Pods.xcodeproj`
  Cleaning up sandbox directory

Integrating client project

Integrating target `Pods-PodsDemo` (`PodsDemo.xcodeproj` project)
  - Running post integrate hooks
  - Writing Lockfile in `Podfile.lock`
  - Writing Manifest in `Pods/Manifest.lock`
  CDN: trunk Relative path: CocoaPods-version.yml exists! Returning local
  because checking is only perfomed in repo update

-> Pod installation complete! There are 0 dependencies from the Podfile and 0 total pods installed.

[!] The Podfile does not contain any dependencies.

可以看到,执行了很多操作,让我们逐步来分析一下。

读取 Podfile 文件

执行 pod 命令时,CocoaPods 会读出 Podfile 文件的内容,通过 eval 方法执行文件的内容。

CocoaPods 会下载 source 添加的「源」到 ~/.cocoapods 目录下,源里存放着三方库的 podspec 文件。 podspec 文件指导 pod 如何配置三方库,可以理解为库的说明书。

版本控制和冲突

CocoaPods 使用「语义化版本 - Semantic Versioning」约束对版本的依赖。由于冲突解决系统建立在非重大变更的补丁版本之间,这使得解决依赖关系变得容易很多。例如,两个不同的 pods 依赖 XXSDK 的两个版本,假设一个依赖于 2.3.1,另一个依赖于 2.3.3,此时冲突解决系统可以使用最新的版本 2.3.3,因为这个可以向后与 2.3.1 兼容。

当然,总会有一些冲突需要手动解决。如果一个库依赖 XXSDK 的 1.2.5,另外一个库则依赖于 2.3.1,那么只有最终用户通过明确指定使用某个版本来解决冲突。

加载源文件

CocoaPods 执行的下一步是加载源码。每个 .podspec 文件都包含一个源代码的索引,这些索引一般包裹一个 git 地址和 git tag(当然源码不一定非要托管在 GitHubGitLabSVN 等其他文件托管形式都是支持的)。它们以 commit SHAs 的方式存储在 ~/Library/Caches/CocoaPods 中。这个路径中文件的创建是由 Core gem 负责的。

CocoaPods 将依照 Podfile.podspec 和缓存文件的信息将源文件下载到 Pods 目录中。

生成 Pods.xcodeproj

每次 pod install 执行,如果检测到改动时,CocoaPods 会利用 Xcodeproj gem 组件对 Pods.xcodeproj 进行更新。如果该文件不存在,则用默认配置生成。否则,会将已有的配置项加载至内存中。

进一步分析 CocoaPods

文章篇幅有限,进一步分析请阅读 CocoaPods 源码github.com/CocoaPods/C…

安装第三方库

CocoaPods 往工程中添加一个第三方库时,不仅仅是添加代码这么简单,还会添加很多内容。由于每个第三方库有不同的 target,因此对于每个库,都会有几个文件需要添加,每个 target 都需要:

  • 一个包含编译选项的 .xcconfig 文件
  • 一个同时包含编译设置和 CocoaPods 默认配置的私有 .xcconfig 文件
  • 一个编译所必须的 prefix.pch 文件
  • 一个 SwiftObjective-C 混编必须的 -umbrella.h 文件
  • 一个声明、导出模块用的 .modulemap 文件
  • 另一个编译必须的文件 dummy.m

一旦每个 podtarget 完成了上面的内容,整个 Pods target 就会被创建。如果源码中包含有资源 bundle,将这个 bundle 添加至程序 target 的指令将被添加到 Pods-Resources.sh 文件中。还有一个名为 Pods-environment.h 的文件,文件中包含了一些宏,这些宏可以用来检查某个组件是否来自 pod

写入至磁盘

直到现在,许多工作都是在内存中进行的。为了让这些成果能被重复利用,我们需要将所有的结果保存到一个文件中。所以 Pods.xcodeproj 文件被写入磁盘,另外两个非常重要的文件:Podfile.lockManifest.lock 都将被写入磁盘。

Podfile.lock

这是 CocoaPods 创建的最重要的文件之一。它记录了需要被安装的 pod 中每个已安装的版本。如果你想知道已安装的 CocoaPods 是哪个版本,可以查看这个文件。推荐将 Podfile.lock 文件加入到版本控制中,这有助于提升整个团队的一致性。

Manifest.lock

这是每次运行 pod install 命令时创建的 Podfile.lock 文件的副本。如果你遇见过这样的错误——沙盒文件与 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock),这是因为 Manifest.lock 文件和 Podfile.lock 文件不一致所引起。由于 Pods 所在的目录并不总在版本控制之下,这样可以保证开发者运行 App 之前都会更新他们的 pods

xcproj

如果你已经在系统上安装了 xcproj,它会对 Pods.xcodeproj 文件执行一下 touch 以将其转换成为旧的 ASCII plist 格式的文件。为什么要这么做呢?虽然在很久以前就不被其它软件支持了,但是 Xcode 仍然依赖于这种格式。如果没有 xcproj,你的 Pods.xcodeproj 文件将会以 XML 格式的 plist 文件存储,当你用 Xcode 打开它时,它会被改写,并造成大量的文件改动。

制作私有库

当项目源代码激增,团队规模不断扩张,组件化可能是你无法绕过的话题,但你又不可能将公司内部库以 CocoaPods 库的形式提供给所有人,这时候你就需要制作私有库仅供公司内部团队使用。

创建一个「私有 pod 库」

可以使用 CocoaPods 提供的以下命令创建库。

一、创建一个崭新的库:

1、pod lib create xxx

2、在交互式对话框里选择适合自己的选项。

3、完成以上交互你会发现在执行命令的目录下已经生成了一套完整的库模板,至此可以开始私有库功能开发了。

像这样:

Yao:Desktop username$ pod lib create XXSDK
Cloning `https://github.com/CocoaPods/pod-template.git` into `XXSDK`.
Configuring XXSDK template.
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin20/rbconfig.rb:229: warning: Insecure world writable dir /Users/username/.cargo/bin in PATH, mode 040777
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.

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

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: 
 - https://guides.cocoapods.org/making/using-pod-lib-create.html
 ( hold cmd and double click links to open in a browser. )


What platform do you want to use?? [ iOS / macOS ]
 > iOS

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

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

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

Would you like to do view based testing? [ Yes / No ]
 > No
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>

Running pod install on your new library.

/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin20/rbconfig.rb:229: warning: Insecure world writable dir /Users/username/.cargo/bin in PATH, mode 040777
Analyzing dependencies
Downloading dependencies
Installing XXSDK (0.1.0)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `XXSDK.xcworkspace` for this project from now on.
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.

[!] Your project does not explicitly specify the CocoaPods master specs repo. Since CDN is now used as the default, you may safely remove it from your repos directory via `pod repo remove master`. To suppress this warning please add `warn_for_unused_master_specs_repo => false` to your Podfile.

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

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

4、这时候目录结构如下:

module-directory

Example:示例工程,开发过程中在这里调试。 XXSDK:私有库的源代码。

  • XXSDK/Assets: 这里放资源,如:图片、视频、json文件等资源文件
  • XXSDK/Classes:代码源文件放在这里

XXSDK.podspec:这将是你最关心的文件之一,它是库的配置文件,在这里配置编译选项,管理源代码、资源和依赖,他指导 CocoaPods 怎么依赖和配置这个库,甚至可以配置宿主工程,可以理解为这个库的图纸。

二、改造一个现有的模块为库:

1、按以上格式整理你的代码文件和资源文件。

2、创建 podspec 文件:执行命令 pod spec create XXSDK

Yao:XXSDK username$ pod spec create XXSDK
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin20/rbconfig.rb:229: warning: Insecure world writable dir /Users/username/.cargo/bin in PATH, mode 040777

Specification created at XXSDK.podspec

3、创建 Example 工程:

  • 进入 Example 根目录
  • pod init
  • pod install
  • Podfile 以相对路径本地依赖你的库 pod "XXSDK", :path => "../",这里的 path 要指向 podspec 所在的目录
  • pod install 后即可在 Example 工程调试你的库

配置 podspec

当你在以上工程里创建类文件、添加资源文件后需要 pod install 后才能在 demo 工程里调试,因为 demo 工程也是以三方库依赖的形式引入的这个库。

下面来认识下 podspec 文件:

#
#  Be sure to run `pod spec lint XXSDK.podspec' to ensure this is a
#  valid spec and to remove all comments including this before submitting the spec.
#
#  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#

Pod::Spec.new do |spec|

  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  These will help people to find your library, and whilst it
  #  can feel like a chore to fill in it's definitely to your advantage. The
  #  summary should be tweet-length, and the description more in depth.
  #

  spec.name         = "XXSDK"
  spec.version      = "0.0.1"
  spec.summary      = "A short description of XXSDK."

  # 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!
  spec.description  = <<-DESC
                   DESC

  spec.homepage     = "http://EXAMPLE/XXSDK"
  # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"


  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Licensing your code is important. See https://choosealicense.com for more info.
  #  CocoaPods will detect a license file if there is a named LICENSE*
  #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
  #

  spec.license      = "MIT (example)"
  # spec.license      = { :type => "MIT", :file => "FILE_LICENSE" }


  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the authors of the library, with email addresses. Email addresses
  #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
  #  accepts just a name if you'd rather not provide an email address.
  #
  #  Specify a social_media_url where others can refer to, for example a twitter
  #  profile URL.
  #

  spec.author             = { "username" => "username@ijovo.com" }
  # Or just: spec.author    = "username"
  # spec.authors            = { "username" => "username@ijovo.com" }
  # spec.social_media_url   = "https://twitter.com/username"

  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If this Pod runs only on iOS or OS X, then specify the platform and
  #  the deployment target. You can optionally include the target after the platform.
  #

  # spec.platform     = :ios
  # spec.platform     = :ios, "5.0"

  #  When using multiple platforms
  # spec.ios.deployment_target = "5.0"
  # spec.osx.deployment_target = "10.7"
  # spec.watchos.deployment_target = "2.0"
  # spec.tvos.deployment_target = "9.0"


  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the location from where the source should be retrieved.
  #  Supports git, hg, bzr, svn and HTTP.
  #

  spec.source       = { :git => "http://EXAMPLE/XXSDK.git", :tag => "#{spec.version}" }


  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  CocoaPods is smart about how it includes source code. For source files
  #  giving a folder will include any swift, h, m, mm, c & cpp files.
  #  For header files it will include any header in the folder.
  #  Not including the public_header_files will make all headers public.
  #

  spec.source_files  = "Classes", "Classes/**/*.{h,m}"
  spec.exclude_files = "Classes/Exclude"

  # spec.public_header_files = "Classes/**/*.h"


  # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  A list of resources included with the Pod. These are copied into the
  #  target bundle with a build phase script. Anything else will be cleaned.
  #  You can preserve files from being cleaned, please don't preserve
  #  non-essential files like tests, examples and documentation.
  #

  # spec.resource  = "icon.png"
  # spec.resources = "Resources/*.png"

  # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"


  # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Link your library with frameworks, or libraries. Libraries do not include
  #  the lib prefix of their name.
  #

  # spec.framework  = "SomeFramework"
  # spec.frameworks = "SomeFramework", "AnotherFramework"

  # spec.library   = "iconv"
  # spec.libraries = "iconv", "xml2"


  # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If your library depends on compiler flags you can set them in the xcconfig hash
  #  where they will only apply to your library. If you depend on other Podspecs
  #  you can include multiple dependencies to ensure it works.

  # spec.requires_arc = true

  # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
  # spec.dependency "JSONKit", "~> 1.4"

end

字段含义:

可配置属性非常多,访问 官网(guides.cocoapods.org/syntax/pods…) 了解更多

属性含义
name库名称
version版本号
authors作者
homepage网站主页
summary库的简短描述
source源文件获取来源
swift_version支持的 Swift 版本,这里一般指定大版本,例如 4.0 ,而不是 4.1 或 4.2 这种
source_files库要暴露的源文件
framework依赖的系统库
weak_frameworks弱依赖的系统库——有些库在新的设备或系统才存在此时应该使用弱依赖,代码层面要做好兼容
dependency依赖的三方库或其他子模块
info_plistinfo_plist 配置,会覆盖依赖工程的初始值(要慎用)
requires_arc当部分代码要使用 mrc 管理内存时需要在这里配置
pod_target_xcconfigpod target 的 .xcconfig 配置
public_header_files暴露的公共头文件
exclude_files应从其他文件模式中排除的文件模式列表

私有源方案

CocoaPods 允许你将私有库发布到私有源,这时你需要维护自己的源(source)

1、创建并维护自己的「源」

「源」即存放私有库 .podspec 文件的 git 仓库,pod installpod 从源里获取三方库信息,可以托管到 GitLab

你只需创建一个 GitLab 私有仓库做为源即可。

2、Podfile 里添加自己的源

使用 source xxx 添加一个源;例如:source 'https://github.com/CocoaPods/Specs.git'

source 'http://gitlab.XXX/XXX/PrivateSpecs.git'

3、私有库开发、维护

源代码开发、资源管理和 podspec 维护

4、发布私有库到私有源 PrivateSpecs

发布一个库到私有源其实就是将该版本的 podspec 文件复制到「源」仓库中具有一定规则的文件夹内,后面 pod install 命令执行时 pod 就能找到你的私有库了。

目录结构像这样: pod-source-directory

  1. 「源」仓库下创建一个与库同名的文件夹
  2. 在该文件夹创建一个以版本号命名的文件夹
  3. copy podspec 文件到对应版本号的文件夹下

这些可以不用你手动操作,CocoaPods 提供了工具帮你做好这一切

  • pod lib lint XXSDK.podspec 校验合法性
  • pod repo push privateSpecs XXSDK.podspec 发布到私有源

当然如果你觉得 lint 校验过于苛刻,你也可以按上述规则借助脚本实现自动化发版.

解决方案集合

如何使用私有库?

需要在 Podfile 的头部添加私有源,告诉 pod 该去哪获取你的私有库。

Podfile 头部添加私有源

source 'http://gitlab.XXX/XXX/PrivateSpecs.git'

主工程无法找到你的组件

请检查是否是问题「如何使用私有库」引起的。

组件里新增的文件消失了

有时你可能会遇到这种问题,刚完成了一个功能开发准备跟领导炫耀,顺手敲了 pod install,Xcode 编译的时候发现报错了,仔细一看刚才开发的文件都不见了。

分析:遇到这种问题很可能是你创建文件的时候放错了地方,而这个地方没被包含在 source_files 里,此时 pod install 这些文件就不会被引用。

方案Show in Finder,找到消失的文件并放到正确的位置。

图片、视频、字体、json 文件等资源放哪里?

建议通过 resource_bundle 把资源打包进 bundle,注意使用这些资源时要从对应的 bundle 加载。

spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }

如何使用 resource_bundle 指定的资源文件?

这时你需要从指定 bundle 加载资源,且文件被打包为 bundle 放在了 mainbundle 下,因为每个库的 bundle 组织方式可能不太一样,所以目录结构也不是固定的,你可以解包应用的 .ipa 文件来研究下这个规则。

私有库包含 .framework.a 怎么配置 podspec

建议第一时间搞懂 podspec 各字段含义

spec.ios.vendored_frameworks = 'Frameworks/MyFramework.framework'
spec.vendored_frameworks = 'MyFramework.framework', 'TheirFramework.xcframework'

spec.ios.vendored_library = 'Libraries/libProj4.a'
spec.vendored_libraries = 'libProj4.a', 'libJavaScriptCore.a'

最低支持 iOS 9.0 的模块 A 依赖最低支持 iOS 10 的 B 模块

出现这种问题后可以评估下 A 模块能不能升到 iOS 10,或者 B 模块能不能降到 iOS 9, 如果不能则要考虑换掉其中一个了。

你的库提供了核心能力 core、特定功能 A,有些业务线只需要依赖核心能力

通过 subspec「子模块」方式设计私有库可能是更好的选择。

podspec 文件这样配置

  spec.subspec 'core' do |core|
    core.source_files = 'Classes/core/**/*.{swift,h,m,c,mm}'
  end

  spec.subspec 'A' do |A|
    A.source_files = 'Classes/A/**/*.{swift,h,m,c,mm}'
    A.dependency 'XXSDK/core'
  end
  
  spec.subspec 'B' do |B|
    B.source_files = 'Classes/B/**/*.{swift,h,m,c,mm}'
    B.dependency 'XXSDK/core'
  end

Podfile 里这样引用

pod XXSDK/core
or
pod XXSDK/bbf
or
pod XXSDK

模块内 Objective-C 与 Swift 混编

在 Apple 强推 Swift 的背景下,业务迭代自然会使用 Swift 编写,Swift 不可避免会调用 Objective-C,Objective-C 也很可能要调用 Swift。

Objective-C 调用 Swift:需导入桥接头文件 ModuleName-Swift.h, 编译后就可以使用。

Swift 调用 Objective-C:可直接调用。

紧急修复三方库的 bug

反馈给三方库作者是最好的办法,不过有时你等不及,必须自己解决立马上线,你知道怎么从代码层面改掉这个 bug,不过你苦于无法改动源码好像也不值得维护一个镜像。

你可以在 Podfile 里修改三方库源文件,像这样:

Podfile 文件

post_install do |installer|
    find_and_replace("xx.Swift", bugCode, fixCode)
end

修复 bug 后拉不到最新代码

修改了库源码并重新打了 tagpod install 死活拉不到最新 tag 的代码,这也许是你修改了一个库的代码而没有修改版本号重新发版导致的。

CocoaPods 会对下载过的库源码进行缓存,不修改版本号即使你重新打了 tagpod install 还是会直接从缓存 copy 一份代码到工作目录。

解决这个问题有两种方案:

一、修改版本号发布个新版本。

二、如果你不想频繁发布新版本,那就需要:

1、重新打 tag。

2、清理缓存:

pod cache clean XXSDK
或
从这里 ~/Library/Caches/CocoaPods/ 找到你的库手动删除。

3、pod install

想忽略已经提交过的 Pods 文件

某一天,你的领导克隆 iOS 工程时发现整个工程的大小为 10 个 GB,问你 Pods 目录里放的是什么,能不能忽略?这时候你意识到 Pods 不应被纳入版本控制,这时候你去 .gitignore 加了句 Pods/,一个月后你发现并没起作用。

执行以下命令即可解除 git 管理:

1、在 .gitignore 文件里加上 Pods/

2、执行以下 git 命令:

cd 项目根目录
git rm -r --cached .
git add .
git commit -m "忽略 Pods 文件。"

忽略 Pods 警告

Podfile 中对应的 target 添加 inhibit_all_warnings 即可忽略 pod 警告。

我要在主工程也能修改并提交组件代码

本地依赖方式允许直接修改源代码并提交。 像这样引用依赖: pod 'XXSDK', :path => 'xxx/xxx/XXSDK.podspec'

组件化初期维护成本过高

组件化初期常常同时修改多个模块且需要在主工程联调,远程方式依赖这些组件是不能在主工程改动并提交的,那么你组件化进程将严重拖累业务迭代还会遭到队友的吐槽。

分析:远程依赖无法改动并提交三方库代码,CocoaPods 默认给远程三方库加了锁,应该特别注意即使你选择解锁文件也不要在三方库里添加重要代码,因为 pod install 后这些改动都会消失而且无法找回。 方案:本地依赖结合 git submodule。引入 git submodule 是为了使用相对路径,团队内各成员使用相对路径引用子模块:pod 'XXSDK', :path => 'Submodules/XXSDK',当然模块稳定后应尽快发版并移除子模块。

小结

CocoaPods 简化了 iOS 的开发流程,在这里你可以找到适合自己的优秀三方库,或者把自己的代码以三方库的形式发布到 CocoaPods 上助力 iOS 开源生态,也可以发布到公司内部供内部使用。了解 CocoaPods 的原理能让你充分利用开源工具提高生产力,开拓思路,进而找到更好的解决方案。