WWDC Quick Look 💓 By SwiftGGTeam
What's new in Swift

What's new in Swift

观看原视频

Highlight

Swift 5.3 把更新集中在运行时体积和内存、Xcode 诊断和补全、跨平台支持、@main 与多尾随闭包、Apple Archive、OSLog、Swift Numerics 和 ArgumentParser,让 Swift 能覆盖 app、命令行工具、系统层库和服务器函数。

核心内容

Swift 5 已经完成 ABI stability 和 API stability,SwiftUI 也把 Swift 推到了 Apple 平台开发的中心。到了 WWDC20,问题变成了另一个层面:Swift 是否足够轻、足够好调试、足够适合系统层和服务器场景(00:14)。

这场 session 的回答先从底层性能开始。Swift 5.3 继续缩小代码体积差距,Swift 版本的 UIKit app 已经降到 Objective-C 版本代码体积的 1.5 倍以下;MovieSwiftUI 的应用逻辑代码体积减少超过 40%。堆内存也同步下降,同一个 Swift app 在新版本里的启动期 runtime overhead 降到上一年版本的三分之一以下(02:2906:04)。

开发体验也被放在同等重要的位置。编译器诊断开始指向更精确的位置,并给出更多 fix-it 线索;SourceKit 驱动的补全和缩进针对 SwiftUI 链式调用做了优化,部分补全场景相比 Xcode 11.5 快到 15 倍;LLDB 在 Clang module 导入失败时可以从 DWARF debug information 回退导入 C 和 Objective-C 类型,让变量视图和表达式求值更可靠(07:1809:3110:58)。

语言层面,Swift 5.2 和 5.3 增加了十多个特性。Session 挑出几个会直接改变日常代码形态的点:多尾随闭包让带多个 closure 参数的调用更适合 DSL;@main 把入口点从特殊的 main.swift 扩展成类型协议能力;KeyPath expression 可以当函数传递;enum 可以自动合成 Comparable,也可以用 enum case 满足 static protocol requirement(15:0220:0923:29)。

最后一部分把 Swift 从语言更新推向生态更新。SDK 里出现 Float16、Apple Archive、Swift System 和更快的 OSLog;Swift Package Manager 之外还有 Swift Numerics、Swift Argument Parser 和 Swift Standard Library Preview。它们共同指向一个变化:Swift 的新能力不只等系统 SDK 发布,也会先通过开源 package 进入开发者手里(26:0428:5430:38)。

详细内容

运行时优化让 Swift 更适合系统层

02:29)Swift 5.3 的优化来自 runtime 和工具链层面的累积,并不对应单个 API。Session 给出两个具体指标:一个 Swift UIKit app 的代码体积已低于 Objective-C 版本的 1.5 倍;MovieSwiftUI 的应用逻辑代码体积减少超过 40%。这会影响下载体积,也会影响可被系统回收的 clean memory。

05:15)堆内存示例说明了 Swift value type 的收益:400 个模型对象在 Swift 里约占 20KB heap memory,Objective-C 版本约占 35KB。Swift 5.3 还降低了启动期 caches、protocol conformance、type information 和 Objective-C bridging 相关开销,让一个 Swift app 的 heap memory 低于上一年版本的三分之一。

06:52)Standard Library 被放到 Foundation 下方后,Swift 可以用于实现低于 Objective-C 层级的框架。这个改动解释了为什么 Apple 要持续压 runtime memory:系统 daemon 和低层框架对每一字节都敏感。

关键点:

  • 代码体积优化、clean memory 和 heap memory 是这场 session 的开场主线,不是附带指标。
  • value type、small string、连续数组存储解释了 Swift 模型数据更紧凑的原因。
  • Standard Library 位置下沉意味着 Swift 能进入过去更多依赖 C 的系统层场景。

跨平台 Swift 和 AWS Lambda

12:34)Swift 5.3 更新了 Ubuntu 支持,增加 CentOS 和 Amazon Linux 2,并预告 Windows 初始支持。Session 随后把跨平台能力落到 AWS Lambda:用开源 Swift AWS runtime 写 serverless function。

