做网站教程流程,做营销网站企业,网页设计基础教程上机实训,wordpress 添加栏目前言
对于大多数软件开发团队来说#xff0c;依赖管理工具必不可少#xff0c;它能针对开源和私有依赖进行安装与管理#xff0c;从而提升开发效率#xff0c;降低维护成本。针对不同的语言与平台#xff0c;其依赖管理工具也各有不同#xff0c;例如 npm 管理 Javascri…
前言
对于大多数软件开发团队来说依赖管理工具必不可少它能针对开源和私有依赖进行安装与管理从而提升开发效率降低维护成本。针对不同的语言与平台其依赖管理工具也各有不同例如 npm 管理 Javascript、Gradle 、Maven 管理 Jar 包、pip 管理 Python 包Bundler、RubyGems 等等。本文聚焦于 iOS 方面对 CocoaPods 的使用和部分原理进行阐述。
简单易用的 CocoaPods
对于 iOSer 来说CocoaPods 并不陌生几乎所有的 iOS 工程都会有它的身影。CocoaPods 采用 Ruby 构建它是 Swift 和 Objective-C Cocoa 项目的依赖管理工具。在 MacOS 上推荐使用默认的 Ruby 进行安装 (以下操作均在 CocoaPods 1.10.1、Ruby 2.7.2 进行)
sudo gem install cocoapods如果安装成功便可以使用 pod 的相关命令了。针对一个简单的项目来说只需三步便可引入其他的依赖
创建 Podfile 文件( CocoaPods 提供了 pod init 命令创建) 对 Podfile 文件进行编写添加依赖的库版本等信息。 在命令行执行 pod install 命令
顺利的话这时在项目目录下会出现以下文件
.xcworkspaceCocoaPods 将项目分为了主工程与依赖工程(Pods)。与 .xcodeproj 相比 .xcworkspace 对于管理多个项目的能力更强你也可以将复杂的大型应用转换为以 .xcworkspace 构建的多个兄弟项目从而更轻松的维护和共享功能。Podfile.lock记录并跟踪依赖库版本将依赖库锁定于某个版本。Pods 文件夹存放依赖库代码。Pods/Manifest.lock每次 pod install 时创建的 Podfile.lock 的副本用于比较这两个文件。一般来说Podfile.lock 会纳入版本控制管理而 Pods 文件夹则不会纳入版本控制变更这意味着 Podfile.lock 表示项目应该依赖的库版本信息而 Manifest.lock 则代表本地 Pods 的依赖库版本信息。在 pod install 后会将脚本插入到 Build Phases名为 [CP] Check Pods Manifest.lock从而保证开发者在运行 app 之前能够更新 Pods以确保代码是最新的。
pod install vs. pod update
pod install在每一次编辑 Podfile 以添加、更新或删除 pod 时使用。它会下载并安装新的 Pod并将其版本信息写入 Podfile.lock 中。pod outdated列出所有比 Podfile.lock 中当前记录的版本 newer 版本的 pod。pod update [PODNAME]CocoaPods 会查找 newer 版本的 PODNAME同时将 pod 更新到可能的最新版本(须符合 Podfile 限制)。若没有 PODNAME则会将每一个 pod 更新到可能的最新版本。
一般来说每次编辑 Podfile 时使用 pod install仅在需要更新某个 pod 版本(所有版本)时才使用 pod update。同时需提交 Podfile.lock 文件而不是 Pods 文件夹来达到同步所有 pod 版本的目的。
ps: newer 代表更加新的若采用中文理解起来比较别扭。
Podfile 语法规范
Podfile 描述了一个或多个 Xcode 项目的 target 依赖关系它是一种 DSL了解它对我们使用好 CocoaPods 是一个必不可少的步骤。下面列出其相关的语法规范
Root Options
install!指定 CocoaPods 安装 Podfile 时使用的安装方法和选项。如
install! cocoapods,:deterministic_uuids false,:integrate_targets false:clean根据 podspec 和项目支持平台的指定清理所有不被 pod 使用的文件默认为 true。:deduplicate_targets是否对 pod target 进行重复数据删除默认为 true。:deterministic_uuids创建 pod project 是否产生确定性 UUID默认为 true。:integrate_targets是否继承到用户项目中为 false 会将 Pod 下载并安装到到 project_path/Pods 目录下默认为 true。:lock_pos_sources是否锁定 pod 的源文件当 Xcode 尝试修改时会提示解锁文件默认为 true。:warn_for_multiple_pod_sources当多个 source 包含同名同版本 pod 时是否发出警告默认为 true。:warn_for_unused_master_specs_repo如果没有明确指出 master specs repo 的 git 是否发出警告默认为 true。:share_schemes_for_development_pods是否为开发中的 pod 分享 schemes默认为 false。:disable_input_output_paths是否禁用 CocoaPods 脚本阶段的输入输出路径Copy Frameworks 和 Copy Resources默认为 false。:preserve_pod_file_structure是否保留所有 pod 的文件结构默认为 false。:generate_multiple_pod_projects是否为每一个 pod target 生成 一个 project生成与 Pods/Pods 文件夹中默认为 false。:incremental_installation仅对自上次安装的 target 与其关联的 project 的变更部分进行重新生成默认为 false。:skip_pods_project_generation是否跳过生成 Pods.xcodeproj 并仅进行依赖项解析与下载默认为 false。 ensure_bundler!当 bundler 版本不匹配时发出警告。
ensure_bundler! ~ 2.0.0Dependencies
pod指定项目的依赖项
依赖版本控制、、、、 为字面意思~ 0.1.2 表示 0.1.2 currVersion 0.2 之间的符合要求的最新版本版本。Build configurations默认依赖安装在所有的构建配置中但也可仅在指定构建配置中启用。Modular Headers用于将 pod 转换为 module 以支持模块这时在 Swift 中可以不用借助 bridging-header 桥接就可以直接导入简化了 Swift 引用 Objective-C 的方式也可以采用 use_modular_headers! 进行全局的变更。Source指定具有依赖项的源同时会忽略全局源。Subspecs默认会安装所有的 subspecs但可制定安装某些 subspecs。Test Specs默认不会安装 test specs但可选择性安装 test specs。Local path将开发的 pod 与其客户端一起使用可采用 path。指定某个特殊或者更为先进的 pod 版本
# 依赖版本控制
pod Objection, ~ 0.9
# Build configurations
pod PonyDebugger, :configurations [Debug, Beta]
# Modular Headers
pod SSZipArchive, :modular_headers true
# Source
pod PonyDebugger, :source https://github.com/CocoaPods/Specs.git
# Subspecs
pod QueryKit, :subspecs [Attribute, QuerySet]
# Test Specs
pod AFNetworking, :testspecs [UnitTests, SomeOtherTests]
# Local path
pod AFNetworking, :path ~/Documents/AFNetworking
# 指定某个特殊或者更为先进的 Pod 版本
pod AFNetworking, :git https://github.com/gowalla/AFNetworking.git, :branch dev
pod AFNetworking, :git https://github.com/gowalla/AFNetworking.git, :tag 0.7.0
pod AFNetworking, :git https://github.com/gowalla/AFNetworking.git, :commit 082f8319af
# 指定某个 podspec
pod JSONKit, :podspec https://example.com/JSONKit.podspecinherit设置当前 target 的继承模式。
:complete 继承父级 target 的所有行为:none 不继承父级 target 的任何行为:search_paths 仅继承父级的搜索路径。
target App dotarget AppTests doinherit! :search_pathsend
endtarget与 Xcode 中的 target 相对应block 中是 target 的依赖项。 默认情况下target 包含在父级 target 定义的依赖项也即 inherit! 为 :complete。关于 :complete 和 :search_paths:complete 会拷贝父级 target 的 pod 副本而 :search_paths 则只进行 FRAMEWORK_SEARCH_PATHS 和 HEADER_SEARCH_PATHS 的相关拷贝具体可通过比对 Pods/Target Support Files 的相关文件得以验证一般在 UnitTests 中使用以减少多余的 install_framework 过程。
target ShowsApp dopod ShowsKit# 拥有 ShowsKit 和 ShowTVAuth 的拷贝target ShowsTV dopod ShowTVAuthend# 拥有 Specta 和 Expecta 的拷贝# 并且能够通过 ShowsApp 进行访问 ShowsKit, 相当于 ShowsApp 是 ShowsTests 的宿主APPtarget ShowsTests doinherit! :search_pathspod Spectapod Expectaend
endabstract_target定义 abstract_target方便 target 进行依赖继承在 CocoaPods 1.0 版本之前为 link_with。
abstract_target Networking dopod AlamoFiretarget Networking App 1target Networking App 2
endabstract表示当前 target 是抽象的不会链接到 Xcode 的 target 中。 script_phase添加脚本阶段。 在执行完 pod install 之后 CocoaPods 会将脚本添加到对应的 target build phases。
target App do
script_phase {
:name scriptName # 脚本名称,:script echo nihao # 脚本内容,:execution_position :before_compile / :after_compile:shell_path /usr/bin/ruby # 脚本路径:input_files [/input/filePath], # 输入文件:output_files [/outpput/filePath] # 输出文件
}
endTarget configuration
platform指定其构建平台。 默认值为 iOS 4.3、OSX 10.6、tvOS 9.0 和 watchOS 2.0。CocoaPods 1.0 之前的版本为 xcodeproj
platform :ios, 4.0project指定包含 target 的 Xcode project。这一般在 workspace 存在多个 xcode project 中使用
# 在 FastGPS Project 中可以找到一个名为 MyGPSApp 的 target
target MyGPSApp doproject FastGPS...
endinhibit_all_warnings!禁止所有警告。如果针对单个 Pod则可以采用
pod SSZipArchive, :inhibit_warnings true
pod SSZipArchive, :inhibit_warnings trueuser_modular_headers!将所有 Pod 模块化。如果针对单个 Pod则可以采用
pod SSZipArchive, :modular_headers true
pod SSZipArchive, :modular_headers falseuser_frameworks!采用 framework 而不是 .a 文件的静态库。 可以通过 :linkage 指定使用静态库还是动态库
use_frameworks:linkage :dynamic / :staticsupports_swift_versions指定 target definition 支持的 swift 版本要求 supports_swift_versions 3.0, 4.0 Workspace
workspace指定包含所有项目的 Xcode workspace。
workspace MyWorkspaceSources
sourcesPodfile 从指定的源列表中进行检索。sources 默认存储在 ~/.cocoapods/repos 中是全局的而非按 target definition 存储。当有多个相同的 Pod 时优先采用检索到的 Pod 的第一个源因此当指定另一个来源时则需显示指定 CocoaPods 的源。
source https://github.com/artsy/Specs.git
source https://github.com/CocoaPods/Specs.gitHooks
plugin指定在安装期间使用的插件。
plugin cocoapods-keys, :keyring Eidolon
plugin slatherpre_install在下载后和在安装 Pod 前进行更改。
pre_install do |installer|# Do something fancy!
endpre_integrate在 project 写入磁盘前进行更改。
pre_integrate do |installer|# perform some changes on dependencies
endpost_install对生成 project 写入磁盘前进行最后的修改。
post_install do |installer|installer.pods_project.targets.each do |target|target.build_configurations.each do |config|config.build_settings[GCC_ENABLE_OBJC_GC] supportedendend
endpost_integrate在 project 写入磁盘后进行最后更改。
post_integrate do |installer|# some change after project write to disk
endpodspec 语法规范
podspec pod Specification意为 pod 规范它是一个 Ruby 文件。包含了 Pod 的库版本详细信息例如应从何处获取源、使用哪些文件、要应用构建设置等信息也可以看作该文件是整个仓库的索引文件了解它对我们知道 Pod 库是如何组织、运作的提供了很大帮助。podspec 的 DSL 提供了极大的灵活性文件可通过 pod spec create 创建。
Root
名称用途必需namepod 名称requiredversionpod 版本遵循语义化版本控制requiredswift_version支持的 Swift 版本cocoapods_version支持的 CocoaPods 版本authorspod 维护者的姓名和电子邮件用“, ”进行分割requiredlicensepod 的许可证requiredhomepagepod 主页的 URLrequiredsource源地址即源文件的存放地址支持多种形式源requiredsummarypod 的简短描述requiredprepare_command下载 pod 后执行的 bash 脚本static_framework是否采用静态 framework 分发deprecated该库是否已被弃用deprecated_in_favor_of该库名称已被弃用取而代之
Pod::Spec.new do |s|s.name CustomPods.version 0.1.0s.summary A short description of CustomPod.s.swift_versions [3.0, 4.0, 4.2]s.cocoapods_version 0.36s.author { nihao XXXXqq.com }s.license { :type MIT, :file LICENSE }s.homepage https://github.com/XXX/CustomPod# Supported Key
# :git :tag, :branch, :commit,:submodules
# :svn :folder, :tag,:revision
# :hg:revision
# :http :flatten, :type, :sha256, :sha1,:headerss.source { :git https://github.com/XX/CustomPod.git, :tag s.version.to_s }s.prepare_command ruby build_files.rbs.static_framework trues.deprecated trues.deprecated_in_favor_of NewMoreAwesomePod
endPlatform
platformpod 支持的平台留空意味着 pod 支持所有平台。当支持多平台时应该用 deployment_target 代替。
spec.platform :osx, 10.8deployment_target允许指定支持此 pod 的多个平台为每个平台指定不同的部署目标。
spec.ios.deployment_target 6.0
spec.osx.deployment_target 10.8Build settings
dependency基于其他 pods 或子规范的依赖
spec.dependency AFNetworking, ~gt; 1.0, :configurations gt; [Debug]info_plist加入到生成的 Info.plist 的键值对会对 CocoaPods 生成的默认值进行覆盖。仅对使用 framework 的框架有影响对静态库无效。对于应用规范这些值将合并到应用程序主机的 Info.plist对于测试规范这些值将合并到测试包的 Info.plist。
spec.info_plist {CFBundleIdentifier gt; com.myorg.MyLib,MY_VAR gt; SOME_VALUE
}requires_arc允许指定哪些 source_files 采用 ARC不使用 ARC 的文件将具有 -fno-objc-arc 编译器标志
spec.requires_arc false
spec.requires_arc Classes/Arc
spec.requires_arc [Classes/*ARC.m, Classes/ARC.mm]frameworks使用者 target 需要链接的系统框架列表
spec.ios.framework CFNetwork
spec.frameworks QuartzCore, CoreDataweak_frameworks使用者 target 需要弱链接的框架列表
spec.weak_framework Twitter
spec.weak_frameworks Twitter, SafariServiceslibraries使用者 target 需要链接的系统库列表
spec.ios.library xml2
spec.libraries xml2, zcompiler_flags应传递给编译器的 flags
spec.compiler_flags -DOS_OBJECT_USE_OBJC0, -Wno-formatpod_target_xcconfig将指定 flag 添加到最终 pod 的 xcconfig 文件
spec.pod_target_xcconfig { OTHER_LDFLAGS gt; -lObjC }user_target_xcconfig 将指定 flag 添加到最终聚合的 target 的 xcconfig不推荐使用此属性因为会污染用户的构建设置可能会导致冲突。
spec.user_target_xcconfig { MY_SUBSPEC gt; YES }prefix_header_contents 在 Pod 中注入的预编译内容不推荐使用此属性因为其会污染用户或者其他库的预编译头。
spec.prefix_header_contents #import lt;UIKit/UIKit.hgt;, #import lt;Foundation/Foundation.hgt;prefix_header_file预编译头文件false 表示不生成默认的 CocoaPods 的与编译头文件。 不推荐使用路径形式因为其会污染用户或者其他库的预编译头。
spec.prefix_header_file iphone/include/prefix.pch
spec.prefix_header_file falsemodule_name生成的 framrwork / clang module 使用的名称而非默认名称。
spec.module_name Three20header_dir存储头文件的目录这样它们就不会被破坏。
spec.header_dir Three20Coreheader_mappings_dir用于保留头文件文件夹的目录。如未提供头文件将被碾平。
spec.header_mappings_dir src/includescript_phases该属性允许定义脚本在 pod 编译时执行其作为 xcode build 命令的一部分执行还可以利用编译期间所设置的环境变量。
spec.script_phases [{ :name gt; Hello World, :script gt; echo Hello World },{ :name gt; Hello Ruby World, :script gt; puts Hello World, :shell_path gt; /usr/bin/ruby },]File patterns
文件模式指定了库的所有文件管理方式如源代码、头文件、framework、libaries、以及各种资源。其文件模式通配符形式可参考 LINK。
source_files指定源文件
spec.source_files Classes/**/*.{h,m}, More_Classes/**/*.{h,m}public_header_files指定公共头文件这些头文件与源文件匹配并生成文档向用户提供。如果未指定则将 source_files 中的所有头文件都包含生成。
spec.public_header_files Headers/Public/*.hproject_header_files指定项目头文件与公共头文件相对应以排除不应向用户项目公开且不应用于生成文档的标头且不会出现在构建目录中。
spec.project_header_files Headers/Project/*.hprivate_header_files私有头文件与公共头文件对应以排除不应向用户项目公开且不应用于生成文档的标头这些头文件会出现在产物中的 PrivateHeader 文件夹中。
spec.private_header_files Headers/Private/*.hvendered_frameworkspod 附加的 framework 路径
spec.ios.vendored_frameworks Frameworks/MyFramework.framework
spec.vendored_frameworks MyFramework.framework, TheirFramework.xcframeworkvendered_librariespod 附加的 libraries 路径
spec.ios.vendored_library Libraries/libProj4.a
spec.vendored_libraries libProj4.a, libJavaScriptCore.aon_demand_resources根据 Introducing On demand Resources 按需加载资源不推荐与主工程共享标签默认类别为 category gt; :download_on_demand
s.on_demand_resources {Tag1 gt; { :paths gt; [file1.png, file2.png], :category gt; :download_on_demand }
}
s.on_demand_resources {Tag1 gt; { :paths gt; [file1.png, file2.png], :category gt; :initial_install }
}resources为 pod 构建的 bundle 的名称和资源文件其中 key 为 bundle 名称值代表它们应用的文件模式。
spec.resource_bundles {
MapBox gt; [MapView/Map/Resources/*.png],MapBoxOtherResources gt; [MapView/Map/OtherResources/*.png]
}exclude_files排除的文件模式列表
spec.ios.exclude_files Classes/osx
spec.exclude_files Classes/**/unused.{h,m}preserve_paths下载后不应删除的文件。默认情况下CocoaPods 会删除与其他文件模式不匹配的所有文件
spec.preserve_path IMPORTANT.txt
spec.preserve_paths Frameworks/*.frameworkmodule_mappod 继承为 framework 时使用的模块映射文件默认为 trueCocoaPods 根据 公共头文件创建 module_map 文件。
spec.module_map source/module.modulemap
spec.module_map falseSubspecs
subspec子模块的规范实行双重继承specs 自动继承所有 subspec 作为依赖项(除非指定默认 spec)subspec 继承了父级的属性
# 采用不同源文件的 Specs, CocoaPods 自动处理重复引用问题
subspec Twitter do |sp|sp.source_files Classes/Twitter
endsubspec Pinboard do |sp|sp.source_files Classes/Pinboard
end# 引用其他子规范
s.subspec Core do |ss|ss.source_files Sources/Moya/, Sources/Moya/Plugins/ss.dependency Alamofire, ~gt; 5.0ss.framework Foundationends.subspec ReactiveSwift do |ss|ss.source_files Sources/ReactiveMoya/ss.dependency Moya/Coress.dependency ReactiveSwift, ~gt; 6.0ends.subspec RxSwift do |ss|ss.source_files Sources/RxMoya/ss.dependency Moya/Coress.dependency RxSwift, ~gt; 5.0end
end# 嵌套子规范
Pod::Spec.new do |s|s.name Roots.subspec Level_1 do |sp|sp.subspec Level_2 do |ssp|endend
enddefault_subspecs默认子规范数组名称不指定将全部子规范作为默认子规范:none 表示不需要任何子规范。
spec.default_subspec Core
spec.default_subspecs Core, UI
spec.default_subspecs :nonescheme用以给指定 scheme configuration 添加拓展
spec.scheme { :launch_arguments gt; [Arg1] }
spec.scheme { :launch_arguments gt; [Arg1, Arg2], :environment_variables gt; { Key1 gt; Val1} }test_spec测试规范在 1.8 版本支持。可参考CocoaPods 1.8 Beta
requires_app_host是否需要宿主 APP 运行测试仅适用于测试规范。
app_host_name必要时作用于应用程序的应用程序规范名称
app_spec宿主 APP 规范
Pod::Spec.new do |s|s.name CannonPodders.version 1.0.0# ...rest of attributes heres.app_spec DemoApp do |app_spec|app_spec.source_files DemoApp/**/*.swift# Dependency used only by this app spec.app_spec.dependency Alamofireends.test_spec Tests do |test_spec|test_spec.requires_app_host true# Use DemoApp as the app host.test_spec.app_host_name CannonPodder/DemoApp# ...rest of attributes here# This is required since DemoApp is specified as the app host.test_spec.dependency CannonPodder/DemoAppend
endMulti-Platform support
存储特定于某一个平台的值分别为 ios、osx、macOS、tvos、watchos
spec.resources Resources/**/*.png
spec.ios.resources Resources_ios/**/*.pngPod 的开发流程
了解完 Podfile 和 podspec 的相关的规范之后那么开发自己的 pod 应该是一件驾轻就熟的事。
Spec Repo
Spec Repo 是 podspec 的仓库即是存储相关的 podspec 文件的地方。本地源存储于 ~/.cocoapods/repos中它从 git 上拉取并完全保留目录结构。可以发现 Master Specs Repo 的现在目录结构有些特殊以往版本的 Master Spec Repo 是完全在同一目录下的但若大量文件在同一目录中会导致了 Github 下载慢 的问题。为解决这个问题采用散列表形式处理。具体方式为对名称进行 MD5 计算得到散列值取前三位作为目录前缀以对文件分散化。初次之外CocoaPods 后续还采用 CDN 以及 trunk 进一步加快下载速度有兴趣可以参考 CocoaPods Source 管理机制。
如md5(CJFoundation) gt; 044d913fdd5a52b303222c357521f744CJFoundation 则在 /Specs/0/4/4 目录中 Create
只需利用 pod lib create [PodName] 命令便可以快速创建一个自己的 pod 。填写好使用平台、使用语言、是否包含 Demo、测试框架等信息CocoaPods 会从默认的 Git 地址中拉取一份 pod 模版同时也可以通过 --template-urlURL 指定模版地址。在执行完后整个文件结构如下
tree CustomPod -L 2
CustomPod
├── CustomPod
│ ├── Assets // 存放资源文件
│ └── Classes
│ └── RemoveMe.[swift/m] // 单一文件以确保最初编译工作
├── CustomPod.podspec // Pod 的 spec 文件, 是一个 Pod 依赖的索引以及规范信息
├── Example // 用作演示/测试的示例项目
│ ├── CustomPod
│ ├── CustomPod.xcodeproj
│ ├── CustomPod.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── _Pods.xcodeproj -gt; Example/Pods/Pods.xcodeproj // 指向 Pods 项目的以获得 Carthage 支持
├── LICENSE // 许可证
└── README.md // 自述文件Development
将源文件和资源分别放入 Classes / Assets 文件夹中或者按你喜欢的方式组织文件并在 podspec 文件中编辑相应项。如果你有任何想使用的配置项可参考前面的podsepc 语法规范 。
一般来说开发 Pod 一般都是作为本地 Pod 被其他 Project 所依赖进行开发无论是使用 example 文件夹的 project 或者其他的 Project。
pod Name, :path gt; ~/CustomPod/
Testing
通过 pod lib lint 以验证 Pod 仓库的使用是否正常。
Release
前面提到过 podspec 可以看作是整个仓库的索引文件有了这个文件也就能组织起一个 Pod。因此官方的源以及私有源都只需要 podspec 即可而其他文件则应推送到 podspec 中 source 中指定仓库这个仓库应该是你自创建的。
在准备发布推送源代码时需要更新版本号以及在 git 上打上 tag这是为了进行版本号匹配因为默认情况下的 podspec 文件中
s.source { :git gt; https://github.com/XXX/CustomPod.git, :tag gt; s.version.to_s }可能你的工作流操作如下
$ cd ~/code/Pods/NAME
$ edit NAME.podspec
# set the new version to 0.0.1
# set the new tag to 0.0.1
$ pod lib lint
$ git add -A amp;amp; git commit -m Release 0.0.1.
$ git tag 0.0.1
$ git push --tags存有几种方式推送 podspec 文件
推送到公共仓库需要用到的 trunk 子命令更多可以参考 Getting setup with Trunk
# 通过电子邮箱进行注册
pod trunk register ortacocoapods.org Orta Therox --descriptionmacbook air
# 将指定podspec文件推送到公共仓库中
pod trunk push [NAME.podspec]
# 添加其他人作为协作者
pod trunk add-owner ARAnalytics kylecocoapods.org 推送到私有源例如 Artsy/Specs需要用到 repo 子命令更多可以参考 Private Pods
# 将私有源地址添加到本地
pod repo add REPO_NAME SOURCE_URL
# 检查私有源是否安装成功并准备就绪
cd ~/.cocoapods/repos/REPO_NAME
pod repo lint .
# 将Pod的podspec添加到指定REPO_NAME中
pod repo push REPO_NAME SPEC_NAME.podspec不推送到任何源中若能存在以 URL 方式检索到 podspec文件则可用该 URL一般采用仓库地址例如
pod AFNetworking, :git gt; https://github.com/XXX/CustomPod.gitSemantic Versioning
语义化版本控制顾名思义是一种语义上的版本控制它不要求强制遵循只是希望开发者能够尽量遵守。如果库之间依赖关系过高可能面临版本控制被锁死的风险可能需要对每一个依赖库改版才能完成某次升级如果库之间依赖关系过于松散又将无法避免版本的混乱可能库兼容性不再能支持以往版本语义化版本控制正是作为这个问题的解决方案之一。无论在 CocoaPods 中还是 Swift Packager Manager 上官方都希望库开发者的的版本号能遵循这一原则
例如给定版本号 MAJOR.MINOR.PATCH
MAJOR进行不兼容的 API 更改时进行修改MINOR向后兼容的方式添加新功能时进行修改PATCH进行向后兼容的错误修复时进行修改
先行版本号以及版本编译信息可以添加到 MAJOR.MINOR.PATCH 后面以作为延伸。
CocoaPods 原理浅析
CococaPods 核心组件
CocoaPods 被 Ruby 管理其核心部分也被分为一个一个组件。下载源码可以看到 Gemfile 文件如下其依赖了若干个 gem有意思的是 cp_gem 函数通过 SKIP_UNRELEASED_VERSIONS 与 path 来控制是否采用本地的 gem 路径实现了 DEVELOPMENT 与 RELEASE 环境的切换。
SKIP_UNRELEASED_VERSIONS false
# Declares a dependency to the git repo of CocoaPods gem. This declaration is
# compatible with the local git repos feature of Bundler.
def cp_gem(name, repo_name, branch master, path: false)return gem name if SKIP_UNRELEASED_VERSIONSopts if path{ :path gt; ../#{repo_name} }elseurl https://github.com/CocoaPods/#{repo_name}.git{ :git gt; url, :branch gt; branch }endgem name, opts
endsource https://rubygems.orggemspecgroup :development docp_gem claide, CLAidecp_gem cocoapods-core, Corecp_gem cocoapods-deintegrate, cocoapods-deintegratecp_gem cocoapods-downloader, cocoapods-downloadercp_gem cocoapods-plugins, cocoapods-pluginscp_gem cocoapods-search, cocoapods-searchcp_gem cocoapods-trunk, cocoapods-trunkcp_gem cocoapods-try, cocoapods-trycp_gem molinillo, Molinillocp_gem nanaimo, Nanaimocp_gem xcodeproj, Xcodeprojgem cocoapods-dependencies, ~gt; 1.0.beta.1...
end这些组件相对独立被分成一个一个 Gem 包在 Core Components 中可以找到对这些组件的简要描述。同时也可以到 CocoaPods 的 Github 中去看详细文档。
CocoaPods命令行支持与安装程序也会处理 CocoaPods 的所有用户交互。cocoapods-core对模版文件的解析如 Podfile、.podspec 等文件。CLAide一个简单的命令解析器它提供了一个快速创建功能齐全的命令行界面的 API。cocoapods-downloader用于下载源码为各种类型的源代码控制器(HTTP/SVN/Git/Mercurial) 提供下载器。它提供 tags、commites、revisions、branches 以及 zips 文件的下载与解压缩操作。MonlinilloCocoaPods对于依赖仲裁算法的封装它是一个具有前项检察的回溯算法。不仅在 pods 中Bundler 和 RubyGems 也是使用这一套仲裁算法。Xcodeproj通过 Ruby 来对 Xcode projects 进行创建于修改。如脚本管理、libraries 构建、Xcode workspece 和配置文件的管理。cocoapods-plugins插件管理其中有 pod plugins 命令帮助你获取的可用插件列表以及开发一个新插件等功能具体可用 pod plugins --help 了解。
pod install 做了什么
执行 pod install --verbose会显示 pod install 过程中的更多 debugging 信息。下文主要参考整体把握 CocoaPods 核心组件
经过消息转发与 CLAide 命令解析最终调用了 CocoaPods/lib/cocoapods/installer.rb 的 install! 函数主要流程图如下
def install!prepareresolve_dependenciesdownload_dependenciesvalidate_targetsclean_sandboxif installation_options.skip_pods_project_generation?show_skip_pods_project_generation_messagerun_podfile_post_install_hookselseintegrateendwrite_lockfilesperform_post_install_actions
end1. Install 环境准备prepare
def prepare# 如果检测出当前目录是 Pods直接 raise 终止if Dir.pwd.start_with?(sandbox.root.to_path)message Command should be run from a directory outside Pods directory.message lt;lt; \n\n\tCurrent directory is #{UI.path(Pathname.pwd)}\nraise Informative, messageendUI.message Preparing do# 如果 lock 文件的 CocoaPods 主版本和当前版本不同将以新版本的配置对 xcodeproj 工程文件进行更新deintegrate_if_different_major_version# 对 sandbox(Pods) 目录建立子目录结构sandbox.prepare# 检测 PluginManager 是否有 pre-install 的 pluginensure_plugins_are_installed!# 执行插件中 pre-install 的所有 hooks 方法run_plugins_pre_install_hooksend
end在 prepare 阶段会完成 pod install 的环境准备包括目录结构、版本一致性以及 pre_install 的 hook。
2. 解决依赖冲突resolve dependencies
def resolve_dependencies# 获取 Sourcesplugin_sources run_source_provider_hooks# 创建一个 Analyzeranalyzer create_analyzer(plugin_sources)# 如果带有 repo_update 标记UI.section Updating local specs repositories do# 执行 Analyzer 的更新 Repo 操作analyzer.update_repositoriesend if repo_update?UI.section Analyzing dependencies do# 从 analyzer 取出最新的分析结果analysis_resultaggregate_targetspod_targetsanalyze(analyzer)# 拼写错误降级识别白名单过滤validate_build_configurationsend# 如果 deployment? 为 true会验证 podfile amp; lockfile 是否需要更新UI.section Verifying no changes doverify_no_podfile_changes!verify_no_lockfile_changes!end if deployment?analyzer
end通过 Podfile、Podfile.lock 以及 manifest.lock 等生成 Analyzer 对象其内部会使用个 Molinillo 算法解析得到一张依赖关系表进行一系列的分析与依赖冲突解决。
3. 下载依赖文件download dependencies
def download_dependenciesUI.section Downloading dependencies do# 构造 Pod Source Installerinstall_pod_sources# 执行 podfile 定义的 pre install 的 hooksrun_podfile_pre_install_hooks# 根据配置清理 pod sources 信息主要是清理无用 platform 相关内容clean_pod_sourcesend
end经过前面分析与解决依赖冲突后这是会进行依赖下载。会根据依赖信息是否被新添加或者修改等信息进行下载同时下载后也会在本地留有一份缓存其目录在 /Library/Caches/CocoaPods 。
4. 验证 targetsvalidate targets
def validate_targetsvalidator Xcode::TargetValidator.new(aggregate_targets, pod_targets, installation_options)validator.validate!
enddef validate!verify_no_duplicate_framework_and_library_namesverify_no_static_framework_transitive_dependenciesverify_swift_pods_swift_versionverify_swift_pods_have_module_dependenciesverify_no_multiple_project_names if installation_options.generate_multiple_pod_projects?
endverify_no_duplicate_framework_and_library_names验证是否有重名的 framework / libraryverify_no_static_framework_transitive_dependencies验证动态库是否有静态链接库依赖。个人认为这个验证是不必要的起码不必要 error。verify_swift_pods_swift_version验证 Swift pod 的 Swift 版本配置且相互兼容verify_swift_pods_have_module_dependencies验证 Swift pod 是否支持 moduleverify_no_multiple_project_names验证没有重名的 project 名称
5. 生成工程Integrate
def integrategenerate_pods_projectif installation_options.integrate_targets?# 集成用户配置读取依赖项使用 xcconfig 来配置integrate_user_projectelseUI.section Skipping User Project Integrationend
enddef generate_pods_project# 创建 stage sanbox 用于保存安装前的沙盒状态以支持增量编译的对比stage_sandbox(sandbox, pod_targets)# 检查是否支持增量编译如果支持将返回 cache resultcache_analysis_result analyze_project_cache# 需要重新生成的 targetpod_targets_to_generate cache_analysis_result.pod_targets_to_generate# 需要重新生成的 aggregate targetaggregate_targets_to_generate cache_analysis_result.aggregate_targets_to_generate# 清理需要重新生成 target 的 header 和 pod foldersclean_sandbox(pod_targets_to_generate)# 生成 Pod Project组装 sandbox 中所有 Pod 的 path、build setting、源文件引用、静态库文件、资源文件等create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,cache_analysis_result.build_configurations, cache_analysis_result.project_object_version)# SandboxDirCleaner 用于清理增量 pod 安装中的无用 headers、target support files 目录SandboxDirCleaner.new(sandbox, pod_targets, aggregate_targets).clean!# 更新安装后的 cache 结果到目录 Pods/.project_cache 下update_project_cache(cache_analysis_result, target_installation_results)
end将之前版本仲裁的所有组件通过 project 文件的形式组织起来并对 project 中做一些用户指定的配置。
6. 写入依赖write lockfiles
def write_lockfileslockfile generate_lockfileUI.message - Writing Lockfile in #{UI.path config.lockfile_path} do# No need to invoke Sandbox#update_changed_file here since this logic already handles checking if the# contents of the file are the same.lockfile.write_to_disk(config.lockfile_path)endUI.message - Writing Manifest in #{UI.path sandbox.manifest_path} do# No need to invoke Sandbox#update_changed_file here since this logic already handles checking if the# contents of the file are the same.lockfile.write_to_disk(sandbox.manifest_path)end
end将依赖更新写入 Podfile.lock 与 Manifest.lock
7. 结束回调perform post install action
def perform_post_install_actions# 调用 HooksManager 执行每个插件的 post_install 方法 run_plugins_post_install_hooks# 打印过期 pod target 警告warn_for_deprecations# 如果 pod 配置了 script phases 脚本会主动输出一条提示消息warn_for_installed_script_phases# 警告移除的 master specs repo 的 specswarn_for_removing_git_master_specs_repo# 输出结束信息 Pod installation complete!print_post_install_message
end最后的收尾工作进行 post install action 的 hook 执行以及一些 warning 打印。
CocoaPods Plugins
早在 2013 年CocoaPods 就添加了对插件的支持以添加不符合依赖管理和生态系统增长为主要目标的功能。CocoaPods Plugins 可以在 install 前后添加 hook、添加新命令到 pod、以及利用 Ruby 动态性做任何事。下面介绍一下常见的插件
cocoapods-binary一个比较早期的二进制插件库是诸多二进制方案的灵感来源cocoapods-repo-update自动化 pod repo updatecocoapods-integrate-flutter将 flutter 与现有 iOS 应用程序集成cocoapods-uploader上传文件/目录到远程仓库
ps许多插件可能许久未维护读者使用需自行斟酌。
不太常见概念
CocoaPods 的配置内容几乎包含了 Xcode Build 的方方面面因此存在许多不太常见的概念在此做一个链接聚合以供参考。
Clang Module / module_map / umbrella headerClang Module 是 Clang 16.0.0 中引入的概念用以解决 #include / #import 头文件引入导致的相关问题module_map 是用以描述 clang module 与 header 的关系umbrella header 则是 module_map 中的语法规范表示指定目录中的头文件都应包含在模块中。
Modules
Clang Module
LLVM 中的 Module
Hmap / Xcode Header / CocoaPods Headers
Header Map 是一组头文件信息映射表用 .hmap 后缀表示整体结构以 Key-Value 形式存储Key为头文件名称、Value 为 头文件物理地址。
Xcode Phases - Header 在构建配置中分为 public、private 与 project 用以与 target 关联其中 public 、private 就复制到最终产物的 header 和 PrivateHeaders 中而 project 头文件不对外使用则不会放到最终产物。
一款可以让大型iOS工程编译速度提升50%的工具
What are build phases?
Xcconfig
一种配置文件用以对构建设置进行声明与管理比如区分不同的开发环境等。
Xcode Build Configuration Files
On demand resourceWWDC 2015 引入的概念对资源文件的按需加载。
Introducing On Demand Resources [1] Cocoapods.org
[2] Xcode Workspace with multiple projects
[3] 深入理解 CocoaPods
[4] 系统理解 iOS 库与框架
[5] Cocoapods script phases
[6] CocoaPods Podfile 解析原理
[7] Semantic Versioning 2.0.0
[8] 一款可以让大型iOS工程编译速度提升50%的工具
[9] CocoaPods Source 管理机制
[10] 版本管理工具及 Ruby 工具链环境
[11] 整体把握 CocoaPods 核心组件
[12] 工程效率优化CocoaPods优化
作者nihao1011 链接https://juejin.cn/post/7179231344147300412 来源稀土掘金 著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。