Swift 命令速查表 - Swift 常用命令与 iOS 开发大全
这套速查给用 Swift 交付 iOS/macOS 应用、命令行工具或服务端代码的开发者,横跨两条工具链:Swift / SwiftPM 的命令行,以及 xcodebuild / xcrun simctl。覆盖从脚本编译运行、初始化并构建 Swift 包、对某个 scheme 构建/测试/归档,到驱动模拟器,再加上 let/var、可选绑定、map/filter/reduce 等语法惯用法和断言调试。与一条条堆 Swift 命令不同,条目沿"编译→测试→归档→模拟器运行"的真实循环编排。读完应能把一个包从 swift package init 一路带到通过的 swift test,并在终端驱动整个 Xcode scheme。
典型使用场景
面向要在命令行或 Xcode 之外直接跑 Swift 的开发者,以及需要跨 iOS/macOS 复用同一份构建脚本的 CI 维护者。本表覆盖三条主线:swift 解释器与 swift package / swift build / swift test 的包工作流、xcodebuild 与 xcrun simctl 的无 UI 构建与模拟器操作、以及语言层面最容易出错的 optional 解包与集合高阶函数。适合「CI 上跑测试」「本地想快速验证一段算法」这类不需要打开完整 IDE 的场景。
编译与运行 6
swift main.swiftswiftc main.swift -o mainswift main.swiftcswift -e "print("Hello")"swift package init --type executableswift package init --type librarySwift Package Manager 8
swift buildswift build -c releaseswift runswift testswift package resolveswift package updateswift package resetswift package show-dependenciesXcode 命令行 7
xcodebuild -listxcodebuild -scheme MyApp buildxcodebuild -scheme MyApp testxcodebuild archive -scheme MyAppxcrun simctl list devicesxcrun simctl boot <device-id>xcrun simctl install booted MyApp.app常用语法 8
var name: String = "Swift"let age: Int = 18if let value = optional { ... }guard let value = optional else { return }array.map { $0 * 2 }array.filter { $0 > 5 }array.reduce(0) { $0 + $1 }for (index, value) in array.enumerated()调试与性能 5
print("debug: \(variable)")debugPrint(object)assert(condition, "message")precondition(condition, "message")Instruments参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
swift build -c release | 带优化的构建,交付与压测性能时用;调试期仍用默认 debug | swift build -c release |
guard let ... else | 可选解包 + 提前退出,把坏路径收敛在函数开头 | guard let value = optional else { return } |
xcodebuild -scheme MyApp test | 在无 UI 环境下跑该 scheme 的测试,适合 CI | xcodebuild -scheme MyApp test |
易错点与避坑指南
现象运行时报 fatal error: unexpectedly found nil。
原因对 optional 用了强制解包 !,而该值在某些分支确实为 nil。
处置用 if let value = optional { ... } 或 guard let value = optional else { return } 处理分支。
现象改了一行代码,swift build 却报告与改动无关的编译错误。
原因.build 增量缓存与源码状态不一致。
处置执行 swift package reset 清空缓存后重新 swift build。
现象依赖拉到最新版本后编译失败,CI 挂了但本地没事。
原因本地已有一份旧的 Package.resolved,CI 从零解析拿到更新版本。
处置先 swift package resolve 锁定版本,再把 Package.resolved 一起提交;确认无误后才 swift package update。
现象assert 的断言在 release 包里被跳过,问题上线后才暴露。
原因assert 只在 debug 配置生效,release 下被移除。
处置关键不变量改用 precondition(condition, "message"),它在所有模式下都会求值。
排障路径
1确认工程里有哪些 scheme 再选构建目标
xcodebuild -list先拿到 scheme 名,否则 -scheme 会直接失败并提示找不到目标。
2拿到模拟器 id 并安装构建产物做手工验证
xcrun simctl install booted MyApp.appid 来自 xcrun simctl list devices,装之前先让模拟器处于 booted 状态。
3确认依赖树与锁定版本是否符合预期
swift package show-dependencies以树形打印每个依赖的来源与版本,用来核对版本冲突和意外引入的间接依赖。
命令示例
跑一段脚本与编译成可执行文件
swift main.swift
swiftc main.swift -o main第一条直接解释执行脚本,第二条产出可执行文件;swift -e "print(...)" 可以只跑一行代码。
初始化可执行包并跑测试
swift package init --type executable
swift build
swift test产物落在 .build 目录;swift test 失败时返回非零退出码,CI 可据此拦截回归。
release 构建与依赖版本对齐
swift build -c release
swift package resolve
swift package show-dependencies全新 clone 后先 resolve,再靠 show-dependencies 看清每个包的来源与锁定版本。
可选解包与集合高阶函数的常见组合
if let value = optional {
print(value)
}
array.map { $0 * 2 }
array.filter { $0 > 5 }
array.reduce(0) { $0 + $1 }三个高阶函数都返回新值、不改原数组;map 做模型转换,filter 筛出满足条件的元素,reduce 从初值累积。
常见坑与注意事项
- assert 只在 debug 模式生效,release 构建里不会执行;需要所有模式都拦下来的前置条件请用 precondition。
- 给可选类型强解包(!)在值为 nil 时直接触发运行期崩溃,坏路径请交给 if let 或 guard let 处理。
- swift build -c release 打开优化后编译明显变慢,调试期用默认 debug 配置,别把两者混在同一个循环里。
- swift package reset 会清空 .build 做全量重建,下次构建耗时大增,只在增量编译报错与改动无关时使用。
- xcrun simctl 的设备 id 只能从 xcrun simctl list devices 的输出里取;boot 之前也必须先确认目标设备处于可用状态。
提示
- swift package init 生成的 Package.swift 是 SPM 的配置文件。
- 可选类型 ? 是 Swift 空安全的核心,尽量用 if let / guard let 解包。
- xcodebuild 命令行适合 CI/CD 自动化构建和测试。
常见问题
swift run 和先 swift build 再手动跑产物有什么区别?
swift run 内部就是先按 Package.swift 编译再执行可执行 target 的产物,差别只在于省掉了你手动找 .build 目录下产物路径这一步。调试期想在断点前后插入 swift build 单独看编译结果时,两步走更清楚;CI 里只想确认产物能不能跑起来,用 swift run 一条命令就够。另外 swift run 只会作用于可执行 target,库包执行它没有意义。
为什么本地能编过,swift test 却在 CI 上失败?
最常见的原因是依赖版本没锁死。本地已经有 .build 缓存和已解析的依赖,而 CI 是全新 clone,必须先 swift package resolve 把依赖拉到 Package.resolved 锁定的版本;只有这个文件是最新且被提交进版本库时,两边才会一致。如果依赖的 Package.swift 在 CI 上被更新过,就要用 swift package show-dependencies 打印依赖树,逐个确认每个包的来源与版本是否符合预期,再决定是提交 Package.resolved 还是回退约束。
if let 和 guard let 到底该怎么选?
两者的差别是「解包失败之后要不要离开当前作用域」。if let value = optional 后面跟代码块,解包失败就整块跳过,控制流保持在一个分支里;guard let value = optional else { return } 解包失败时立即 return(循环里是 continue),因此它之后的代码里 value 已经是解包后的非可选值,不用再写问号。实践上把 guard 放在函数或循环开头集中处理坏路径,剩下主体就能当作非可选值直接写。
swift package reset 什么时候值得用?
它清空 .build 缓存并重新做一次完整构建,属于「前面的手段都试过了」这一档的处置。适用信号是增量编译报出与刚改动的代码无关的错误,或者改了一行却看到明显陈旧的报错信息。代价是下一次 swift build 要全量编译,耗时明显变长,所以不要当成日常操作的默认一步,先确认错误确实指向缓存而不是依赖或代码本身。
模拟器上验证一个构建产物需要哪几步?
顺序是固定的四步:先用 xcodebuild -list 确认 scheme 名字,用它做 xcodebuild -scheme MyApp build 产出 .app;再用 xcrun simctl list devices 拿到目标设备的 UDID 并 xcrun simctl boot 该 id;接着 xcrun simctl install booted MyApp.app 把产物装进已启动的模拟器;最后手动点验。UDID 只能从 simctl list 的输出里取,猜一个 id 一定会失败。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。