症状: 新项目不知道该默认选哪种依赖管理方式,存量项目又担心迁移影响发版。
最快解法: 新建项目优先评估 Swift Package Manager;仍依赖 Pod、复杂脚本或特殊二进制集成的项目先保留 CocoaPods,混合项目采用可回退的双轨迁移。

01

适用人群与阅读重点

这篇文章适合准备创建原生 Swift iOS / macOS 项目的独立开发者,尤其是需要确定默认依赖管理方案的人。

如果你正在维护包含 CocoaPods、私有组件或二进制 SDK 的存量 App,也可以用本文判断迁移是否值得。需要在远程 Mac 或持续集成环境中稳定恢复依赖并完成 Archive 的小型团队,同样适用。

02

CocoaPods vs Swift Package Manager:先按项目现状做选择

正确比较这两种工具,不是看哪一个“更现代”,而是看你的项目能否从干净环境稳定恢复,并继续完成测试、签名和 Archive。

项目现状 默认建议 需要先确认的条件 暂停或回退信号
新建原生 Swift 项目,依赖主要提供 Package 优先评估 Swift Package Manager Package 是否支持目标平台、资源和二进制 Target 关键 SDK 无法编译或资源丢失
已稳定发布的 CocoaPods 项目 先保留 CocoaPods Podfile、Podfile.lock、Workspace 和脚本是否已固化 迁移同时影响多个 Target 或发布周期紧张
私有源码组件 按认证和交付方式选择 Git 访问、版本标签、CI 凭据是否可用 干净主机无法拉取依赖
XCFramework 或带资源 SDK 先看提供方正式文档 Package 或 Pod 的二进制、资源和签名说明 下载成功但链接、资源或 Archive 失败
两种依赖混合 双轨验证,再逐项替换 是否会重复引入同一库或传递依赖 构建图出现重复符号、版本冲突

Swift Package Manager 会解析依赖图,并将解析结果记录到 Package.resolved。在 Xcode 项目中,该文件通常位于 .xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved,而不是项目根目录的普通配置文件。(Swift Package Manager 官方文档)

CocoaPods 则围绕 Podfile 解析依赖,并生成或使用 Xcode Workspace。官方指南要求日常打开生成的 .xcworkspace,而不是只打开原始 .xcodeproj。(CocoaPods 安装与使用指南)

这两个细节直接决定了 CI 恢复方式。一个项目即使在本地“能编译”,只要提交错了锁定文件、打开错了工程,远程机器仍可能得到完全不同的依赖状态。

03

新项目优先评估 Swift Package Manager 的条件

新建项目时,Swift Package Manager 的主要价值不是一句“Xcode 原生支持”就能概括,而是依赖声明、解析、锁定和工程集成可以放进更接近 Xcode 项目本身的流程里。

Apple 文档确认,Xcode 可以添加、移除和管理 Swift Package,也支持源码、资源和二进制形式的 Package。Swift 官方文档则说明,依赖解析会根据版本要求计算结果,并通过 Package.resolved 记录精确版本。(Apple 的 Swift Packages 文档)

对新项目来说,这通常意味着:

  • ✅ 不必一开始就引入 Ruby、Gem 和 CocoaPods 安装链路。
  • ✅ 依赖版本可以通过 Package.resolved 纳入代码审查。
  • ✅ 本地 Package、远程 Package 和部分二进制交付方式可以放进同一套 Xcode 工作流。
  • ✅ 新建 Target 时,依赖关系更容易和项目结构一起检查。

但“优先评估”不等于“直接添加所有 Package”。你需要先检查依赖提供方是否真的支持当前项目需求:

  • 是否包含 iOS 和 macOS 目标需要的正确平台声明。
  • 是否提供资源,并且资源 Bundle 能被应用正确加载。
  • 是否使用二进制 Target、XCFramework 或宏,且与你当前工具链兼容。
  • 是否需要私有仓库认证。
  • 是否要求额外的脚本、环境变量或构建阶段配置。

“支持 Swift”只能说明代码使用 Swift,不能证明它已经提供可用的 Swift Package。最终判断应以依赖提供方的正式文档、Package manifest、测试项目和你的 Archive 结果为准。

场景案例:新 App 只有少量公开依赖

