WWDC22 上 Swift 语言的公告
Swift 在过去一年中经历了显著的发展,我们已经看到了该语言的两次重大更新。Swift 5.6 于 2022 年 3 月发布,对类型系统、并发模型和 Swift 生态系统进行了重大改进。它为 Swift 5.7 中的进一步更新奠定了基础,Swift 5.7 包含在 Xcode 14 的 Beta 版本中,并可在 下载页面上获取。
在 WWDC22 期间,许多 Swift 更新都受到了理所当然的赞誉,会议重点关注语言更改、工具改进、Swift 软件包的添加等。Swift 新功能视频概述了过去一年 Swift 的新闻。在这篇文章中,我们想分享我们在 WWDC 上关于 Swift 生态系统的亮点。
社区倡议
围绕 Swift 语言的社区正在壮大,早期推出的伟大倡议正在扩展并获得更广泛的支持。已经宣布成立新的工作组,这些工作组将专注于 Swift 生态系统中的特定领域。Swift 网站工作组正在指导 网站的发展,Swift 和 C++ 互操作性组正在努力推进这两种语言之间的互操作性支持,而 Swift 语言工作组正在监督语言和标准库。Swift 导师计划于 2021 年宣布,已延长一年,并包括其他主题,例如 DocC、C++ 互操作性和 Swift 网站。值得一提的是, 网站现在是开源的,并已准备好接受社区贡献。
说到开源,去年宣布的用于 Swift 框架和软件包的 Swift-DocC 编译器现在也是开源的,并且有一些很大的改进。它增加了对应用程序项目的支持,以及对 Objective-C 和 C API 文档的支持。我们绝对建议您查看Swift-DocC 新功能会议以了解更多信息。
Swift 软件包
我们已经看到了 Swift Package Manager 的重要更新,包括对模块消除歧义的支持、定义构建工具插件和自定义命令插件的能力,以及安全性和性能改进。
从 Swift 5.6 开始,Swift Package Manager 执行首次使用时信任 (TOFU) 验证。软件包的指纹在首次下载软件包时记录,如果指纹不同,后续下载将报告错误。此更改非常有利于提高软件包的安全性。
在 Swift 5.7 中,Swift Package Manager 获得了一项令人兴奋的改进,这将帮助我们避免在项目中使用多个同名软件包时出现问题。它现在允许我们命名来自定义它们的软件包外部的模块,并添加模块别名。
借助新的 PackagePlugin
API,我们可以定义自定义插件来生成源代码或自动化发布任务。创建 Swift Package 插件会议教我们如何在 Swift 中编写此类插件。
语言更新
今年,我们看到了 Swift 语言的一些重大新增功能,其中包括从较小的语法改进到较大的泛型和并发更新的各种变化。
这篇文章重点介绍了今年 WWDC 上引起我们注意的一些 Swift 更新。对于以下各节中的代码示例,我们从新西兰的 年度鸟类比赛中汲取了灵感。毕竟,考虑到 2021 年度鸟类大赛的获胜者是谁(是一只蝙蝠🤫),为参赛者提供一些额外的类型安全不会有坏处。
生活质量改进
Swift 5.7 中一个虽小但非常好的改进是使用 if let
、guard let
和 while let
进行可选解包的简写语法。我们可以删除右侧,以获取与原始名称相同的解包可选值。
var startDate: Date?
if let startDate {
print("""
Bird of the Year competition \
starts on \(startDate.formatted())
""")
}
另一个将改进我们代码的更改是支持具有多条语句的复杂闭包的类型推断。如果我们的闭包包含 if else
、do catch
或任何其他控制流语句,我们不再需要手动指定返回类型。
let participants = ["Kororā", "Weka", "Pekapeka-tou-roa"]
let introductions = participants.map {
if $0.hasPrefix("Pekapeka") {
return "\($0) is a mammal"
} else {
return "\($0) is a bird"
}
}
字符串处理
Swift 5.7 具有字符串处理的重大更新,引入了正则表达式字面量和 RegexBuilder 库,并配有匹配方法和强类型捕获。
我们可以命名我们的匹配项,并轻松地从最终结果中提取它们。
let intent = "I vote for Pūkeko and Kea"
let regex = /I vote for (?<bird1>.+?) and (?<bird2>.+?)/
if let votes = try? regex.wholeMatch(in: intent) {
print("Your first choice is \(votes.bird1)")
print("Your second choice is \(votes.bird2)")
}
借助 RegexBuilder,我们可以使用 SwiftUI 风格的语言构建正则表达式搜索,这可以使其更具可读性并解锁更强大的功能。
let word = OneOrMore(.word)
let regex = Regex {
"I vote for "
Capture { word }
" and "
Capture { word }
}
if let votes = try? regex.wholeMatch(in: intent) {
let (_, bird1, bird2) = votes.output
print("Your first choice is \(bird1)")
print("Your second choice is \(bird2)")
}
关于新的字符串处理 API 有两个内容丰富的会议:认识 Swift Regex 和 Swift Regex:超越基础。
泛型和协议
今年 Swift 协议和泛型有很多改进。协议现在支持主要关联类型。我们可以在协议名称旁边的尖括号中指定它,它应该是调用站点比其他类型更常用的类型。
protocol Contestant<Habitat> {
associatedtype Habitat: Territory
associatedtype Food
var home: Habitat { get set }
var favoriteFood: Food { get set }
var name: String { get }
}
使用尖括号语法,我们可以将主要关联类型约束为特定类型。
func fundReforestation<Animal: Contestant<Forest>>(for animal: Animal) {
scheduleTreePlanting(in: animal.home)
}
由于可以使用 some
和参数类型的新功能,编写泛型方法和函数也变得更容易。如果泛型参数仅在一个地方使用,我们可以指定协议以及 some
关键字作为其类型,而无需在函数上设置泛型约束。
func fundConservationEfforts(for animal: some Contestant) {
establishProtectedAreas(in: animal.home)
startIntensiveMonitoring(of: animal)
}
我们甚至可以将 some
与具有主要关联类型约束的参数一起使用,因此我们可以以更简洁的方式重写我们之前的 fundReforestation()
函数示例。
func fundReforestation(for animal: some Contestant<Forest>) {
scheduleTreePlanting(in: animal.home)
}
Swift 中的存在类型也进行了大量改进。我们现在可以使用 any
关键字来标记使用存在类型的位置,并且我们不再具有以前将存在类型与 Self
或 associatedtype
要求一起使用时的约束。我们可以将它们放入集合中,使用存在类型来约束变量或将其用作参数类型。
例如,我们可以创建一个参赛者数组,这在以前是无法做到的,因为我们示例中的 Contestant
协议具有关联类型。
let contestants: [any Contestant] = [
brownKiwi, ruru, whio
]
for contestant in contestants {
fundConservationEfforts(for: contestant)
}
了解到以前的存在类型约束已不复存在,我们可以审核我们现有的代码以查找类型擦除包装器,并检查我们是否可以使用现在的存在类型重新实现它们。
要了解有关 Swift 5.7 中泛型的更多信息,请务必查看拥抱 Swift 泛型会议。要了解如何使用协议设计高级抽象,请观看在 Swift 中设计协议接口。
并发
今年的并发更新建立在去年的更改之上,并侧重于数据竞争安全性。我们甚至在 Xcode 中有了新的选择加入安全检查,以帮助我们识别潜在问题。
去年引入的 Swift Actor 通过隔离对其属性的访问来帮助我们编写线程安全的代码。今年新的分布式 Actor 将 Actor 隔离的概念更进一步,并简化了分布式系统的开发。开源 Distributed Actors 库为在 Swift 中构建服务器端集群分布式系统提供了完整的解决方案。在 Swift 中认识分布式 Actor 视频更详细地介绍了在处理分布式系统和应用程序时如何使用 Actor。
我们还有一个新的 Swift 软件包 Async Algorithms,它为使用 AsyncSequence
提供了现成的解决方案。它包括一些我们自己可能难以实现的算法,例如 debounce
、throttle
、merge
和 zip
。这些算法帮助我们处理随时间变化的值。
let kiwiFinder = KiwiFinder()
let counter = Counter()
for try await (i, kiwi) in zip(counter, kiwiFinder) {
print("Kiwi number \(i) is a \(kiwi.name)")
}
Swift 中的另一个伟大新增功能是与时间交互的新标准方式,其中包括三个不同的组件:时钟、瞬时和持续时间。这些新的 API 与并发任务很好地集成。
func announceWinner(_ winner: some Contestant) async throws {
print("Building suspense...")
try await Task.sleep(
until: .now + .seconds(10),
tolerance: .seconds(1),
clock: .suspending
)
print("The winner is \(winner.name)!")
}
要了解有关 Async Algorithms 软件包的更多信息,并发现使用 Swift Clock
类型处理随时间变化的值的最佳实践,请查看 认识 Swift Async Algorithms WWDC 会议。
今年 Swift 中的并发支持获得了许多其他重大升级,例如性能优化的改进、Actor 优先级和内置的优先级反转避免。Instruments 现在有一个新的 Swift Concurrency 模板,可以帮助我们调查性能问题。可视化和优化 Swift 并发演讲讨论了我们在应用程序中可能遇到的常见问题,并展示了如何使用 Instruments 查找和解决性能问题。
还有很多其他很棒的更新,无法全部涵盖。由于并行化,我们在 Xcode 中获得了更快的构建速度,协议和 where
子句的函数签名的类型检查速度更快,运行时协议一致性检查的优化等等。随着标准库尺寸的减小,Swift 可以在各种环境中运行,并且随着 Linux 的精简工具链分发,我们中的许多人可以在我们喜欢的平台上享受使用 Swift 的乐趣。
我们真的很期待进一步探索所有这些新功能,并在我们的项目中使用它们。