What’s new in Xcode 16
Developer Tools 进阶 20m

Xcode 16 新特性一览

What’s new in Xcode 16

2024年6月10日

在 Apple 官方观看视频

一句话判断

Xcode 16 在编辑、构建、调试、测试四个环节都有实质改进,其中 Explicit Modules 和 Swift 6 渐进式迁移是最值得开发者立刻上手的两件事。

这场 Session 讲了什么

这场 Session 覆盖了 Xcode 16 在开发全流程中的更新。编辑方面,代码补全用上了设备端训练的 Swift/Apple SDK 模型,能根据函数名和注释上下文给出更贴切的建议,这完全依赖 Apple Silicon 的本地算力。

Swift 6 带来了新的语言模式,将数据竞争(data race)从运行时问题提升为编译期问题。Xcode 16 支持在 Build Settings 的 “Swift Compiler - Upcoming Features” 中逐个启用即将到来的语言特性,不需要一步切到 Swift 6。每启用一个特性,构建时就会产生对应的警告,你可以逐个修复,等准备好了再切换语言版本。

Preview 方面有两个新 API:Previewable 宏和 PreviewModifier。Previewable 让你在 Preview 块里直接使用 @State 等属性包装器,不用再写包装视图。PreviewModifier 解决了预览间共享数据的问题,比如统一提供 Mock 的 ModelContainer,而且数据会被预览系统缓存,只初始化一次。

构建方面,Explicit Modules 是今年的重头戏。它将编译过程拆分为扫描依赖、构建模块、编译源码三个显式阶段,带来更好的并行性、更清晰的错误信息、更快的调试体验。对 C/Objective-C 默认开启,Swift 需要手动 opt-in。

值得深挖的点

Explicit Modules:不只是构建变快

Explicit Modules 的价值不仅仅在于构建速度。以前模块依赖是在编译源文件时隐式处理的,出了问题错误信息很难定位。现在依赖扫描和模块构建有了独立的构建日志条目,“Scan dependencies” 和 “Compile Clang/Swift module” 一目了然。

对调试也有直接影响。LLDB 现在可以复用构建产物中的模块信息来计算表达式,调试时 evaluate expression 变快了。再加上 DWARF5 成为 macOS Sequoia / iOS 18 默认的调试符号格式,dSYM 包更小、符号查找更快。

在 Build Timeline 中你能清晰看到每个模块的构建耗时,方便找到优化点。需要特别注意的是 Swift 端需要手动在 Build Settings 中开启 “Explicitly Built Modules”。

PreviewModifier:预览数据共享的最佳实践

PreviewModifier 的工作机制值得仔细理解。它有两个要求:makeSharedContext 是 async + throwing 的,可以异步加载数据并处理错误,关键是预览系统会缓存这个方法的返回值——同类型的 modifier 只会调用一次。body 方法负责把共享数据注入预览视图。

实际场景中这特别适合 SwiftData 的 ModelContainer、网络服务的 mock 实例、大型数据集的加载。以前每个 Preview 都要重复准备数据,现在统一在一个地方搞定。定义一个 PreviewTrait 扩展可以让调用点更简洁。

代码片段

Previewable 宏简化预览

// 以前:需要写包装视图
// struct RobotFacePreview: View {
//     @State var face: RobotFace = .happy
//     var body: some View { RobotFaceSelectorView(face: $face) }
// }

// Xcode 16:用 Previewable 宏直接在预览中使用 @State
#Preview {
    @Previewable var face: RobotFace = .happy
    RobotFaceSelectorView(face: $face)
}

坑点:Previewable 目前只支持属性包装器,普通变量不行。如果你的 Binding 需要更复杂的初始化逻辑,还是得用包装视图。

PreviewModifier 共享预览数据

// 定义 modifier
struct RobotNamerPreviewModifier: PreviewModifier {
    // 只会被调用一次,结果被预览系统缓存
    func makeSharedContext() async throws -> RobotNamer {
        // 从本地文件加载,避免每次都请求外部服务器
        let names = try loadNamesFromBundle()
        return RobotNamer(names: names)
    }
    
    // 将共享数据注入预览视图
    func body(content: Content, context: RobotNamer) -> some View {
        content.environment(context)
    }
}

// 定义 trait 扩展让调用更简洁
extension PreviewTrait where T == PreviewModifierTraits {
    static var robotNamer: Self {
        .modifier(RobotNamerPreviewModifier())
    }
}

// 使用
#Preview(.robotNamer) {
    RobotNameSelectorView()
}

Swift 6 渐进式迁移

// Build Settings > Swift Compiler - Upcoming Features
// 逐个启用即将到来的语言特性

// 例如启用 "Isolated Global Variables" 后
// 编译器会警告不安全的全局变量

// 修复前(触发警告)
var logger = Logger() // 非并发安全的全局变量

// 修复后
let logger = Logger() // 改为 let,不可变即安全

坑点:不要一次性开启所有 upcoming features,逐个处理更可控。在 Swift 6 语言模式下这些警告会变成错误,所以早处理比晚处理好。

最佳实践

迁移到 Xcode 16 后建议做三件事。第一,在 Build Settings 中开启 Explicit Modules for Swift,享受更好的构建并行性和调试性能。第二,开始在测试目标中使用 Swift Testing(Xcode 16 新测试模板的默认选择)。第三,如果项目还没迁移 Swift 6,从 “Upcoming Features” 中挑一两个最相关的特性开始启用,比如 Isolated Global Variables 和 Strict Concurrency,逐步解决警告。

还有什么值得关注

  • Thread Performance Checker 新增了磁盘写入过多和应用启动慢的诊断
  • Organizer 中新增了应用启动诊断日志分类,可以看到最慢的代码路径签名
  • 磁盘写入诊断现在可以跨版本对比问题影响趋势
WWDC 2024