假设你准备创建一个新的原生 App,主要依赖是网络层、图片加载和内部 Swift 模块。你不需要先搭建 CocoaPods 的 Ruby 环境,也没有旧 Workspace 或大量 post_install 修改。

此时可以先用 Swift Package Manager 创建最小项目,提交 Package.resolved,再在干净的 Mac 环境中执行完整构建。只要依赖下载、源码编译、单元测试和 Archive 都成功,就没有必要为了兼容旧习惯主动引入 CocoaPods。

04

成熟 CocoaPods 项目以发版稳定性为先

如果一个项目已经连续发版,并且 CocoaPods 链路包含多个 Target、私有 Spec Repo、脚本钩子和定制 Build Settings,那么迁移本身就是一次工程变更,不是简单替换几行配置。

CocoaPods 官方文档说明,Podfile.lock 会记录已安装的 Pod 版本;使用 pod install 时,已锁定的依赖不会因为仓库出现更新版本就自动改变。相反,pod update 会忽略锁定版本,重新寻找符合约束的更新版本。(CocoaPods 的 install 与 update 说明)

只要下面任一条件成立,就应先把发版稳定性放在迁移目标之前:

  • 关键 SDK 只提供 Pod,没有等价的 Swift Package。
  • 一个 Pod 同时被多个 App Target、Extension 或测试 Target 使用。
  • Podfile 中存在 post_install、自定义 Build Settings 或资源复制逻辑。
  • 项目依赖私有 Spec Repo,且远程构建已经稳定运行。
  • 当前正处于紧急修复、审核提交或版本冻结阶段。
  • 团队没有时间为每个 Target 重新验证签名、测试和 Archive。

CocoaPods 的安装链路还可能依赖 Ruby 环境。官方入门文档建议不要把系统 Ruby 当成长期项目环境,并给出独立 Ruby、Bundler 等管理方式。

这并不意味着 CocoaPods 一定更差,而是说明它的稳定性可能已经沉淀在现有脚本和工程结构中。迁移后如果只是减少了一个工具,却增加了多个 Target 的验证工作,你得到的不是简化,而是新的发布风险。

⚠️ 注意:不要把“依赖安装成功”当成迁移完成。源码下载成功、依赖解析成功、项目编译成功、测试通过和 Archive 成功,是五个不同的验收层级。

05

私有依赖与二进制 SDK 的判断标准

私有源码、内部组件、XCFramework 和带资源的第三方 SDK,不能用同一个标准判断。你需要先看“依赖如何交付”,再看“工具是否方便”。

依赖类型 Swift Package Manager 需要确认 CocoaPods 需要确认 更适合优先保留的情况
私有源码仓库 Git URL、标签、认证和 CI SSH 凭据 私有 Spec Repo、来源地址和访问权限 现有 Pod 私有仓库已经稳定
内部 Swift 组件 Package.swift、产品名和目标平台 Podspec、源码路径和模块名 内部组件已有成熟 Podspec
XCFramework Binary Target、校验值、平台切片和资源 Vendored Framework、脚本和签名处理 提供方只维护 Pod 交付方式
带资源 SDK Resource Bundle、资源访问 API resourcesresource_bundles 配置 旧项目已有可靠资源复制流程
需要安装脚本的 SDK 是否需要额外脚本或环境变量 script_phasepost_install 和 Build Phase 脚本行为是 SDK 正常工作的必要条件

Apple 的 Swift Package 文档覆盖了资源、本地 Package 和包含 XCFramework 的二进制 Package,但是否能用于你的项目,仍取决于具体 Package 的声明和交付质量。

如果使用 CocoaPods 管理私有组件,官方指南建议建立私有 Spec Repo,并让团队成员都能访问该仓库;Podspec 还需要经过校验和版本管理。(CocoaPods 私有 Spec Repo 指南)

远程构建时,私有依赖的关键不是“本地能下载”,而是非交互环境能否下载。Apple 针对 CI 的说明指出,私有 Swift Package 需要额外提供访问凭据;如果直接使用 xcodebuild,可以配置 SSH 方式访问私有 Package。(Apple 的 CI 依赖配置说明)

因此,不要把“这个 SDK 是 Swift 写的”当成迁移依据。你真正要问的是:它有没有正式 Package、二进制是否完整、资源是否可用、认证是否支持 CI,以及提供方是否说明了当前集成方式。

