虽然国内实际开发用 Swift Package Manager(后称 SPM)的比较少,但是国外的一些开源库里却经常用到。所以经常遇到项目 clone 下来之后,SPM 加载失败导致项目无法运行的问题。

网上关于 SPM 加速的文章也有很多,方法也是五花八门,本文挑选几个进行记录。

根本问题

根本问题还是 Xcode 内部服务无法连接代理,导致访问 Github 经常超时。
所以解决方案还是要从代理入手。

解决方案

先列举一下本文记录的几种解决方案:

以上方法在我看来谈不上哪个更好。开发应当在合适的时候选择合适的方案。

路由器端直接设置代理

在路由器上直接设置代理,从网络源头解决问题。但是哪怕是在一些公司,也不太方便添加软路由等方法做这些操作。有条件的个人可以搞一下。

通过命令行直接拉取

参考 如何让swift package manager走代理

具体方法:

  1. 首先是打开终端,然后在终端内按照 “为终端设置代理” 的方法,设置 https_proxyhttp_proxyall_proxy

  2. 项目根目录下 执行下列命令:

xcodebuild -resolvePackageDependencies -scmProvider system

通过命令行直接拉取 SPM 依赖,执行完成后打开项目即可。

有的文章可能提到可以为 git config 设置代理,其命令为:

# 需要将端口替换成自己的端口
git config --global http.proxy 'http://127.0.0.1:1080'
git config --global https.proxy 'http://127.0.0.1:1080'

这里我没有进行尝试,有想法的读者可以自行尝试。

ClashX 增强模式

ClashX Pro 的增强模式可以穿透绝大多数软件,包括终端、Xcode。开启后直接使用 Xcode 即可下载依赖。

这个方案的缺点也比较明显:增强模式会导致一些内网服务不可用,所以建议仅在需要时临时打开,不建议常驻使用。所以你需要记得在 Fetch 成功后关闭增强模式。

如果好奇 ClashX 做了什么,可以参考:请问下增强模式是做了什么处理

代理 Xcode

如果你没有或者不想使用 ClashX,那么可以通过 Proxifier 进行拦截,然后再走代理。(不过虽然 Proxifier 提供了免费试用,但仍然算是收费那一档的)

有条件的伙伴可以查看 Xcode设置SPM代理 这篇文章,里面有详细的介绍。

设置 SPM Mirror

介绍和使用方法

最近偶然间发现,SPM 在 Swift 5 提供了一个 Mirror 镜像功能,提案在这里:Package Manager Dependency Mirroring

即如下命令:

swift package config set-mirror --original https://github.com/QMUI/LookinServer.git --mirror http://git.aaaaaa.top/LookinServer.git

--original 是原地址,而 --mirror 则是镜像仓库的 url。

所以有条件的通过可以在内网 Git 上 Fork Github Repo,或者在国内找寻现有镜像,然后通过 Mirror 下载依赖。

不过有一点需要注意:提案中有如下这么一句话:

The Package.resolved file will contain the mirror URLs that were used during dependency resolution.

但是在 Xcode 15 上实践后发现 Package.resolved 显示的还是 Original URL,不是 Mirror URL。

搜索后发现有这么一个 issue:SwiftPM: mirrored URLs end up in Package.resolved file。也就是说 Package.resolved 文件中显示 Original URL 在现在是正确行为。至于 Fetch 时使用的哪个 url,可以在 Report navigator 中查看具体的 log。

Package.swift

如果你开发的是一个 Swift 框架,要管理 Package.swift 中的依赖(使用该文件打开项目), 那么可以直接在项目根目录下执行上述命令。

xcodeproj

但是如果你是想为 xcodeproj 中添加的依赖设置 mirror,那么在项目根目录下使用 swift package config set-mirror 命令是会报错的:error: Could not find Package.swift in this directory or any of its parent directories.

通过报错可知该方法仅适用于 Package.swift 管理的项目。但是别着急。

在 Apple 文档 Adding package dependencies to your app 中有这么一小段描述:

You can find the Package.resolved file inside your .xcodeproj directory at [appName].xcodeproj/project.workspace/xcshareddata/swiftpm/Package.resolved.

通过这段描述我们可以知道,project.workspace 里面有一个 swiftpm 目录,进入后会发现该目录下就有一个 configuration 文件夹。

所以我们可以直接将 set-mirror 命令生成的 mirrors.json 文件放到该文件夹下,即可完成镜像设置。

注意:一般情况下我们的 .gitignore 文件配置会导致该路径不会被提交到 git 上。这点请您注意。

此时回到 Xcode 中添加依赖,就会发现速度 ... 好像没有变化。这就十分尴尬了...

通过进一步观察、查看 log 以及搜索相关资料。我进一步确定:mirror 这个配置仅适用Package.swift 软件包。所以如果我们直接在 Xcode 里通过一个 github 的 url 来添加依赖,那么它是不会走 mirror 配置的。但是该依赖所依赖的其他组件,又能走 mirror 配置,因为它是一个 Package.swift 软件包。

所以回到这小节讨论的问题上:你只能使用上面提到的其他方法,来解决在 Xcode 里向 project 添加依赖的问题。

我们要放弃吗?并不是,我想到了一个折中/取巧的方法来解决该问题。详见我在 stackoverflow 上的回答。

总结

让我们回到实际场景下来看看上述提到的几个解决方案:

  1. 你的 CI/CD 只能访问内部网络:
    不论你是在开发应用程序,还是你或你的公司在开发 SPM 软件包。在只能访问内部网络,完全无法访问 Github 的情况下,你只能通过 mirror 的方式来使用 SPM。

  2. 你的公司没有那么多要求,而且人均具备搭梯子的能力/你是具有搭梯子能力的个人开发者:
    那么其他几种方法都可以满足你的需求。