症状:升级 Swift 6.3 后,编译器突然出现大量并发、依赖或多 Target 报错。
最快解法:不要立即把所有 Target 切到 Swift 6 language mode;先固定基线,再按 Target 分阶段启用诊断,保留旧语言模式与新编译器并行验证。
这篇文章适合 3 类人:正在处理 Swift Concurrency 诊断的 iOS 与 macOS 开发者;维护多模块、混合语言或旧依赖项目的技术负责人;需要在 CI 中验证 Swift 6.3 迁移结果的构建工程师。
最后更新于 2026 年 8 月 24 日,规则与版本信息核实自 Swift 6.3 官方发布说明、Swift 官方迁移指南 与 Apple Developer 文档。
先把编译器版本和语言模式拆开
Swift 6.3 已于 2026 年 3 月 24 日正式发布,但“使用 Swift 6.3 编译器”与“启用 Swift 6 语言模式”是两个独立决定。Swift 官方发布说明确认的是工具链版本;迁移指南则明确说明,Swift 6 编译器可以编译 Swift 6、Swift 5、Swift 4.2 和 Swift 4 等不同语言模式。
这意味着,一个旧项目可以先换用 Swift 6.3 编译器,却继续让部分 Target 使用 Swift 5 模式。只有当某个 Target 的 SWIFT_VERSION 或对应构建设置切换到 Swift 6,严格的数据竞争诊断才会以 Swift 6 语言规则参与编译。
| 检查对象 | 它决定什么 | 迁移时的常见误判 |
|---|---|---|
| Swift 编译器版本 | 使用哪套编译器、标准库和诊断能力 | 以为换编译器就会全项目切换语言规则 |
Swift language mode |
当前 Target 遵循 Swift 5 还是 Swift 6 的语言规则 | 只检查 Xcode 版本,没有逐 Target 记录 |
| 严格并发检查 | 在旧语言模式下提前暴露部分 Sendable 与隔离问题 | 把警告数量直接当成真实数据竞争数量 |
| Package 工具链与依赖版本 | 依赖能否被当前编译器和接口规则正确构建 | 认为主工程能编译,所有依赖就自然兼容 |
在动代码之前,先导出每个 Target 的语言模式、并发检查级别、部署设置、依赖锁定文件和当前测试结果。你要建立的是“迁移前快照”,而不是凭记忆判断哪些模块已经完成。
第一步:建立可回滚的迁移基线
先从稳定分支复制一条迁移分支,并记录以下内容:
- 每个 App、Framework、Unit Test、UI Test Target 的 Swift language mode。
- 当前编译警告、测试失败、归档失败和签名失败。
Package.resolved、子模块提交点、生成代码版本和构建脚本版本。- Debug、Release、Archive 3 种构建方式是否都能完成。
- 关键异步流程的测试基线,例如登录、网络请求、数据库写入、后台任务和推送处理。
不要只运行一次本地 Debug 构建。迁移后的问题可能只在 Release 优化、模拟器与真机差异、归档签名或特定测试并发顺序下出现。Apple 的 分发签名代码流程也把 Archive 与 exportArchive 分成独立步骤,因此 CI 不能只验证“能否编译”。
第二张表可以作为初始分批依据:
| Target 类型 | 建议迁移顺序 | 进入 Swift 6 模式前的最低条件 |
|---|---|---|
| 工具类或纯 Swift 小模块 | 第一批 | 依赖少、接口清晰、单元测试覆盖关键路径 |
| 独立 Framework | 第二批 | 对外 API 已标明 Actor、Sendable 或明确的同步边界 |
| 混合 Swift / Objective-C 模块 | 第三批 | 桥接头、Block、代理回调和错误类型完成跨语言验证 |
| 核心业务模块 | 后续批次 | 并发测试稳定,依赖升级方案已锁定 |
| App 主 Target | 最后批次之一 | 所有嵌入 Framework、测试 Target 和归档流程均已验证 |
大型项目可以利用 Swift 语言模式之间的模块互操作性,先迁移一个 Framework,再逐步向上层推进。Swift 版本兼容文档明确说明,新旧语言模式的 Target 可以互相依赖,这正是分阶段迁移的基础。
第二步:把并发诊断按风险分类
“报错很多”不等于“需要改很多处注解”。你应先根据诊断触发的位置分类,而不是按照错误文本逐条机械消除。
共享可变状态
如果一个普通 class、全局变量、缓存或单例同时被多个任务访问,先判断它到底需要哪一种保护:
- 只能由 UI 访问的状态,通常应明确放入
@MainActor。 - 需要跨任务安全访问的可变状态,可以考虑使用独立
actor。 - 只读配置、值类型数据或复制后使用的结果,优先保持值语义,而不是增加不安全标注。
- 需要跨隔离域传递的类型,检查其成员是否真的满足
Sendable。
Actor 的作用不是“让报错消失”,而是把可变状态的访问边界表达给编译器。Apple 对 Actor 的说明指出,Actor 类型本身可以安全地跨并发域共享,但其内部状态仍必须通过隔离规则访问。
Sendable 诊断
当错误出现在任务闭包、Actor 调用、异步函数参数或返回值附近时,重点检查是否有引用类型越过并发边界。一个 class 即使暂时没有出现崩溃,也不代表它可以安全地标记为 Sendable。
你可以按这个顺序处理:
- 先把可变 class 改成不可变值类型,或只传递必要的数据副本。
- 如果状态确实需要集中管理,改成 Actor 或由明确的全局 Actor 隔离。
- 如果是第三方类型,先查官方仓库是否已有并发兼容版本。
- 只有在你能证明类型内部已有同步机制时,才审慎考虑
@unchecked Sendable。 - 不要为了清零诊断而大面积使用
nonisolated、@preconcurrency或不安全 Sendable 标记。
Swift 官方的 并发迁移指南建议采用渐进方式,并提醒 MainActor.run 不能替代对隔离需求的静态表达。换句话说,临时把代码包进主 Actor,可能隐藏边界设计问题,却不会自动修复数据竞争。
异步边界与混合语言接口
Objective-C Delegate、Completion Handler、C 回调和系统通知通常是迁移中最容易扩散的边界。你需要逐个确认:
- 回调在哪个 Actor 上执行。
- 闭包是否会逃逸并被并发调用。
- 传入的对象是值类型、不可变对象,还是可变引用类型。
- Swift 方法标注是否会改变 Objective-C 暴露的签名。
- C 指针、缓冲区和生命周期是否跨越异步边界。
Apple 在 Swift 并发专题视频中强调,隔离不仅涉及线程位置,还涉及数据是否在不同并发域之间安全传递。你要验证的是“谁拥有状态、谁能修改状态、何时发生转移”,而不是简单地把所有 API 放到后台任务中。
常见迁移问答
并发诊断突然成批出现,排查顺序应该怎样安排?
先回到基线,确认是编译器切换、语言模式切换,还是严格并发检查级别变化导致诊断增加。随后按共享可变状态、Actor 隔离、Sendable 和异步回调分类,只处理真实的跨并发域风险;不要批量添加不安全标注来换取暂时的绿色构建。
编译器版本与 Target 的语言规则分别负责什么?
编译器版本是工具链层面的选择,语言模式是 Target 遵循哪套语言规则的选择。Swift 6.3 编译器可以构建 Swift 5 模式代码,因此你可以先升级工具链,再按模块启用 Swift 6 language mode,避免把全项目迁移变成一次性切换。
多模块工程怎样安排迁移批次,才能不拖垮主分支?
从依赖少、测试完整、对外接口少的 Target 开始,然后迁移共享 Framework,最后处理核心业务和 App 主 Target。每完成一批,都要验证 Swift、Objective-C、C 边界、测试、Archive 和签名,不要只以单个 Scheme 的 Debug 编译成功作为完成标志。
旧版第三方库无法配合新工具链时,应该采取哪些方案?
先查询依赖官方仓库是否有兼容版本或源码修复,再决定升级、锁定旧版本、替换实现或建立隔离适配层。临时修改应限制在边界模块内,并把依赖版本写入可审查的锁定文件,避免不同开发机解析出不同结果。
迁移分支无法通过验证时,怎样恢复稳定发布链路?
保留旧语言模式构建节点、原始依赖锁定文件和签名配置。迁移分支失败时,先恢复工具链与构建设置,再执行 Debug、Release、测试、Archive 和安装验证;只有这些结果与稳定分支一致,才可以把回滚标记为完成。
第三步:把旧依赖隔离在边界模块
第三方依赖不支持 Swift 6.3 时,先分清 3 种情况:
- 依赖有新版本,只是当前 Package 约束没有允许升级。
- 依赖源码可以在本地修复,但官方尚未发布兼容版本。
- 依赖包含预编译二进制模块,当前版本无法用新工具链稳定导入。
优先检查依赖项目的官方仓库、发布说明和兼容性声明。不要把修改直接散布到业务代码中,可以建立一个 Adapter 或 Compatibility Target,让主工程只依赖你定义的协议。
这种边界做法有 3 个好处:
- ✅ 依赖升级或回退时,改动集中在一个模块。
- ✅ Swift 5 模式依赖仍可暂时被旧 Target 使用。
- ✅ 并发不安全的 API 不会直接扩散到整个业务层。
如果依赖是二进制模块,还要额外验证模块接口、架构、签名和归档导出。源码能通过不代表二进制模块就能被所有构建配置正确导入;这类问题必须在真实的 Archive 流程中复现,而不能只看 Xcode 编辑器是否显示类型信息。
第四步:在 CI 中运行双工具链验证
迁移阶段不要让新流水线替代生产流水线。建议并行保留:
- 稳定流水线:旧语言模式、旧依赖锁定、当前发布签名和生产归档。
- 迁移流水线:Swift 6.3 编译器、指定 Target 的 Swift 6 模式、新依赖分支和额外并发检查。
- 比较报告:编译结果、单元测试、UI 测试、归档、签名、安装和关键运行时流程。
如果使用命令行构建,至少把 xcodebuild 的工程、Scheme、SDK、配置和目的地固定下来。Apple 的 命令行归档与导出说明明确区分 archive 与 exportArchive,因此 CI 需要分别保存归档日志和导出日志。
你还应设置明确的失败门槛:
- 迁移 Target 出现新的数据竞争诊断,流水线失败。
- 测试失败但编译成功,不能合并。
- Archive 成功但签名或导出失败,不能视为发布可用。
- 只要依赖解析结果发生变化,就必须重新运行完整测试。
- 迁移分支失败不能阻断稳定分支的生产发布。
如需把构建节点、工具链和签名环境分开管理,可以先阅读 Mac 构建环境隔离与远程使用说明,再决定是复制节点、建立独立队列,还是继续复用现有机器。
用这份清单判断是否可以扩大迁移范围
- [ ] 已记录每个 Target 的 Swift language mode 与并发检查设置。
- [ ] 已保存迁移前的依赖锁定文件、构建脚本和签名配置。
- [ ] 已在 Debug、Release、Archive 3 种配置下建立基线。
- [ ] 已选择一个依赖少、测试完整的首批 Target。
- [ ] 已将并发诊断分为共享状态、Actor、Sendable 和异步边界。
- [ ] 已确认每个
@unchecked Sendable或不安全标注都有代码级理由。 - [ ] 已查询第三方依赖的官方兼容版本或源码修复方案。
- [ ] 已验证 Swift、Objective-C 和 C 接口的跨模块调用。
- [ ] 已让稳定流水线与迁移流水线并行运行。
- [ ] 已完成测试、Archive、签名、导出和安装验证。
- [ ] 已演练恢复旧工具链、旧依赖和旧构建设置。
- [ ] 只有在上述项目全部通过后,才移除旧语言模式节点。
最后一步:用验收结果决定是否完成迁移
真正的完成标准不是“所有警告暂时消失”,而是以下条件同时成立:
- 目标 Target 的关键并发诊断已经清零,剩余项有明确记录和风险判断。
- 并发测试覆盖共享状态、Actor 切换、取消、超时和回调生命周期。
- 第三方依赖版本已经锁定,源码补丁和替代实现可以复现。
- 多 Target、混合语言、模拟器、真机和归档流程都通过。
- 稳定构建与迁移构建的产物行为没有未解释差异。
- 团队可以在独立节点恢复旧工具链,而不是依赖某位工程师本地机器上的缓存。
如果你现在只有一套可发布环境,直接在原节点完成全量迁移的风险很高:一旦语言模式、依赖解析和签名配置同时变化,失败原因会互相覆盖;而且迁移分支可能占用生产构建队列,影响正常发版。更稳妥的做法是复制一套经过验证的 Mac 构建环境,在独立节点中按 Target 推进 Swift 6.3 并发检查,再把通过验收的配置逐步合并回主流水线。
与继续复用唯一的本地 Mac 节点相比,Macstripe 的租赁方案更适合需要临时迁移环境、隔离 CI 队列或短期回归测试的团队:你不必承担一次性购置硬件、长期闲置维护和环境被发布任务占用这几项成本。若项目属于长期稳定重负载、必须连接专用物理设备,或需要持续保留固定本地资产,自购 Mac 仍可能更合适;但如果你的目标是先复制环境、验证 Swift 6.3 迁移并安全回滚,可以从 Mac 构建节点配置与订单开始评估临时节点方案。