import AWSLambdaRuntime

Lambda.run { (_, event: String, callback) in
    callback(.success("Hello, \(event)"))
}

关键点:

  • import AWSLambdaRuntime 说明这项能力来自开源 runtime package,而非 Foundation 或系统 SDK API。
  • Lambda.run 注册 Lambda handler,闭包接收事件并通过 callback 返回结果。
  • event: String 展示了演示里最小的输入模型,适合说明 Swift 在服务器函数里的启动方式。
  • callback(.success(...)) 把返回值交给 Lambda runtime,示例刻意保持到 “Hello, world” 级别。

@main 把入口点交给类型

20:09)过去 AppDelegate 可以用 UIApplicationMain 让编译器生成隐式 main.swift。Swift 5.3 把这种能力泛化为 @main:库作者在协议或父类上定义 static main,使用者把入口类型标注为 @main,编译器生成入口代码。

// Type-based program entry points

import ArgumentParser

@main
struct Hello: ParsableCommand {
    @Argument(help: "The name to greet.")
    var name: String

    func run() {
        print("Hello, \(name)!")
    }
}

关键点:

  • import ArgumentParser 引入的是 Swift open-source package,后文的 Argument Parser 示例会继续扩展它。
  • @main 标在 Hello 类型上,入口点从单独文件变成类型声明的一部分。
  • ParsableCommand 提供命令行工具约定,库作者可以通过协议把入口规则暴露给用户。
  • @Argument 把命令行位置参数声明为属性,run() 是真正执行命令逻辑的地方。

enum 自动合成 Comparable

23:29)Swift 4.1 已经能合成 EquatableHashable。Swift 5.3 继续扩展到符合条件的 enum Comparable,让有自然顺序的状态枚举少写样板代码。

// Synthesized comparable conformance for enums

enum MessageStatus: Hashable, Comparable {
    case draft
    case saved
    case failedToSend
    case sent
    case delivered
    case read

    var wasSent: Bool {
        self >= .sent
    }
}

关键点:

  • MessageStatus 同时声明 HashableComparable,比较逻辑由 compiler synthesize。
  • case 的声明顺序就是比较顺序,.sent 之后的状态都代表已经发出。
  • wasSentself >= .sent 表达业务规则,比手写 switch 更短。
  • 这个例子适合状态机、消息队列、同步阶段等有线性顺序的 enum。

Apple Archive 和 Swift System

26:55)Apple Archive 是新的模块化归档格式,基于 Apple 用来交付系统更新的技术,提供 Finder 集成、命令行工具和 Swift API。示例展示了把目录写入 .aar 文件,并用 LZFSE 压缩。

// Apple Archive

import AppleArchive

try ArchiveByteStream.withFileStream(
    path: "/tmp/VacationPhotos.aar",
    mode: .writeOnly,
    options: [.create, .truncate],
    permissions: [.ownerReadWrite, .groupRead, .otherRead]
) { file in
    // Receives raw bytes and writes compressed bytes to `file`
    try ArchiveByteStream.withCompressionStream(using: .lzfse, writingTo: file) { compressor in
        // Receives archive entries, and writes bytes to `compressor`
        try ArchiveStream.withEncodeStream(writingTo: compressor) { encoder in
            // Writes all entries from `src` to `encoder`
            try encoder.writeDirectoryContents(archiveFrom: source, keySet: fieldKeySet)
        }
    }
}

关键点:

  • ArchiveByteStream.withFileStream 创建目标文件流,optionspermissions 使用强类型枚举。
  • withCompressionStream(using: .lzfse, writingTo: file) 把原始字节压缩后写到文件。
  • ArchiveStream.withEncodeStream 接收 archive entries,再交给压缩流。
  • writeDirectoryContents(archiveFrom:keySet:) 负责把源目录写入 archive,示例里的 sourcefieldKeySet 来自演示上下文。
  • Session 特别指出 FileStream 构造器依赖 Swift System,它用强类型包装低层系统调用,减少 Darwin overlay 原始接口的易错点。

