Migrate your app to Swift 6
SwiftUI & UI Frameworks 进阶 20m

将你的 App 迁移到 Swift 6

Migrate your app to Swift 6

2024年6月10日

在 Apple 官方观看视频

一句话判断

如果你的 Swift App 还没开 Swift 6 模式,现在是时候了——编译器会告诉你所有潜在的数据竞争问题,而且迁移过程可以按模块逐步推进。

这场 Session 讲了什么

Swift 6 语言模式在 WWDC 2024 正式发布,核心能力是完整的数据隔离(data isolation)强制执行。在这之前,即使你用了 actor 和 async/await,编译器也不会阻止你把一个 class 实例在多个 actor 之间传递——共享可变状态仍然可能导致数据竞争。Swift 6 模式下,编译器会在编译期把这类问题全部标记出来。

Session 以 CoffeeTracker App 为例,展示了完整的迁移流程。这个 App 之前已经迁移到了 Swift concurrency(使用了 @MainActor、async/await、Sendable),看起来”应该”是安全的,但实际上编译器仍然允许不安全的代码存在。启用 Swift 6 模式后,编译器会变成一个”严格的代码审查者”,指出每一个可能的并发安全问题。

迁移策略是按模块逐步推进:先对每个 target 开启 Complete Concurrency Checking(产生警告但不阻止编译),解决所有警告后再开启 Swift 6 模式(将警告升级为错误)。Session 强调了一个重要原则:不要同时做大规模重构和开启 Swift 6,应该分开进行。

值得深挖的点

Swift 6 迁移的实际收益

很多人觉得”我的 App 已经用了好几年了,数据竞争早就修完了,迁移没意义”。Session 明确反驳了这种想法。迁移的真正价值不在于修已知 bug,而在于保护未来写的新代码。

当你新增功能或重构代码时,Swift 6 编译器会实时检查每一段新代码是否引入了数据竞争。这意味着你可以放心地增加并发——比如把某个同步操作改成异步,或者把某个 class 改成 actor——编译器会在出问题的第一时间阻止你。没有 Swift 6 模式的话,这类问题通常只在线上以难以复现的偶发崩溃出现。

对于维护公开 Swift Package 的开发者,尽早迁移到 Swift 6 还有一个额外好处:你的用户在迁移自己的项目时,不用再花时间处理依赖库的并发警告。

Complete Checking 到 Swift 6 的渐进式路径

Session 推荐的迁移路径是分三步走:先在 Build Settings 中对单个 target 开启 “Strict Concurrency Checking” 设为 “Complete”;然后逐个修复所有警告;最后将 Swift Language Version 切换为 6。每完成一个 target 再进入下一个。

这个渐进式路径的关键在于:Complete Checking 产生的是警告,不会阻止编译和运行。你可以在不影响日常开发节奏的情况下逐步推进。而且编译器的诊断信息非常具体,会直接指出哪行代码有并发安全问题、为什么有问题、建议怎么改。Session 形容它”像一个 pair programmer 在帮你找 bug”。

代码片段

场景一:开启 Complete Concurrency Checking 并解读警告

// 在 Xcode 中:Build Settings -> Swift Compiler - Language
// 将 "Strict Concurrency Checking" 设为 "Complete"
// 或者在 Package.swift 中:
target(
    name: "CoffeeKit",
    swiftSettings: [.unsafeFlags(["-strict-concurrency=complete"])]
)

// 开启后,编译器会对以下代码发出警告:
class CoffeeStore {
    var servings: [Coffee] = []  // ⚠️ Shared mutable state

    func addServing(_ coffee: Coffee) {
        servings.append(coffee)
    }
}

// 修复方案:标记为 actor 或 @MainActor
@MainActor  // 确保只在主线程访问
class CoffeeStore {
    var servings: [Coffee] = []

    func addServing(_ coffee: Coffee) {
        servings.append(coffee)
    }
}

// 坑点:如果 Coffee 不是 Sendable 的,跨 actor 传递时会报错
// 需要同时让 Coffee 遵循 Sendable 协议
struct Coffee: Sendable {  // struct 自动满足 Sendable
    let name: String
    let caffeine: Double
}

场景二:处理非 Sendable 类型的跨 actor 传递

// 场景:HealthKit 回调在任意线程,需要把数据传到主 actor
// Swift 6 会阻止直接传递非 Sendable 类型

// 错误写法:
func onHealthData(_ data: HealthSnapshot) {
    // ⚠️ HealthSnapshot 不是 Sendable,不能跨 actor 传递
    Task { @MainActor in
        viewModel.update(with: data)  // 编译器会报错
    }
}

// 修复方案一:让 HealthSnapshot 遵循 Sendable
struct HealthSnapshot: Sendable {
    let heartRate: Double
    let timestamp: Date
}

// 修复方案二:使用 @unchecked Sendable(当确认线程安全时)
final class HealthCache: @unchecked Sendable {
    private let lock = NSLock()
    private var _data: [HealthSnapshot] = []

    var data: [HealthSnapshot] {
        lock.lock()
        defer { lock.unlock() }
        return _data
    }
}
// 坑点:@unchecked Sendable 绕过了编译器检查
// 确保你真的保证了线程安全,否则等于把问题藏起来了

场景三:逐步迁移多个 target

# 迁移步骤(以 CoffeeTracker App 为例):

# 1. 确认 App 在 Xcode 16 下能正常编译
#    Swift 6 编译器保证源码兼容性,现有代码应该能直接编译

# 2. 对底层框架(CoffeeKit)开启 Complete Checking
#    Build Settings -> Swift Compiler - Language
#    -> Strict Concurrency Checking = Complete

# 3. 修复 CoffeeKit 中的所有并发警告
#    常见修复:
#    - class -> actor 或 @MainActor class
#    - struct 添加 Sendable 遵循
#    - closure 标记 @Sendable
#    - 使用 nonisolated 关键字隔离方法

# 4. 将 CoffeeKit 的 Swift Language Version 切为 6
#    此时所有警告变为错误,确保不会再有新的并发问题引入

# 5. 对 App 主 target 重复步骤 2-4

# 6. 全部完成后,可以考虑做一轮整体重构
#    比如移除之前临时加的 nonisolated(unsafe) 等

最佳实践

  • 迁移顺序:从底层依赖开始,逐层向上迁移。框架先迁移,App target 后迁移。因为底层框架迁移后,上层代码的 Sendable 约束更容易满足。
  • 不要混合重构和迁移:这两个操作应该分开做。先完成 Swift 6 迁移(保持代码逻辑不变),再单独做架构重构。混合进行会让问题定位变得极其困难。
  • nonisolated(unsafe) 是临时方案:Swift 6 提供了 nonisolated(unsafe) 来暂时绕过检查,但它是过渡工具,不是最终解决方案。在迁移完成后应该回过头用正确的架构替代它。

还有什么值得关注

  • Swift Package Index 网站可以查看主流开源库的 Swift 6 迁移进度,选依赖时可以参考
  • Swift 6 的数据隔离不仅影响 App 代码,extension 和 framework target 也需要迁移
  • Session 提到 Apple 内部的 iCloud Keychain、Photos、Notes 等服务都在用 Swift on Server,Swift 6 的安全性保障在大规模分布式系统中同样重要
WWDC 2024