Highlight
Swift 5.7 带来了包管理器插件、分布式 actor、原生 Regex、简化的泛型语法、C 语言互操作改进等大量新特性,同时 docC 和 Swift.org 网站正式开源。
核心内容
包管理器插件:让构建流程自动化
你的项目里总有一些重复性工作:生成代码、运行文档工具、格式化源码。以前这些要么靠脚本,要么靠 Xcode 的 Build Phase,维护起来很分散。Swift 5.7 给 SPM 加了两种插件:Command Plugin 像 Makefile 目标,可以从命令行调用;Build Tool Plugin 更强大,能在编译前/后自动执行。同时还引入了 module aliases 解决同名模块冲突问题。
Swift 并发:走向 Swift 6
Swift 5.7 在并发安全方面迈出了重要一步。编译器现在能检测更多数据竞争场景,包括闭包内捕获可变状态和跨任务的数据竞争。分布式 actor(distributed actor)是最重要的并发新特性,它把 actor 模型从单进程扩展到多进程,调用远程 actor 和调用本地 actor 语法完全一致。
详细内容
包管理器插件:让构建流程自动化
(07:19)Swift 5.7 给 SPM 加了两种插件。Command Plugin 像 Makefile 目标,你可以从命令行调用:
import PackagePlugin
@main struct MyPlugin: CommandPlugin {
func performCommand(context: PluginContext, arguments: [String]) throws {
let doccExec = try context.tool(named: "docc").path
let doccArgs = ["convert", "--output-path", "docs"]
let process = try Process.run(doccExec, arguments: doccArgs)
process.waitUntilExit()
}
}
关键点:
CommandPlugin从命令行触发,适合文档生成、代码格式化等任务context.tool(named:)获取工具路径,不用硬编码
Build Tool Plugin 更强大,它能在编译前/后自动执行:
import PackagePlugin
@main struct MyCoolPlugin: BuildToolPlugin {
func createBuildCommands(context: TargetBuildContext) throws -> [Command] {
let generatedSources = context.pluginWorkDirectory.appending("GeneratedSources")
return [
.buildCommand(
displayName: "Running MyTool",
executable: try context.tool(named: "mycooltool").path,
arguments: ["create"],
outputFilesDirectory: generatedSources
)
]
}
}
关键点:
BuildToolPlugin在编译流程中自动执行outputFilesDirectory声明生成的文件,Xcode 会自动把它们加入编译
(09:23)还有一个长期痛点:两个依赖包导出了同名模块。Swift 5.7 的 module aliases 解决了这个问题:
let package = Package(
name: "MyStunningApp",
dependencies: [
.package(url: "https://.../swift-metrics.git"),
.package(url: "https://.../swift-log.git")
],
targets: [
.executableTarget(
name: "MyStunningApp",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Metrics", package: "swift-metrics",
moduleAliases: ["Logging": "MetricsLogging"]),
])
]
)
使用时两个模块名不再冲突:
import Logging // from swift-log
import MetricsLogging // from swift-metrics
let swiftLogger = Logging.Logger()
let metricsLogger = MetricsLogging.Logger()
Swift 并发:走向 Swift 6
(13:14)Swift 5.7 在并发安全方面迈出了重要一步。编译器现在能检测更多数据竞争场景:
var numbers = [3, 2, 1]
numbers.removeAll(where: { number in
number == numbers.count // 编译器警告:在闭包中捕获了可变状态
})
(14:10)跨任务的数据竞争也会被捕获:
var numbers = [3, 2, 1]
Task { numbers.append(0) } // 在另一个任务中修改
numbers.removeLast() // 编译器警告:数据竞争
(15:54)分布式 actor(distributed actor)是 Swift 5.7 最重要的并发新特性之一。它把 actor 模型从单进程扩展到多进程:
import Distributed
distributed actor Player {
var ai: PlayerBotAI?
var gameState: GameState
distributed func makeMove() -> GameMove {
return ai.decideNextMove(given: &gameState)
}
}
关键点:
distributed actor声明一个可能位于远程的 actordistributed func标记可以跨网络调用的方法- 参数和返回值需要满足序列化要求(默认 Codable)
调用远程 actor 和调用本地 actor 语法完全一致:
func endOfRound(players: [Player]) async throws {
for player in players {
let move = try await player.makeMove()
}
}
语言简化:更干净的语法
(20:12)Swift 5.7 简化了 optional unwrapping。以前要写:
if let mailmapURL = mailmapURL {
mailmapLines = try String(contentsOf: mailmapURL).split(separator: "\n")
}
变量名短了可读性差,长了又冗余。Swift 5.7 允许省略右侧:
if let workingDirectoryMailmapURL {
mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n")
}
guard let workingDirectoryMailmapURL else { return }
mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n")
(21:07)闭包类型推断也改进了。以前复杂闭包需要显式标注类型,现在编译器能从上下文推断:
let entries = mailmapLines.compactMap { line in
try? parseLine(line)
}
func parseLine(_ line: Substring) throws -> MailmapEntry { }
(22:15)C 语言互操作方面,Swift 5.7 改进了指针类型转换:
func removeDuplicates(from map: UnsafeMutablePointer<mailmap_t>) {
var size = mailmap_get_size(map)
size -= moveDuplicatesToEnd(map)
withUnsafeMutablePointer(to: &size) { signedSizePtr in
signedSizePtr.withMemoryRebound(to: UInt32.self, capacity: 1) { unsignedSizePtr in
mailmap_truncate(map, unsignedSizePtr)
}
}
}
关键点:
withMemoryRebound安全地在Int和UInt32之间转换指针- 不再需要手动管理内存布局
Swift Regex:字符串处理的新方式
(23:41)字符串解析一直是 Swift 的痛点。以前用 String 的索引操作写起来冗长且容易出错:
func parseLine(_ line: Substring) throws -> MailmapEntry {
func trim(_ str: Substring) -> Substring {
String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...]
}
let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)])
guard let nameEnd = activeLine.firstIndex(of: "<"),
let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"),
trim(activeLine[activeLine.index(after: emailEnd)...]).isEmpty else {
throw MailmapError.badLine
}
let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd])
let email = activeLine[activeLine.index(after: nameEnd)..<emailEnd]
return MailmapEntry(name: name, email: email)
}
(25:10)Swift 5.7 引入了原生 Regex,支持字面量和强类型捕获:
func parseLine(_ line: Substring) throws -> MailmapEntry {
let regex = /\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/
guard let match = line.prefixMatch(of: regex) else {
throw MailmapError.badLine
}
return MailmapEntry(name: match.1, email: match.2)
}
关键点:
/.../是 Regex 字面量,编译器会检查语法match.1和match.2是强类型捕获,类型是Substring
(26:34)对于更复杂的场景,Regex Builder 提供了声明式语法:
import RegexBuilder
let regex = Regex {
ZeroOrMore(.horizontalWhitespace)
Optionally {
Capture(OneOrMore(.noneOf("<#")))
}
.repetitionBehavior(.reluctant)
ZeroOrMore(.horizontalWhitespace)
"<"
Capture(OneOrMore(.noneOf(">#")))
">"
ZeroOrMore(.horizontalWhitespace)
ChoiceOf {
"#"
Anchor.endOfSubjectBeforeNewline
}
}
(27:05)Regex 组件可以复用,甚至可以嵌入 Foundation 的解析器:
struct DatedMailmapLine: RegexComponent {
@RegexComponentBuilder
var regex: Regex<(Substring, Substring?, Substring, Date)> {
ZeroOrMore(.horizontalWhitespace)
Optionally {
Capture(OneOrMore(.noneOf("<#")))
}
.repetitionBehavior(.reluctant)
ZeroOrMore(.horizontalWhitespace)
"<"
Capture(OneOrMore(.noneOf(">#")))
">"
ZeroOrMore(.horizontalWhitespace)
Capture(.iso8601.year().month().day())
ZeroOrMore(.horizontalWhitespace)
/#|\Z/
}
}
关键点:
RegexComponent协议让你把常用模式封装成可复用组件.iso8601.year().month().day()直接嵌入 Foundation 的日期解析器- 捕获的类型是
Date,不是字符串
泛型语法简化
(29:02)Swift 5.7 让泛型代码写起来更自然。以前协议作为类型使用时限制很多:
// 以前:只能用 generic 函数
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map)
// 以前:protocol 不能直接作为类型
func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout Mailmap) // 编译错误
(31:05)Swift 5.7 引入了显式的 any 语法:
func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) {
for entry in entries {
mailmap.addEntry(entry)
}
}
关键点:
any Mailmap明确表示这是一个 existential type- 带关联类型的协议也可以作为
any类型使用
(32:54)主关联类型(primary associated type)让约束表达更简洁:
// 以前
func addEntries1<Entries: Collection, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map)
where Entries.Element == MailmapEntry
// Swift 5.7
func addEntries1(_ entries: some Collection<MailmapEntry>, to mailmap: inout some Mailmap)
// 或者用 any
func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap)
关键点:
some Collection<MailmapEntry>等价于some Collection where Element == MailmapEntryany用于需要异构存储的场景,some用于隐藏具体类型
核心启发
1. 用 SPM 插件自动化项目工作流
把代码生成、文档构建、格式化检查等任务写成 SPM 插件。Command Plugin 适合 CI/CD 和本地开发命令,Build Tool Plugin 适合代码生成。检查你的项目里有哪些 shell 脚本可以迁移。
入口:PackagePlugin 框架,CommandPlugin / BuildToolPlugin 协议
2. 用 Regex Builder 替换字符串解析代码
项目里所有用 String.index 手工解析的地方,都值得看看能不能用 Regex 或 Regex Builder 替代。特别是日志解析、配置文件读取、数据格式转换这类场景。
入口:RegexBuilder 模块,RegexComponent 协议
3. 为 Swift 6 的严格并发检查做准备
开启编译器的 Strict Concurrency 检查,逐步给类型标注 Sendable。分布式 actor 为跨进程通信提供了类型安全的抽象,适合游戏、协作工具等需要实时通信的 App。
入口:-strict-concurrency=complete,Sendable 协议,distributed actor
4. 简化现有泛型代码
检查项目中手写的类型擦除包装器(AnyPublisher、AnyCollection 等自定义版本),看看能不能直接换成 any 语法。新的 some + 主关联类型写法让 API 签名更短更清晰。
入口:any ProtocolName<AssociatedType>,some ProtocolName<AssociatedType>
关联 Session
- Design protocol interfaces in Swift — 深入讲解 Swift 5.7 的
any/some语法和 same-type requirement - Meet Swift Regex — Swift Regex 的完整使用指南,包含 Unicode 处理细节
- Meet Swift Async Algorithms — 基于 Swift 并发的新算法库,包含 Zip、Merge、Debounce 等
- Meet distributed actors in Swift — 分布式 actor 的实战指南,包含 WebSocket 和本地网络示例
评论
GitHub Issues · utterances