Swift Package Manager(SPM)是 Swift 官方的构建与依赖管理工具。本文面向 iOS/Swift 开发者入门与进阶,把「创建包 / Xcode 集成 / 添加依赖 / 资源打包 / 命令行工具 / 常见坑」串成一条可直接照做的路径。
TL;DR(最常用命令)
1 | # 创建可执行包(命令行工具) |
1. 新建一个可执行 Package(命令行工具)
1 | mkdir hello && cd hello |
初始化后,默认结构通常是:
1 | hello/ |
你可以直接运行:
1 | swift run |
2. 先搞清楚:Target、Product、Dependency 是什么
- Dependency:你依赖的“外部包”(例如 Alamofire 的 Git 仓库)
- Target:你自己包里的“模块”(源码组织单位,能被其他 target 依赖)
- Product:对外暴露出来可被别人使用的产物(library / executable),通常由 target 组合而来
常见误区是把“外部依赖包名”当作可以直接写进 dependencies: [...] 的名字。更稳妥的方式是 依赖它的 product:
1 | .product(name: "Alamofire", package: "Alamofire") |
3. 用 Xcode 打开与运行(推荐方式)
现在一般不需要生成 .xcodeproj:直接用 Xcode 打开 Package.swift 即可 Run / Debug(或在目录里执行 xed .)。
旧命令
swift package generate-xcodeproj已经逐渐退出主流工作流;建议直接用 Xcode 打开 package。
4. 在 Xcode 工程里添加 SPM 依赖(两条路)
4.1 通过 Xcode UI 添加(最常见)
两种入口等价(也支持本地 package,选择时点 Add Local):
- Xcode → File → Add Packages…
- Project Settings → Project →
Package Dependencies→ “+”
4.2 把依赖写回 Package.swift(更可控)
当你的工程本身也是一个 package(或你在维护一个库)时,把依赖写进 Package.swift 更容易 review 与回滚。
5. Package.swift:一个“可复制粘贴就能跑”的示例
下面示例匹配默认目录结构(Sources/<TargetName>/...),并演示:
- 一个可执行 target:
Hello - 一个内部模块:
Core - 一个外部依赖:
Alamofire(用.product(...)引入)
1 | // swift-tools-version: 5.8 |
对应的建议目录结构:
1 | Sources/ |
如果你要做的是 iOS App/Framework 的依赖包,通常会把 .executableTarget 换成 .target / .library,并把 platforms 改成 .iOS(.vXX)(或同时支持 iOS/macOS)。
6. 给 Swift Package 添加资源(Resources)与 Bundle.module
SwiftPM 支持把资源打进 bundle(图片、json 等)。典型做法是把资源放进某个目录(例如 Sources/Core/Resources),然后在 target 配置 resources:
1 | .target( |
读取资源时用 Bundle.module(只在 package 内可用):
1 | import Foundation |
参考:https://useyourloaf.com/blog/add-resources-to-swift-packages/
7. 常见问题与排错(按优先级)
7.1 Xcode/命令行拉依赖很慢或失败(代理/网络)
某些网络环境下 Xcode 拉取 GitHub 依赖不稳定,可以先用命令行把依赖 resolve 掉:
1 | # 按你本机代理端口调整(常见还有 http_proxy / all_proxy) |
如果 SPM 依赖是“挂在某个 xcode project”上,可以指定 project + scheme:
1 | xcodebuild -resolvePackageDependencies -project Kickstarter.xcodeproj -scheme Kickstarter-iOS |
如果你的目的是让 xcodebuild -resolvePackageDependencies 走代理,更可靠的方式通常是给“这一次命令”设置代理环境变量(避免污染全局 git 配置):
1 | # HTTP 代理(按你本机代理端口调整) |
另外在部分环境下,-scmProvider system 会更稳定(强制使用系统的 SCM provider):
1 | xcodebuild -resolvePackageDependencies -scmProvider system |
7.2 xcodebuild 找不到 Xcode(CommandLineTools 被选中)
报错类似:
1 | xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance |
执行:
1 | sudo xcode-select -s /Applications/Xcode.app/Contents/Developer |
7.3 Package.resolved schema/version 不兼容
当 Xcode 与 SwiftPM 的 Package.resolved 格式不兼容时,最简单的处理通常是 删除并重新解析。
常见位置:
- 在 package 仓库里:
.swiftpm/Package.resolved(或项目根目录的Package.resolved,取决于工具链/项目类型) - 在 Xcode 工程/工作区里:
Project.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
删除后再执行:
1 | swift package resolve |
7.4 Realm 产品名冲突(multiple products named ‘Realm’ / ‘RealmSwift’)
报错类似:
1 | multiple products named 'Realm' in: 'realm-cocoa', 'realm-swift' |
常见原因是 Realm 仓库地址调整导致的重复依赖。把旧 URL 改为新仓库即可:
1 | // 旧: |
8. “更新依赖”与清缓存(谨慎使用)
命令行没有一个“万能的一键更新所有依赖”的命令;通常是“更新版本约束 + 重新 resolve”。当你遇到缓存/checkout 异常,才考虑清缓存。
建议先从轻到重:
- 在 Xcode 里 Reset Package Caches(通常在
File → Packages → Reset Package Caches) - 删除
Package.resolved并重新 resolve - 最后再清理 DerivedData 与 SwiftPM 缓存(会比较重)
一个“重置到能重新拉依赖”的命令集:
1 | rm -rf ~/Library/Developer/Xcode/DerivedData/ \ |
9. 命令行工具 Keep Alive(避免异步任务没跑完就退出)
命令行程序跑到 main.swift 末尾就会退出,异步任务可能还没完成。常见做法是在末尾 keep alive:
1 | RunLoop.main.run() // 或 dispatchMain() |
10. 常用路径速查(理解“依赖到底存哪儿了”)
- SwiftPM 缓存(git repo 存储):
~/Library/Caches/org.swift.swiftpm/ - Xcode DerivedData 下依赖 checkout:
~/Library/Developer/Xcode/DerivedData/ProjectName-xxxx/SourcePackages
参考链接(延伸阅读)
- Package Collections:
https://swift.org/blog/package-collections/ - Apple 文档:
https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app - Guide:
https://www.raywenderlich.com/1993018-an-introduction-to-swift-package-managerhttps://www.swiftbysundell.com/articles/building-a-command-line-tool-using-the-swift-package-manager/https://www.fivestars.blog/articles/ultimate-guide-swift-executables/https://stackoverflow.com/questions/31944011/how-to-prevent-a-command-line-tool-from-exiting-before-asynchronous-operation-cohttps://forums.swift.org/t/xcode-environment-variables/45249https://appcoda.com.tw/swift-package-manager/