Network framework 的结构化并发实践
Use structured concurrency with Network framework
2025年6月9日
一句话判断
Network framework 终于有了 Swift-native 的声明式 API——NetworkConnection、NetworkListener、NetworkBrowser 三个类型把底层网络编程的门槛拉低到了 SwiftUI 的水平。
这场 Session 讲了什么
这场 Session 介绍了 Network framework 在 iOS/macOS 26 中的三个全新 Swift 类型:NetworkConnection、NetworkListener 和 NetworkBrowser,全部基于 Swift 结构化并发构建。整体设计哲学与 SwiftUI 类似——声明式创建、自动生命周期管理、在 task 取消时自动清理。
NetworkConnection 负责建立连接和收发数据。端点(endpoint)定义目标地址,protocol stack 定义连接方式(TCP、TLS、QUIC 等),parameters 定义行为约束(如 low data mode 下不使用受限网络)。连接状态由框架自动管理:preparing → ready → failed/canceled,开发者可以安装 state handler 做 UI 更新,也可以完全不管——send/receive 会自动等待 ready 状态。
数据收发方面,除了传统的 byte stream 模式(指定接收字节数),今年新增了两个重要能力。一是内置的 TLV(Type-Length-Value)framer,自动帧化消息,解决 TCP 这类流协议不保留消息边界的问题。二是 Coder 协议,直接发送和接收 Codable 类型,支持 JSON 和 property list 格式,彻底省去手动序列化。
NetworkListener 监听入站连接,每次新连接自动启动 subtask,不会阻塞后续连接的接收。NetworkBrowser 发现网络上的设备,支持 Bonjour 和 iOS 26 新增的 Wi-Fi Aware 跨平台点对点发现。
主讲人最后给了一条简洁的选型建议:如果你的 app 做 HTTP 网络请求,继续用 URLSession,不需要改。如果你需要底层连接或者和自己的 app 通信,用 NetworkConnection + TLS/QUIC + Coder 是最佳组合。
值得深挖的点
TLV Framer vs Coder:什么时候用哪个
TLV(Type-Length-Value)framer 和 Coder 都解决同一个核心问题:流协议不保留消息边界。但它们的抽象层级不同。
TLV 是一个轻量级的二进制帧协议,每个消息由 type(描述数据类型)、length(数据大小)、value(实际数据)三部分组成。你需要手动编码/解码消息内容,但 TLV 负责把字节流切分成独立的消息。适合与已有服务器协议互通的场景——很多现有网络协议已经使用 TLV 格式,你的服务器可能已经会说了。
Coder 更进一步,它在 TLV 之上加了一层类型安全。你声明要发送和接收的 Codable 类型,指定编码格式(JSON 或 property list),框架自动完成序列化。发送方直接 connection.send(gameMessage),接收方直接得到 GameMessage 对象,零样板代码。
Trade-off:TLV 更灵活,适合需要精确控制二进制格式或与非 Swift 服务端通信的场景。Coder 更方便,适合 Swift-to-Swift 的全栈场景或你能控制协议格式的项目。性能上,TLV 的开销更小(没有 JSON 编码),Coder 更适合开发效率优先的场景。
实际建议:如果你在写一个新的 app-to-app 通信,直接用 Coder over TLS 或 QUIC。如果你在对接已有的 TLV 协议服务器,用 TLV framer。如果你需要对接一个非 TLV 的自定义协议,回到 byte stream 模式自己处理帧化。
连接状态管理的隐式便利与显式控制
NetworkConnection 的一个设计决策是:send 和 receive 会自动等待连接进入 ready 状态。这意味着你不需要写任何状态检查代码——调用 send 时如果连接还在 preparing,它会自动等待握手完成再发送。
这大幅简化了代码,但也隐藏了一个潜在问题:如果连接永远到不了 ready(比如服务器不可达),send 会一直挂起直到连接进入 failed 或 canceled 状态。在 UI 上你可能需要显示”连接中”的状态,这时候就需要安装 state change handler:
connection.stateUpdateHandler = { state in
switch state {
case .preparing: showConnecting()
case .ready: showConnected()
case .waiting: showWaiting()
case .failed: showError()
default: break
}
}
Trade-off:隐式等待让简单场景的代码极简,但对需要超时控制的场景不够友好。你需要自己用 Task.sleep 或 withTimeout 包装 send/receive 调用来实现超时——NetworkConnection 本身没有提供超时参数。
代码片段
1. 声明式连接 + Coder 发收消息
场景:用 TLS 连接服务器,直接发送和接收 GameMessage。
let endpoint = NWEndpoint.hostPort(host: "game.example.com", port: 1029)
let connection = NetworkConnection(
endpoint: endpoint,
protocolStack: .init(Coder<GameMessage>(format: .json), over: .tls)
)
try await connection.start()
// 发送
try await connection.send(GameMessage.move(.left))
// 接收
for try await (message, _) in connection.messages {
switch message {
case .character(let c): handleCharacter(c)
case .move(let m): handleMove(m)
}
}
坑:Coder 的 format 参数只支持 JSON 和 property list,如果你的服务器用 protobuf 或其他格式,不能用 Coder,需要回退到 TLV 或 byte stream。
2. TLV Framer 与服务器互通
场景:与使用 TLV 格式的已有服务器通信。
let connection = NetworkConnection(
endpoint: endpoint,
protocolStack: .init(TLV(), over: .tls)
)
// 发送时指定 type
try await connection.send(data, type: 1)
// 接收时获取 type
for try await (content, metadata) in connection.messages {
let type = metadata.tlvType
// 根据 type 处理不同消息
}
坑:TLV framer 是 iOS/macOS 26 新增的。如果你需要支持旧系统版本,需要自己实现帧化逻辑。
3. NetworkBrowser 发现附近设备
场景:通过 Wi-Fi Aware 发现并连接附近的 Tic-Tac-Toe 游戏。
let browser = NetworkBrowser(
browseDescriptor: .wifiAware(service: "Tic-Tac-Toe"),
parameters: .init()
)
let endpoint = try await browser.run { endpoints in
.finish(endpoints.first!)
}
// 用发现的端点创建连接
let connection = NetworkConnection(
endpoint: endpoint,
protocolStack: .init(Coder<GameMessage>(format: .json), over: .tls)
)
坑:Wi-Fi Aware 是 iOS 26 新增的,仅支持 iPhone。如果你需要跨平台发现,Bonjour 仍然是更通用的选择。
最佳实践
- HTTP 网络请求继续使用 URLSession,不需要迁移到 Network framework。
- App-to-app 通信优先选择 Coder over TLS/QUIC,获得类型安全和零样板代码的双重收益。
- 对接已有服务器时,先确认服务器使用的协议格式,再选择 TLV framer 或 byte stream 模式。
- 为 NetworkConnection 安装 stateUpdateHandler 以在 UI 中反映连接状态,即使你不需要主动管理状态。
- 不同线程使用不同的 Command Allocator(Metal 4 类比:NetworkConnection 的 task 取消会自动清理连接,但自己创建的 task 仍需注意生命周期)。
- Wi-Fi Aware 仅支持 iOS 26 的 iPhone,跨平台场景用 Bonjour + NetworkBrowser。
还有什么值得关注
- 如果你之前使用的是 Network framework 的 C API 或偏好 completion handler 风格,不需要迁移——所有旧 API 继续得到完整支持。
- NetworkListener 自动为每个新连接启动 subtask,你可以在 handler 里直接做 async 操作而不用担心阻塞后续连接。
- QUIC 支持内置于 protocol stack 中,从 TCP 切换到 QUIC 只需修改 protocol stack 声明,业务代码完全不变。