上周五下午,项目 SPM 又卡在 Resolve 上了。进度条纹丝不动,等了 20 分钟,还是那个转圈。
我做了和每次一样的操作:开全局代理 → Resolve 成功 → 发现内网 GitLab 拉不到了 → 关代理 → 下次又卡。
这个循环我重复了大半年。终于决定彻底解决它。
问题出在哪
三个坑叠在一起:
1. Xcode 不走系统代理
终端里 git clone 飞快,Xcode 里 Resolve Package 卡死。原因是 Xcode 有自己的网络栈,不读系统的 http_proxy 设置,也不走 VPN 分流规则。
2. 全局代理和内网冲突
为了让 Xcode 能访问 GitHub,你开了全局代理。然后公司内网的 GitLab、Jenkins、各种内部服务全挂了。你要么来回切,要么配复杂的分流规则。
3. Reset Package 就从头来
好不容易下载完了,手一抖点了 Reset Package Caches(或者 Xcode 自己抽风),所有依赖重新下载。你前面等的 20 分钟白等了。
解题思路
我回想了 CocoaPods 时代的做法:pod install 在终端跑,终端天然支持代理配置。下完的库就在 Pods/ 目录里,Xcode 直接引用本地文件,根本不需要网络。
SPM 能不能也这么干?
能。Xcode 的 Add Local Package 功能,就是引用一个本地目录作为 SPM 包。那我只需要:
- 在终端把三方库 clone 下来(终端可以走代理)
- Xcode 引用本地路径(不走网络)
这样 Xcode 永远不需要自己去 GitHub 拉代码。Reset Package 也无所谓,本地文件还在。
实现
整个方案就三个文件:
bash
Packages/
├── packages.json # 依赖清单
├── scripts/
│ └── fetch-packages.sh # 下载脚本
└── Caches/ # 三方库源码(gitignore)
依赖清单
packages.json 格式很简单,贴近 SPM 自己的 Package.resolved:
css
[ { "url": "https://github.com/SnapKit/SnapKit" }, { "url": "https://github.com/iAmMccc/SmartCodable.git", "version": "4.3.7" } ]
url 必填,version 可选。不填版本就拉默认分支最新代码,填了就 checkout 到对应 tag。
执行脚本
bash
./Packages/scripts/fetch-packages.sh
脚本做的事情:
- 读取
packages.json,从 URL 自动解析库名 - 没下载过的 →
git clone - 已存在且版本匹配 → 跳过
- 已存在但版本变了 →
fetch + checkout
脚本自动继承终端的 http_proxy / https_proxy,所以你终端能翻墙,脚本就能翻墙。
执行结果长这样:
css
[下载] SnapKit
[下载] SmartCodable 4.3.7
完成。下载: 2 / 更新: 0 / 跳过: 0 / 失败: 0
第二次跑:
css
[跳过] SnapKit
[跳过] SmartCodable 4.3.7
完成。下载: 0 / 更新: 0 / 跳过: 2 / 失败: 0
已经有的不会重复下载。
Xcode 引用
File → Add Package Dependencies → Add Local → 选 Packages/Caches/SnapKit。
每个库只需要添加一次。之后不管你 Reset 多少次,本地文件都在,Xcode 直接用。
日常工作流
| 场景 | 操作 |
|---|---|
| 新增依赖 | packages.json 加一条 → 跑脚本 → Xcode Add Local |
| 更新版本 | 改 version → 跑脚本 → Xcode Resolve |
| 新同事加入 | clone 项目 → 跑一次脚本 → 齐活 |
| Reset Package | 无影响,本地文件还在 |
一键安装
如果你想直接用,在项目根目录跑:
bash
curl -sL https://raw.githubusercontent.com/iAmMccc/ai-essentials/main/install-skill.sh | bash -s spm-local
会自动生成 Packages/ 目录结构、下载脚本、示例配置,并把 Packages/Caches/ 加进 .gitignore。
几个细节
Packages/Caches/不提交到 Git,三方库源码不应该进仓库- 如果某个库是另一个库的 SPM 依赖(比如你用的 A 库依赖了 B 库),B 也要一起本地化,否则 Xcode 还是会去网上找 B
- 脚本依赖
python3解析 JSON,macOS 自带,不需要额外安装
本质上就一句话:让终端替 Xcode 干网络的活。终端能走代理,下载一次就缓存到本地,Xcode 只负责引用本地文件。三个问题同时解决。