OSLog 的字符串插值和格式化

28:11)OSLog 的目标是低开销和隐私保护。Swift 5.3 通过 compiler optimization 加速 OSLog,并增加字符串插值与格式化选项,让日志写法更接近普通 Swift 字符串。

logger.log("\(offerID, align: .left(columns: 10), privacy: .public)")
// Logs "E1Z3F    "

logger.log("\(seconds, format: .fixed(precision: 2)) seconds")
// Logs "1.30 seconds"

关键点:

  • privacy: .public 明确把 offerID 标为可公开显示,符合 unified logging 默认保护敏感数据的设计。
  • align: .left(columns: 10) 让字段在日志里按列对齐,便于后续查看。
  • format: .fixed(precision: 2) 让数值保留两位小数,避免手动字符串格式化。
  • 如果项目还在用 print 调试线上问题,这组 API 是迁移到统一日志的入口。

ArgumentParser 让命令行工具保持声明式

29:48)Swift Argument Parser 是新的开源 package。Session 用同一个 Hello 命令继续演示:添加 count option 后,工具能重复输出 greeting,并自动获得参数校验和帮助信息。

// Swift ArgumentParser

import ArgumentParser

@main
struct Hello: ParsableCommand {
    @Option(name: .shortAndLong, help: "The number of times to say hello.")
    var count: Int = 1

    @Argument(help: "The name to greet.")
    var name: String

    func run() {
        for _ in 1...count {
            print("Hello, \(name)!")
        }
    }
}

关键点:

  • @Option(name: .shortAndLong)count 同时支持短参数和长参数形式。
  • var count: Int = 1 同时声明类型和默认值,Argument Parser 会据此做解析和校验。
  • @Argument 继续表示位置参数,help 文案进入自动生成的帮助页。
  • run() 里只保留业务逻辑,入口、参数解析、帮助屏幕都交给 package 处理。

核心启发

  • 做什么:给现有 Swift app 做一次体积和堆内存回归检查。为什么值得做:Swift 5.3 的代码体积、SwiftUI app 逻辑体积和 runtime heap overhead 都有明确优化,老项目升级后可能直接受益。怎么开始:用同一份代码分别在旧 Xcode 和 Xcode 12 上 Archive,对比 binary size;在 Instruments 里记录启动后的 heap memory。

  • 做什么:把内部脚本改成 Swift Argument Parser 命令行工具。为什么值得做@mainParsableCommand 能把入口、参数声明和帮助信息放在一个类型里,减少手写 main.swift 和参数解析代码。怎么开始:新建 Swift Package,引入 apple/swift-argument-parser,把脚本参数拆成 @Option@Argument

  • 做什么:为线上诊断日志建立 OSLog 包装层。为什么值得做:Swift 5.3 的 OSLog 支持字符串插值、隐私标记和格式化,能替代散落的 print,也能降低泄露敏感信息的风险。怎么开始:从一条关键业务链路开始,定义 Logger 分类,把用户输入默认保持 private,只把必要 ID 标成 .public

  • 做什么:把资源型组件拆成 Swift Package。为什么值得做:本场提到 Swift Package 生态扩展,相关 session 10169 继续讲资源和本地化;组件包可以同时带代码、图片、storyboard 和 localized strings。怎么开始:先抽一个设计系统或空状态组件包,按 SourcesResources.process 组织文件,再在 app target 中替换本地引用。

  • 做什么:用 Apple Archive 做开发资产打包工具。为什么值得做:Apple Archive 提供 Swift API、LZFSE 压缩和命令行工具,适合把示例数据、日志包或设计资源归档成可传输文件。怎么开始:把 session 的 withFileStreamwithCompressionStreamwithEncodeStream 示例改成一个内部 CLI,输入目录,输出 .aar

关联 Session

评论

GitHub Issues · utterances