06

第一步:建立可回退的双轨迁移批次

一个 Xcode 项目可以同时使用 CocoaPods 和 Swift Package Manager,但你必须把并存当成过渡状态,而不是长期无边界叠加。

迁移时建议按以下顺序处理:

  1. 盘点依赖。
    列出每个库的来源、版本、Target、资源、二进制、私有仓库和安装脚本。不要只看 Podfile,还要检查项目文件、Build Phases 和自定义脚本。

  2. 标记重复风险。
    如果同一个库同时通过 Pod 和 Package 引入,先停止迁移。重复模块可能导致重复符号、不同版本同时存在,或资源 Bundle 被复制两次。

  3. 从边缘组件开始。
    先迁移不参与核心启动流程、支付、推送、登录或数据迁移的组件。边缘组件失败时,回退成本更低。

  4. 锁定版本并提交文件。
    Swift Package Manager 项目提交 Package.resolved;CocoaPods 项目提交 Podfile.lock。CocoaPods 官方明确建议提交 Podfile.lock,否则团队成员可能解析出不同的传递依赖版本。

  5. 逐批验证工程。
    每批迁移后,分别执行依赖解析、普通 Debug 构建、Release 构建、测试和 Archive。不要把所有迁移改动集中到一个提交中。

  6. 保留旧方案分支。
    旧的 Podfile、Workspace 和锁定文件不要立即删除。至少等新方案完成一次真实发布链路,再决定是否清理。

  7. 记录停止条件。
    如果某个关键 SDK 无法完成资源加载、私有认证或 Archive,就暂时回退,不要为了追求全 Package 化继续扩大影响范围。

Swift Package Manager 可以使用仅采用已解析版本的模式;Swift 官方命令文档也提供了在解析文件过期时失败的选项。(Swift Package Manager 解析命令文档)

迁移的目标不是让依赖管理工具数量变成一个,而是让每次提交都能在另一台 Mac 上复现。

07

远程 Mac 与 CI 的验收标准

如果你的项目需要远程 Mac 作为 iOS 打包服务器,验收标准应从“能否打开项目”提高到“能否从空白环境稳定完成 Archive”。

Apple 的 CI 文档明确建议将 Package.resolved 提交到代码仓库;直接使用 xcodebuild 时,还可以传入 -disableAutomaticPackageResolution,避免 CI 在没有明确授权的情况下重新解析依赖。

远程 Mac 验收可以按这个顺序执行:

  • [ ] 创建或选择一台没有项目缓存的干净 Mac 环境。
  • [ ] 安装项目要求的 Xcode,并记录实际版本。
  • [ ] 固定 Ruby、Bundler 和 CocoaPods 版本;不要依赖机器默认环境。
  • [ ] 拉取代码、Package.resolvedPodfile.lock 和共享 Scheme。
  • [ ] 配置私有仓库 SSH Key、证书和必要的环境变量。
  • [ ] 执行 Swift Package 依赖恢复,确认使用锁定文件。
  • [ ] 执行 CocoaPods pod install,不要在普通恢复流程中使用无目标的 pod update
  • [ ] 检查项目是否打开正确的 .xcworkspace.xcodeproj
  • [ ] 完成普通构建、测试、Release 构建和 Archive。
  • [ ] 清理缓存后再次恢复,确认流程不依赖本地残留文件。
  • [ ] 重启或更换远程主机后重复关键步骤。
  • [ ] 保存失败日志、依赖版本、认证错误和回退命令。

CocoaPods 的命令参考显示,pod install 会下载 Podfile 中声明的依赖,并创建 Pods 工程;--deployment 参数可以禁止安装时修改 PodfilePodfile.lock。(CocoaPods 命令参考)

如果你需要临时搭建独立 macOS 环境来验证迁移,可以参考 KVMNODE 的 Mac 远程租赁方案,把依赖恢复和 Archive 验收从正在发版的本地环境中隔离出来。

五个必须分开的验收结论

依赖解析成功: 工具找到了满足约束的版本。
依赖下载成功: 源码、二进制和资源能够从仓库取得。
项目编译成功: 模块、链接方式和 Build Settings 没有阻塞编译。
测试通过: 运行时行为和关键功能没有明显回归。
Archive 成功: 签名、资源、Release 配置和发布产物完整。

只有最后一项成功,才说明方案具备接近发布流程的可用性。

08

最终决策卡:保留、迁移还是双轨?

你可以在项目评审会上直接使用下面的决策卡。

继续使用 CocoaPods

满足以下情况时,继续使用通常更稳:

  • 关键依赖没有可验证的 Swift Package。
  • Podfile 中存在多个必要的安装脚本。
  • 多个 Target 共用成熟的 Workspace 集成方式。
  • 当前项目临近发版,无法安排完整回归。
  • 私有 Spec Repo 和远程凭据已经稳定运行。

停止条件: CocoaPods 安装链路开始频繁受 Ruby、脚本或 Workspace 变化影响,并且关键依赖已经提供经过验证的 Package。

迁移到 Swift Package Manager

满足以下情况时,可以优先建立迁移分支:

  • 新项目没有历史 CocoaPods 约束。
  • 主要依赖已经提供正式 Package。
  • 依赖数量和 Target 数量可控。
  • 没有复杂的 post_install 或自定义复制逻辑。
  • 远程 CI 可以访问公开或私有 Package。
  • 已经完成干净环境 Archive。

停止条件: 关键二进制、资源、认证或签名问题无法在迁移窗口内解决。

暂时采用双轨

以下情况适合双轨:

  • 新组件支持 Swift Package Manager,旧核心组件仍依赖 CocoaPods。
  • 你需要逐步替换边缘依赖。
  • 项目同时包含公开 Package、私有源码和特殊二进制 SDK。
  • 团队需要保持旧发布流程可回退。

停止条件: 同一库出现重复引入、传递依赖冲突,或团队无法明确哪一个锁定文件代表生产版本。

如果你还在评估远程构建环境,可以参考 远程 Mac 使用入口,先把依赖恢复和 Archive 验收从本地开发环境隔离出来。

09

常见问题

新建原生项目时,CocoaPods 还有必要保留吗?

新项目不应默认排除 CocoaPods,但如果主要依赖都提供可用的 Swift Package,建议先评估 Swift Package Manager。CocoaPods 仍然适合只提供 Pod、依赖复杂脚本或需要特殊 Workspace 集成的 SDK。判断标准应是完整发布链路,而不是工具名称。

Swift Package Manager 能不能覆盖所有 CocoaPods 使用场景?

不能直接这样判断。Swift Package Manager 支持源码、资源和部分二进制依赖,但不同 SDK 的 Package 配置、认证方式和资源交付质量差异很大。只要关键依赖无法稳定完成编译、测试和 Archive,就不能把迁移称为成功。

存量项目迁移到 Swift Package Manager,收益如何评估?

如果项目是新建、依赖较少且没有复杂脚本,迁移更容易控制。成熟项目则要把多个 Target、私有组件、二进制 SDK、资源 Bundle 和发版周期纳入成本。先迁移低风险组件,再用真实 Archive 结果决定是否扩大范围。

两种依赖管理方式在同一个 Xcode 项目中并存安全吗?

可以并存,但必须避免同一库被两次引入。迁移期间应明确每个组件由哪种工具管理,并检查模块、传递依赖、资源和链接方式。双轨方案应该有批次、分支和回退路径,而不是让两套配置长期无边界叠加。

远程 Mac 上怎样固定 iOS 依赖版本?

提交 Package.resolvedPodfile.lock,固定 Xcode、Ruby、Bundler、CocoaPods 和认证配置。在干净环境中完成依赖恢复、构建、测试和 Archive,再清理缓存重复验证。远程 Mac 的价值在于暴露本地缓存和隐式配置问题,而不是只提供一台能运行 Xcode 的主机。

完成方案选择后,下一步应在一台干净的远程 Mac 上验证依赖恢复、测试和 Archive,而不是立即删除旧配置。对于不希望改动现有本地环境的迁移测试,独立 macOS 环境可以把失败影响限制在测试分支内;如果项目长期重度构建、必须连接本地设备或依赖物理接口,自购 Mac 可能更合适。

相反,当前方案若依赖本地缓存、Ruby 环境漂移、共享密钥或一台长期占用的开发机,就很难稳定复现。临时租用 KVMNODE 的 Mac 环境,可以让你按项目周期完成隔离验证,再决定是否切换生产流程。