axiom-network-framework-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNetwork.framework API Reference
Network.framework API参考文档
Overview
概述
Network.framework is Apple's modern networking API that replaces Berkeley sockets, providing smart connection establishment, user-space networking, built-in TLS support, and seamless mobility. Introduced in iOS 12 (2018) with NWConnection and evolved in iOS 26 (2025) with NetworkConnection for structured concurrency.
Network.framework是苹果推出的现代网络API,可替代Berkeley套接字,提供智能连接建立、用户态网络、内置TLS支持以及无缝移动网络切换能力。该框架于iOS 12(2018年)推出,包含NWConnection,并在iOS 26(2025年)演进为支持结构化并发的NetworkConnection。
Evolution timeline
演进时间线
- 2018 (iOS 12) NWConnection with completion handlers, deprecates CFSocket/NSStream/SCNetworkReachability
- 2019 (iOS 13) User-space networking (30% CPU reduction), TLS 1.3 default
- 2025 (iOS 26) NetworkConnection with async/await, TLV framing built-in, Coder protocol, Wi-Fi Aware discovery
- 2018年(iOS 12) 推出NWConnection,弃用CFSocket/NSStream/SCNetworkReachability
- 2019年(iOS 13) 支持用户态网络(CPU占用降低30%),默认启用TLS 1.3
- 2025年(iOS 26) 推出NetworkConnection(async/await),内置TLV帧、Coder协议、Wi-Fi Aware发现
Key capabilities
核心能力
- Smart connection establishment Happy Eyeballs (IPv4/IPv6 racing), proxy evaluation (PAC), VPN detection, WiFi Assist fallback
- User-space networking ~30% lower CPU usage vs sockets, memory-mapped regions, reduced context switches
- Built-in security TLS 1.3 by default, DTLS for UDP, certificate pinning support
- Mobility Automatic network transition handling (WiFi ↔ cellular), viability notifications, Multipath TCP
- Performance ECN (Explicit Congestion Notification), service class marking, TCP Fast Open, UDP batching
- 智能连接建立 Happy Eyeballs(IPv4/IPv6竞速)、代理评估(PAC)、VPN检测、WiFi Assist fallback
- 用户态网络 相比套接字CPU占用降低约30%,采用内存映射区域,减少上下文切换
- 内置安全 默认启用TLS 1.3,UDP支持DTLS,支持证书绑定
- 移动网络适配 自动处理网络切换(WiFi ↔ 蜂窝网络)、可用性通知、多路径TCP
- 性能优化 显式拥塞通知(ECN)、服务类别标记、TCP快速打开、UDP批量发送
When to use vs URLSession
与URLSession的适用场景对比
- URLSession HTTP, HTTPS, WebSocket, simple TCP/TLS streams → Use URLSession (optimized for these)
- Network.framework UDP, custom protocols, low-level control, peer-to-peer, gaming, streaming → Use Network.framework
- URLSession 适用于HTTP、HTTPS、WebSocket、简单TCP/TLS流 → 优先使用URLSession(针对这些场景做了优化)
- Network.framework 适用于UDP、自定义协议、底层控制、点对点通信、游戏、流媒体 → 使用Network.framework
Related Skills
相关技能
- Use for anti-patterns, common patterns, pressure scenarios
axiom-networking - Use for systematic troubleshooting of connection failures
axiom-networking-diag
- 使用了解反模式、常见模式和压力场景
axiom-networking - 使用进行连接故障的系统化排查
axiom-networking-diag
When to Use This Skill
何时使用本技能
Use this skill when:
- Planning migration from BSD sockets, CFSocket, NSStream, or SCNetworkReachability
- Understanding API differences between NWConnection (iOS 12-25) and NetworkConnection (iOS 26+)
- Implementing all 12 WWDC 2025 examples (TLS connection, TLV framing, Coder protocol, NetworkListener, Wi-Fi Aware)
- Choosing protocols (TCP, UDP, TLS, QUIC) for your use case
- Peer-to-peer discovery setup with NetworkBrowser and Wi-Fi Aware
- Optimizing performance with user-space networking, batching, pacing
- Migrating from completion handlers to async/await (NWConnection → NetworkConnection)
在以下场景中使用本技能:
- 规划迁移 从BSD套接字、CFSocket、NSStream或SCNetworkReachability迁移
- 理解API差异 对比NWConnection(iOS 12-25)和NetworkConnection(iOS 26+)的API差异
- 实现WWDC 2025的12个示例(TLS连接、TLV帧、Coder协议、NetworkListener、Wi-Fi Aware)
- 选择协议 为你的使用场景选择合适的协议(TCP、UDP、TLS、QUIC)
- 点对点发现 使用NetworkBrowser和Wi-Fi Aware设置点对点发现
- 性能优化 利用用户态网络、批量发送、 pacing优化性能
- 迁移到async/await 从NWConnection的完成处理程序迁移到NetworkConnection的async/await
API Evolution
API演进
Timeline
时间线
| Year | iOS Version | Key Features |
|---|---|---|
| 2018 | iOS 12 | NWConnection, NWListener, NWBrowser introduced |
| 2019 | iOS 13 | User-space networking (30% CPU reduction), TLS 1.3 default |
| 2021 | iOS 15 | WebSocket support in URLSession |
| 2025 | iOS 26 | NetworkConnection (async/await), TLV framing, Coder protocol, Wi-Fi Aware |
| 年份 | iOS版本 | 核心特性 |
|---|---|---|
| 2018 | iOS 12 | 推出NWConnection、NWListener、NWBrowser |
| 2019 | iOS 13 | 用户态网络(CPU占用降低30%),默认启用TLS 1.3 |
| 2021 | iOS 15 | URLSession支持WebSocket |
| 2025 | iOS 26 | NetworkConnection(async/await)、TLV帧、Coder协议、Wi-Fi Aware |
NWConnection (iOS 12-25) vs NetworkConnection (iOS 26+)
NWConnection(iOS 12-25)与NetworkConnection(iOS 26+)对比
| Feature | NWConnection (iOS 12-25) | NetworkConnection (iOS 26+) |
|---|---|---|
| Async model | Completion handlers | async/await structured concurrency |
| State updates | | |
| Send | | |
| Receive | | |
| Framing | Manual or custom NWFramer | TLV built-in ( |
| Codable | Manual JSON encode/decode | Coder protocol ( |
| Memory | Requires | No |
| Error handling | Check error in completion | |
| State machine | Callbacks on state changes | |
| Discovery | NWBrowser (Bonjour only) | NetworkBrowser (Bonjour + Wi-Fi Aware) |
| 特性 | NWConnection(iOS 12-25) | NetworkConnection(iOS 26+) |
|---|---|---|
| 异步模型 | 完成处理程序 | async/await结构化并发 |
| 状态更新 | | |
| 发送数据 | | |
| 接收数据 | | |
| 帧处理 | 手动或自定义NWFramer | 内置TLV( |
| 可编码支持 | 手动JSON编解码 | Coder协议( |
| 内存管理 | 所有闭包中需使用 | 无需 |
| 错误处理 | 在完成处理程序中检查错误 | |
| 状态机 | 状态变化时触发回调 | |
| 发现功能 | NWBrowser(仅支持Bonjour) | NetworkBrowser(支持Bonjour + Wi-Fi Aware) |
Recommendation
推荐方案
- New apps targeting iOS 26+: Use NetworkConnection (cleaner, safer)
- Apps supporting iOS 12-25: Use NWConnection (backward compatible)
- Migration: Both APIs coexist, migrate incrementally
- 针对iOS 26+的新应用:使用NetworkConnection(代码更简洁、安全)
- 支持iOS 12-25的应用:使用NWConnection(向后兼容)
- 迁移:两个API可共存,可逐步迁移
NetworkConnection (iOS 26+) Complete Reference
NetworkConnection(iOS 26+)完整参考
4.1 Creating Connections
4.1 创建连接
NetworkConnection uses declarative protocol stack composition.
NetworkConnection使用声明式协议栈组合。
Example 1: Basic TLS Connection (WWDC 4:04)
示例1:基础TLS连接(WWDC 4:04)
swift
import Network
// Basic connection with TLS (TCP and IP inferred)
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLS()
}
// Send and receive with async/await
public func sendAndReceiveWithTLS() async throws {
let outgoingData = Data("Hello, world!".utf8)
try await connection.send(outgoingData)
let incomingData = try await connection.receive(exactly: 98).content
print("Received data: \(incomingData)")
}swift
import Network
// Basic connection with TLS (TCP and IP inferred)
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLS()
}
// Send and receive with async/await
public func sendAndReceiveWithTLS() async throws {
let outgoingData = Data("Hello, world!".utf8)
try await connection.send(outgoingData)
let incomingData = try await connection.receive(exactly: 98).content
print("Received data: \(incomingData)")
}Key points
核心要点
- infers
TLS()andTCP()automaticallyIP() - No explicit connection.start() needed (happens on first send/receive)
- Async/await eliminates callback nesting
- 会自动推断
TLS()和TCP()IP() - 无需显式调用connection.start()(首次发送/接收时自动启动)
- Async/await消除了回调嵌套
Example 2: Custom IP Options (WWDC 4:41)
示例2:自定义IP选项(WWDC 4:41)
swift
// Customize IP fragmentation
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLS {
TCP {
IP()
.fragmentationEnabled(false) // Disable IP fragmentation
}
}
}swift
// Customize IP fragmentation
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLS {
TCP {
IP()
.fragmentationEnabled(false) // Disable IP fragmentation
}
}
}When to customize IP
何时自定义IP
- — For protocols that handle fragmentation themselves (QUIC)
.fragmentationEnabled(false) - — Force IPv6 only (testing)
.ipVersion(.v6)
- — 适用于自行处理分片的协议(如QUIC)
.fragmentationEnabled(false) - — 强制仅使用IPv6(测试场景)
.ipVersion(.v6)
Example 3: Custom Parameters (WWDC 5:07)
示例3:自定义参数(WWDC 5:07)
swift
// Constrained paths (low data mode) + custom IP
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029),
using: .parameters {
TLS {
TCP {
IP()
.fragmentationEnabled(false)
}
}
}
.constrainedPathsProhibited(true) // Don't use cellular in low data mode
)swift
// Constrained paths (low data mode) + custom IP
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029),
using: .parameters {
TLS {
TCP {
IP()
.fragmentationEnabled(false)
}
}
}
.constrainedPathsProhibited(true) // Don't use cellular in low data mode
)Common parameters
常见参数
- — Respect low data mode
.constrainedPathsProhibited(true) - — Don't use cellular/hotspot
.expensivePathsProhibited(true) - — Enable Multipath TCP
.multipathServiceType(.handover)
- — 遵守低数据模式
.constrainedPathsProhibited(true) - — 不使用蜂窝网络/热点
.expensivePathsProhibited(true) - — 启用多路径TCP
.multipathServiceType(.handover)
Endpoint Types
端点类型
swift
// Host + Port
.hostPort(host: "example.com", port: 443)
// Service (Bonjour)
.service(name: "MyPrinter", type: "_ipp._tcp", domain: "local.", interface: nil)
// Unix domain socket
.unix(path: "/tmp/my.sock")swift
// Host + Port
.hostPort(host: "example.com", port: 443)
// Service (Bonjour)
.service(name: "MyPrinter", type: "_ipp._tcp", domain: "local.", interface: nil)
// Unix domain socket
.unix(path: "/tmp/my.sock")Protocol Stack Composition
协议栈组合
swift
// TLS over TCP (most common)
TLS()
// QUIC (TLS + UDP, multiplexed streams)
QUIC()
// UDP (datagrams)
UDP()
// TCP (stream, no encryption)
TCP()
// WebSocket over TLS
WebSocket {
TLS()
}
// Custom framing
TLV {
TLS()
}swift
// TLS over TCP (most common)
TLS()
// QUIC (TLS + UDP, multiplexed streams)
QUIC()
// UDP (datagrams)
UDP()
// TCP (stream, no encryption)
TCP()
// WebSocket over TLS
WebSocket {
TLS()
}
// Custom framing
TLV {
TLS()
}4.2 State Machine
4.2 状态机
NetworkConnection transitions through these states:
setup
↓
preparing (DNS, TCP handshake, TLS handshake)
↓
┌─ waiting (no network, retrying)
│ ↓
└→ ready (can send/receive)
↓
failed (error) or cancelledNetworkConnection会经历以下状态转换:
setup
↓
preparing (DNS, TCP握手, TLS握手)
↓
┌─ waiting (无网络,重试中)
│ ↓
└→ ready (可发送/接收数据)
↓
failed (错误) 或 cancelledMonitoring States
监控状态
swift
// Option 1: Async sequence (monitor in background)
Task {
for await state in connection.states {
switch state {
case .preparing:
print("Connecting...")
case .waiting(let error):
print("Waiting for network: \(error)")
case .ready:
print("Connected!")
case .failed(let error):
print("Failed: \(error)")
case .cancelled:
print("Cancelled")
@unknown default:
break
}
}
}swift
// Option 1: Async sequence (monitor in background)
Task {
for await state in connection.states {
switch state {
case .preparing:
print("Connecting...")
case .waiting(let error):
print("Waiting for network: \(error)")
case .ready:
print("Connected!")
case .failed(let error):
print("Failed: \(error)")
case .cancelled:
print("Cancelled")
@unknown default:
break
}
}
}Key states
核心状态
- .preparing DNS lookup, TCP SYN, TLS handshake
- .waiting No network available, framework retries automatically
- .ready Connection established, can send/receive
- .failed Unrecoverable error (server refused, TLS failed, timeout)
- .cancelled Task cancelled or connection.cancel() called
- .preparing DNS查询、TCP SYN、TLS握手
- .waiting 无可用网络,框架自动重试
- .ready 连接已建立,可发送/接收数据
- .failed 不可恢复错误(服务器拒绝、TLS失败、超时)
- .cancelled 任务已取消或调用了connection.cancel()
4.3 Send/Receive Patterns
4.3 发送/接收模式
Send: Basic
发送:基础用法
swift
let data = Data("Hello".utf8)
try await connection.send(data)swift
let data = Data("Hello".utf8)
try await connection.send(data)Receive: Exact Byte Count (WWDC 7:30)
接收:指定字节数(WWDC 7:30)
swift
// Receive exactly 98 bytes
let incomingData = try await connection.receive(exactly: 98).content
print("Received \(incomingData.count) bytes")swift
// Receive exactly 98 bytes
let incomingData = try await connection.receive(exactly: 98).content
print("Received \(incomingData.count) bytes")Receive: Variable Length (WWDC 8:29)
接收:可变长度(WWDC 8:29)
swift
// Read UInt32 length prefix, then read that many bytes
let remaining32 = try await connection.receive(as: UInt32.self).content
guard var remaining = Int(exactly: remaining32) else { throw MyError.invalidLength }
while remaining > 0 {
let chunk = try await connection.receive(atLeast: 1, atMost: remaining).content
remaining -= chunk.count
// Process chunk...
}swift
// Read UInt32 length prefix, then read that many bytes
let remaining32 = try await connection.receive(as: UInt32.self).content
guard var remaining = Int(exactly: remaining32) else { throw MyError.invalidLength }
while remaining > 0 {
let chunk = try await connection.receive(atLeast: 1, atMost: remaining).content
remaining -= chunk.count
// Process chunk...
}receive() variants
receive()变体
- — Wait for exactly n bytes
receive(exactly: n) - — Get between min and max bytes
receive(atLeast: min, atMost: max) - — Read fixed-size type (network byte order)
receive(as: UInt32.self)
- — 等待接收恰好n字节
receive(exactly: n) - — 接收介于min和max之间的字节
receive(atLeast: min, atMost: max) - — 读取固定大小类型(网络字节序)
receive(as: UInt32.self)
4.4 TLV Framing (iOS 26+)
4.4 TLV帧(iOS 26+)
TLV (Type-Length-Value) solves message boundary problem on stream protocols (TCP/TLS).
TLV(Type-Length-Value) 解决了流协议(TCP/TLS)中的消息边界问题。
Format
格式
- Type: UInt32 (message identifier)
- Length: UInt32 (message size, automatic)
- Value: Message bytes
- Type: UInt32(消息标识符)
- Length: UInt32(消息大小,自动计算)
- Value: 消息字节
Example: GameMessage with TLV (WWDC 11:06, 11:24, 11:53)
示例:带TLV的GameMessage(WWDC 11:06, 11:24, 11:53)
swift
import Network
// Define message types
enum GameMessage: Int {
case selectedCharacter = 0
case move = 1
}
struct GameCharacter: Codable {
let character: String
}
struct GameMove: Codable {
let row: Int
let column: Int
}
// Connection with TLV framing
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLV {
TLS()
}
}
// Send typed message
public func sendWithTLV() async throws {
let characterData = try JSONEncoder().encode(GameCharacter(character: "🐨"))
try await connection.send(characterData, type: GameMessage.selectedCharacter.rawValue)
}
// Receive typed message
public func receiveWithTLV() async throws {
let (incomingData, metadata) = try await connection.receive()
switch GameMessage(rawValue: metadata.type) {
case .selectedCharacter:
let character = try JSONDecoder().decode(GameCharacter.self, from: incomingData)
print("Character selected: \(character)")
case .move:
let move = try JSONDecoder().decode(GameMove.self, from: incomingData)
print("Move: row=\(move.row), column=\(move.column)")
case .none:
print("Unknown message type: \(metadata.type)")
}
}swift
import Network
// Define message types
enum GameMessage: Int {
case selectedCharacter = 0
case move = 1
}
struct GameCharacter: Codable {
let character: String
}
struct GameMove: Codable {
let row: Int
let column: Int
}
// Connection with TLV framing
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
TLV {
TLS()
}
}
// Send typed message
public func sendWithTLV() async throws {
let characterData = try JSONEncoder().encode(GameCharacter(character: "🐨"))
try await connection.send(characterData, type: GameMessage.selectedCharacter.rawValue)
}
// Receive typed message
public func receiveWithTLV() async throws {
let (incomingData, metadata) = try await connection.receive()
switch GameMessage(rawValue: metadata.type) {
case .selectedCharacter:
let character = try JSONDecoder().decode(GameCharacter.self, from: incomingData)
print("Character selected: \(character)")
case .move:
let move = try JSONDecoder().decode(GameMove.self, from: incomingData)
print("Move: row=\(move.row), column=\(move.column)")
case .none:
print("Unknown message type: \(metadata.type)")
}
}Benefits
优势
- Message boundaries preserved (send 3 messages → receive exactly 3)
- Type-safe message handling (enum-based routing)
- Minimal overhead (8 bytes per message: type + length)
- 保留消息边界(发送3条消息 → 恰好接收3条)
- 类型安全的消息处理(基于枚举路由)
- 开销极小(每条消息仅8字节:类型 + 长度)
When to use
适用场景
- Mixed message types (chat + presence + typing)
- Existing protocols using TLV
- Need message boundaries without heavy framing
- 混合消息类型(聊天 + 在线状态 + 输入状态)
- 现有协议使用TLV
- 需要消息边界但不希望使用复杂帧处理
4.5 Coder Protocol (iOS 26+)
4.5 Coder协议(iOS 26+)
Coder eliminates manual JSON encoding/decoding boilerplate.
Coder 消除了手动JSON编解码的样板代码。
Example: GameMessage with Coder (WWDC 12:50, 13:13, 13:53)
示例:带Coder的GameMessage(WWDC 12:50, 13:13, 13:53)
swift
import Network
// Define message types as Codable enum
enum GameMessage: Codable {
case selectedCharacter(String)
case move(row: Int, column: Int)
}
// Connection with Coder
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
// Send Codable directly (no encoding needed!)
public func sendWithCoder() async throws {
let selectedCharacter: GameMessage = .selectedCharacter("🐨")
try await connection.send(selectedCharacter)
}
// Receive Codable directly (no decoding needed!)
public func receiveWithCoder() async throws {
let gameMessage = try await connection.receive().content // Returns GameMessage!
switch gameMessage {
case .selectedCharacter(let character):
print("Character selected: \(character)")
case .move(let row, let column):
print("Move: (\(row), \(column))")
}
}swift
import Network
// Define message types as Codable enum
enum GameMessage: Codable {
case selectedCharacter(String)
case move(row: Int, column: Int)
}
// Connection with Coder
let connection = NetworkConnection(
to: .hostPort(host: "www.example.com", port: 1029)
) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
// Send Codable directly (no encoding needed!)
public func sendWithCoder() async throws {
let selectedCharacter: GameMessage = .selectedCharacter("🐨")
try await connection.send(selectedCharacter)
}
// Receive Codable directly (no decoding needed!)
public func receiveWithCoder() async throws {
let gameMessage = try await connection.receive().content // Returns GameMessage!
switch gameMessage {
case .selectedCharacter(let character):
print("Character selected: \(character)")
case .move(let row, let column):
print("Move: (\(row), \(column))")
}
}Supported formats
支持的格式
- — JSON encoding (human-readable, widely compatible)
.json - — Property list (faster, smaller)
.propertyList
- — JSON编码(人类可读,兼容性广)
.json - — 属性列表(速度更快,体积更小)
.propertyList
Benefits
优势
- No JSON boilerplate (~50 lines → ~10 lines)
- Type-safe (compiler catches message structure changes)
- Automatic framing (handles message boundaries)
- 无JSON样板代码(约50行 → 约10行)
- 类型安全(编译器可捕获消息结构变化)
- 自动帧处理(处理消息边界)
When to use
适用场景
- App-to-app communication (you control both ends)
- Prototyping (fastest time to working code)
- Type-safe protocols
- 应用间通信(你控制两端代码)
- 原型开发(最快实现可运行代码)
- 类型安全协议
When NOT to use
不适用场景
- Interoperating with non-Swift servers
- Need custom wire format
- Performance-critical (prefer manual encoding for control)
- 与非Swift服务器交互
- 需要自定义有线格式
- 性能关键场景(优先使用手动编码以获得控制权)
4.6 NetworkListener (iOS 26+)
4.6 NetworkListener(iOS 26+)
Listen for incoming connections with automatic subtask management.
监听传入连接,自动管理子任务。
Example: Listening for Connections (WWDC 15:16)
示例:监听连接(WWDC 15:16)
swift
import Network
// Listener with Coder protocol
public func listenForIncomingConnections() async throws {
try await NetworkListener {
Coder(GameMessage.self, using: .json) {
TLS()
}
}.run { connection in
// Each connection gets its own subtask
for try await (gameMessage, _) in connection.messages {
switch gameMessage {
case .selectedCharacter(let character):
print("Player chose: \(character)")
case .move(let row, let column):
print("Player moved: (\(row), \(column))")
}
}
}
}swift
import Network
// Listener with Coder protocol
public func listenForIncomingConnections() async throws {
try await NetworkListener {
Coder(GameMessage.self, using: .json) {
TLS()
}
}.run { connection in
// Each connection gets its own subtask
for try await (gameMessage, _) in connection.messages {
switch gameMessage {
case .selectedCharacter(let character):
print("Player chose: \(character)")
case .move(let row, let column):
print("Player moved: (\(row), \(column))")
}
}
}
}Key features
核心特性
- Automatic subtask per connection (no manual Task management)
- Structured concurrency (all subtasks cancelled when listener exits)
- async sequence for receiving
connection.messages
- 每个连接自动分配子任务(无需手动管理Task)
- 结构化并发(监听器退出时所有子任务自动取消)
- 异步序列用于接收数据
connection.messages
Listener configuration
监听器配置
swift
// Specify port
NetworkListener(port: 1029) { TLS() }
// Let system choose port
NetworkListener { TLS() }
// Bonjour advertising
NetworkListener(service: .init(name: "MyApp", type: "_myapp._tcp")) { TLS() }swift
// Specify port
NetworkListener(port: 1029) { TLS() }
// Let system choose port
NetworkListener { TLS() }
// Bonjour advertising
NetworkListener(service: .init(name: "MyApp", type: "_myapp._tcp")) { TLS() }4.7 NetworkBrowser & Wi-Fi Aware (iOS 26+)
4.7 NetworkBrowser & Wi-Fi Aware(iOS 26+)
Discover endpoints on local network or nearby devices.
发现本地网络或附近设备的端点。
Example: Wi-Fi Aware Discovery (WWDC 17:39)
示例:Wi-Fi Aware发现(WWDC 17:39)
swift
import Network
import WiFiAware
// Browse for nearby paired Wi-Fi Aware devices
public func findNearbyDevice() async throws {
let endpoint = try await NetworkBrowser(
for: .wifiAware(.connecting(to: .allPairedDevices, from: .ticTacToeService))
).run { endpoints in
.finish(endpoints.first!) // Use first discovered device
}
// Make connection to the discovered endpoint
let connection = NetworkConnection(to: endpoint) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
}swift
import Network
import WiFiAware
// Browse for nearby paired Wi-Fi Aware devices
public func findNearbyDevice() async throws {
let endpoint = try await NetworkBrowser(
for: .wifiAware(.connecting(to: .allPairedDevices, from: .ticTacToeService))
).run { endpoints in
.finish(endpoints.first!) // Use first discovered device
}
// Make connection to the discovered endpoint
let connection = NetworkConnection(to: endpoint) {
Coder(GameMessage.self, using: .json) {
TLS()
}
}
}Wi-Fi Aware features
Wi-Fi Aware特性
- Peer-to-peer without infrastructure (no WiFi router needed)
- Automatic discovery of paired devices
- Low latency, axiom-high throughput
- iOS 26+ only
- 无需基础设施的点对点通信(无需WiFi路由器)
- 自动发现配对设备
- 低延迟、高吞吐量
- 仅支持iOS 26+
Browse descriptors
浏览描述符
swift
// Bonjour
.bonjour(type: "_http._tcp", domain: "local")
// Wi-Fi Aware (all paired devices)
.wifiAware(.connecting(to: .allPairedDevices, from: .myService))
// Wi-Fi Aware (specific device)
.wifiAware(.connecting(to: .pairedDevice(identifier: deviceID), from: .myService))swift
// Bonjour
.bonjour(type: "_http._tcp", domain: "local")
// Wi-Fi Aware (all paired devices)
.wifiAware(.connecting(to: .allPairedDevices, from: .myService))
// Wi-Fi Aware (specific device)
.wifiAware(.connecting(to: .pairedDevice(identifier: deviceID), from: .myService))NWConnection (iOS 12-25) Complete Reference
NWConnection(iOS 12-25)完整参考
5.1 Creating Connections
5.1 创建连接
NWConnection uses completion handlers (pre-async/await).
NWConnection使用完成处理程序(async/await之前的异步模型)。
Basic TLS Connection (WWDC 2018 lines 133-166)
基础TLS连接(WWDC 2018 lines 133-166)
swift
import Network
// Create connection
let connection = NWConnection(
host: NWEndpoint.Host("mail.example.com"),
port: NWEndpoint.Port(integerLiteral: 993),
using: .tls // TCP inferred
)
// Handle connection state changes
connection.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
print("Connection established")
self?.sendData()
case .waiting(let error):
print("Waiting for network: \(error)")
// Show "Waiting..." UI, don't fail immediately
case .failed(let error):
print("Connection failed: \(error)")
case .cancelled:
print("Connection cancelled")
default:
break
}
}
// Start connection
connection.start(queue: .main)Critical Always use in stateUpdateHandler to prevent retain cycles.
[weak self]swift
import Network
// Create connection
let connection = NWConnection(
host: NWEndpoint.Host("mail.example.com"),
port: NWEndpoint.Port(integerLiteral: 993),
using: .tls // TCP inferred
)
// Handle connection state changes
connection.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
print("Connection established")
self?.sendData()
case .waiting(let error):
print("Waiting for network: \(error)")
// Show "Waiting..." UI, don't fail immediately
case .failed(let error):
print("Connection failed: \(error)")
case .cancelled:
print("Connection cancelled")
default:
break
}
}
// Start connection
connection.start(queue: .main)关键注意事项 始终在stateUpdateHandler中使用以避免循环引用。
[weak self]Custom Parameters
自定义参数
swift
// Create custom parameters
let parameters = NWParameters.tls
// Prohibit expensive networks
parameters.prohibitExpensivePaths = true // Don't use cellular/hotspot
// Prohibit constrained networks
parameters.prohibitConstrainedPaths = true // Respect low data mode
// Require IPv6
parameters.requiredInterfaceType = .wifi
parameters.ipOptions.version = .v6
let connection = NWConnection(host: "example.com", port: 443, using: parameters)swift
// Create custom parameters
let parameters = NWParameters.tls
// Prohibit expensive networks
parameters.prohibitExpensivePaths = true // Don't use cellular/hotspot
// Prohibit constrained networks
parameters.prohibitConstrainedPaths = true // Respect low data mode
// Require IPv6
parameters.requiredInterfaceType = .wifi
parameters.ipOptions.version = .v6
let connection = NWConnection(host: "example.com", port: 443, using: parameters)5.2 State Handling
5.2 状态处理
NWConnection state machine (same as NetworkConnection):
setup → preparing → waiting/ready → failed/cancelledNWConnection状态机(与NetworkConnection相同):
setup → preparing → waiting/ready → failed/cancelledState handling best practices
状态处理最佳实践
swift
connection.stateUpdateHandler = { [weak self] state in
guard let self = self else { return }
switch state {
case .preparing:
// DNS lookup, TCP SYN, TLS handshake in progress
self.updateUI(.connecting)
case .waiting(let error):
// Network unavailable or blocked
// DON'T fail immediately, framework retries automatically
print("Waiting: \(error.localizedDescription)")
self.updateUI(.waiting)
case .ready:
// Connection established, can send/receive
self.updateUI(.connected)
self.startSending()
case .failed(let error):
// Unrecoverable error after all retry attempts
print("Failed: \(error.localizedDescription)")
self.updateUI(.failed)
case .cancelled:
// connection.cancel() called
self.updateUI(.disconnected)
default:
break
}
}swift
connection.stateUpdateHandler = { [weak self] state in
guard let self = self else { return }
switch state {
case .preparing:
// DNS lookup, TCP SYN, TLS handshake in progress
self.updateUI(.connecting)
case .waiting(let error):
// Network unavailable or blocked
// DON'T fail immediately, framework retries automatically
print("Waiting: \(error.localizedDescription)")
self.updateUI(.waiting)
case .ready:
// Connection established, can send/receive
self.updateUI(.connected)
self.startSending()
case .failed(let error):
// Unrecoverable error after all retry attempts
print("Failed: \(error.localizedDescription)")
self.updateUI(.failed)
case .cancelled:
// connection.cancel() called
self.updateUI(.disconnected)
default:
break
}
}5.3 Send/Receive with Callbacks
5.3 带回调的发送/接收
Send with Pacing (WWDC 2018 lines 320-341)
带Pacing的发送(WWDC 2018 lines 320-341)
swift
// Send with contentProcessed callback for pacing
func sendData() {
let data = Data("Hello, world!".utf8)
connection.send(content: data, completion: .contentProcessed { [weak self] error in
if let error = error {
print("Send error: \(error)")
return
}
// contentProcessed = network stack consumed data
// NOW send next chunk (pacing)
self?.sendNextData()
})
}contentProcessed callback Invoked when network stack consumes your data (equivalent to when blocking socket call would return). Use this for pacing to avoid buffering excessive data.
swift
// Send with contentProcessed callback for pacing
func sendData() {
let data = Data("Hello, world!".utf8)
connection.send(content: data, completion: .contentProcessed { [weak self] error in
if let error = error {
print("Send error: \(error)")
return
}
// contentProcessed = network stack consumed data
// NOW send next chunk (pacing)
self?.sendNextData()
})
}contentProcessed回调 当网络栈消耗完你的数据时触发(相当于阻塞套接字调用返回的时机)。使用此回调进行pacing以避免缓冲过多数据。
Receive with Exact Byte Count
接收指定字节数
swift
// Receive exactly 10 bytes
connection.receive(minimumIncompleteLength: 10, maximumLength: 10) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
print("Received \(data.count) bytes")
// Process data...
// Continue receiving
self?.receiveMore()
}
}swift
// Receive exactly 10 bytes
connection.receive(minimumIncompleteLength: 10, maximumLength: 10) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
print("Received \(data.count) bytes")
// Process data...
// Continue receiving
self?.receiveMore()
}
}Receive parameters
接收参数
- : Minimum bytes before callback (1 = return any data)
minimumIncompleteLength - : Maximum bytes per callback
maximumLength - For "exactly n bytes": Set both to n
- : 触发回调的最小字节数(1 = 返回任意数据)
minimumIncompleteLength - : 每次回调返回的最大字节数
maximumLength - 若要“恰好接收n字节”:将两者都设置为n
5.4 UDP Batching (WWDC 2018 lines 343-347)
5.4 UDP批量发送(WWDC 2018 lines 343-347)
Batch sending for 30% CPU reduction.
批量发送可降低30% CPU占用。
swift
// UDP connection
let connection = NWConnection(
host: NWEndpoint.Host("game-server.example.com"),
port: NWEndpoint.Port(integerLiteral: 9000),
using: .udp
)
connection.start(queue: .main)
// Batch multiple datagrams
func sendVideoFrames(_ frames: [Data]) {
connection.batch {
for frame in frames {
connection.send(content: frame, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
}
}
// All sends batched into ~1 syscall
// Result: 30% lower CPU usage vs individual sends
}Without batch 100 datagrams = 100 syscalls = high CPU
With batch 100 datagrams = ~1 syscall = 30% lower CPU (measured with Instruments)
swift
// UDP connection
let connection = NWConnection(
host: NWEndpoint.Host("game-server.example.com"),
port: NWEndpoint.Port(integerLiteral: 9000),
using: .udp
)
connection.start(queue: .main)
// Batch multiple datagrams
func sendVideoFrames(_ frames: [Data]) {
connection.batch {
for frame in frames {
connection.send(content: frame, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
}
}
// All sends batched into ~1 syscall
// Result: 30% lower CPU usage vs individual sends
}不使用批量发送 100个数据报 = 100次系统调用 = 高CPU占用
使用批量发送 100个数据报 = ~1次系统调用 = CPU占用降低30%(通过Instruments测量)
5.5 NWListener (WWDC 2018 lines 233-293)
5.5 NWListener(WWDC 2018 lines 233-293)
Accept incoming connections.
swift
import Network
// Create listener on port 1029
let listener = try NWListener(using: .tcp, on: 1029)
// Advertise Bonjour service
listener.service = NWListener.Service(name: "MyApp", type: "_myapp._tcp")
// Handle service registration
listener.serviceRegistrationUpdateHandler = { update in
switch update {
case .add(let endpoint):
if case .service(let name, let type, let domain, _) = endpoint {
print("Advertising: \(name).\(type)\(domain)")
}
default:
break
}
}
// Handle new connections
listener.newConnectionHandler = { [weak self] newConnection in
print("New connection from: \(newConnection.endpoint)")
newConnection.stateUpdateHandler = { state in
if case .ready = state {
print("Client connected")
self?.handleClient(newConnection)
}
}
newConnection.start(queue: .main)
}
// Handle listener state
listener.stateUpdateHandler = { state in
switch state {
case .ready:
print("Listener ready on port \(listener.port ?? 0)")
case .failed(let error):
print("Listener failed: \(error)")
default:
break
}
}
// Start listening
listener.start(queue: .main)接受传入连接。
swift
import Network
// Create listener on port 1029
let listener = try NWListener(using: .tcp, on: 1029)
// Advertise Bonjour service
listener.service = NWListener.Service(name: "MyApp", type: "_myapp._tcp")
// Handle service registration
listener.serviceRegistrationUpdateHandler = { update in
switch update {
case .add(let endpoint):
if case .service(let name, let type, let domain, _) = endpoint {
print("Advertising: \(name).\(type)\(domain)")
}
default:
break
}
}
// Handle new connections
listener.newConnectionHandler = { [weak self] newConnection in
print("New connection from: \(newConnection.endpoint)")
newConnection.stateUpdateHandler = { state in
if case .ready = state {
print("Client connected")
self?.handleClient(newConnection)
}
}
newConnection.start(queue: .main)
}
// Handle listener state
listener.stateUpdateHandler = { state in
switch state {
case .ready:
print("Listener ready on port \(listener.port ?? 0)")
case .failed(let error):
print("Listener failed: \(error)")
default:
break
}
}
// Start listening
listener.start(queue: .main)5.6 NWBrowser (Bonjour Discovery)
5.6 NWBrowser(Bonjour发现)
Discover services on local network.
swift
import Network
// Browse for Bonjour services
let browser = NWBrowser(
for: .bonjour(type: "_http._tcp", domain: nil),
using: .tcp
)
// Handle discovered services
browser.browseResultsChangedHandler = { results, changes in
for result in results {
switch result.endpoint {
case .service(let name, let type, let domain, _):
print("Found service: \(name).\(type)\(domain)")
// Connect to this service
let connection = NWConnection(to: result.endpoint, using: .tcp)
connection.start(queue: .main)
default:
break
}
}
}
// Handle browser state
browser.stateUpdateHandler = { state in
switch state {
case .ready:
print("Browser ready")
case .failed(let error):
print("Browser failed: \(error)")
default:
break
}
}
// Start browsing
browser.start(queue: .main)发现本地网络上的服务。
swift
import Network
// Browse for Bonjour services
let browser = NWBrowser(
for: .bonjour(type: "_http._tcp", domain: nil),
using: .tcp
)
// Handle discovered services
browser.browseResultsChangedHandler = { results, changes in
for result in results {
switch result.endpoint {
case .service(let name, let type, let domain, _):
print("Found service: \(name).\(type)\(domain)")
// Connect to this service
let connection = NWConnection(to: result.endpoint, using: .tcp)
connection.start(queue: .main)
default:
break
}
}
}
// Handle browser state
browser.stateUpdateHandler = { state in
switch state {
case .ready:
print("Browser ready")
case .failed(let error):
print("Browser failed: \(error)")
default:
break
}
}
// Start browsing
browser.start(queue: .main)Mobility & Network Transitions
移动网络适配与网络切换
Connection Viability (WWDC 2018 lines 453-463)
连接可用性(WWDC 2018 lines 453-463)
Viability = connection can send/receive data (has valid route).
swift
connection.viabilityUpdateHandler = { isViable in
if isViable {
print("✅ Connection viable (can send/receive)")
} else {
print("⚠️ Connection not viable (no route)")
// Don't tear down immediately, may recover
// Show UI: "Connection interrupted"
}
}可用性 = 连接可发送/接收数据(有有效路由)。
swift
connection.viabilityUpdateHandler = { isViable in
if isViable {
print("✅ Connection viable (can send/receive)")
} else {
print("⚠️ Connection not viable (no route)")
// Don't tear down immediately, may recover
// Show UI: "Connection interrupted"
}
}When viability changes
可用性变化场景
- Walk into elevator (WiFi signal lost) → not viable
- Walk out of elevator (WiFi returns) → viable again
- Switch WiFi → cellular → not viable briefly → viable on cellular
Best practice Don't tear down connection on viability loss. Framework will recover when network returns.
- 走进电梯(WiFi信号丢失)→ 不可用
- 走出电梯(WiFi恢复)→ 再次可用
- 从WiFi切换到蜂窝网络 → 短暂不可用 → 蜂窝网络可用
最佳实践 不要在可用性丢失时立即断开连接。框架会在网络恢复时自动恢复连接。
Better Path Available (WWDC 2018 lines 464-477)
更优路径可用(WWDC 2018 lines 464-477)
Better path = alternative network with better characteristics.
swift
connection.betterPathUpdateHandler = { betterPathAvailable in
if betterPathAvailable {
print("📶 Better path available (e.g., WiFi while on cellular)")
// Consider migrating to new connection
self.migrateToNewConnection()
}
}更优路径 = 具有更好特性的替代网络。
swift
connection.betterPathUpdateHandler = { betterPathAvailable in
if betterPathAvailable {
print("📶 Better path available (e.g., WiFi while on cellular)")
// Consider migrating to new connection
self.migrateToNewConnection()
}
}Scenarios
场景
- Connected on cellular, walk into building with WiFi → better path available
- Connected on WiFi, WiFi quality degrades, cellular available → better path available
- 连接蜂窝网络,走进有WiFi的建筑 → 更优路径可用
- 连接WiFi,WiFi质量下降,蜂窝网络可用 → 更优路径可用
Migration pattern
迁移模式
swift
func migrateToNewConnection() {
// Create new connection
let newConnection = NWConnection(host: host, port: port, using: parameters)
newConnection.stateUpdateHandler = { [weak self] state in
if case .ready = state {
// New connection ready, switch over
self?.currentConnection?.cancel()
self?.currentConnection = newConnection
}
}
newConnection.start(queue: .main)
// Keep old connection until new one ready
}swift
func migrateToNewConnection() {
// Create new connection
let newConnection = NWConnection(host: host, port: port, using: parameters)
newConnection.stateUpdateHandler = { [weak self] state in
if case .ready = state {
// New connection ready, switch over
self?.currentConnection?.cancel()
self?.currentConnection = newConnection
}
}
newConnection.start(queue: .main)
// Keep old connection until new one ready
}Multipath TCP (WWDC 2018 lines 480-487)
多路径TCP(WWDC 2018 lines 480-487)
Automatically migrate between networks without application intervention.
swift
let parameters = NWParameters.tcp
parameters.multipathServiceType = .handover // Seamless network transition
let connection = NWConnection(host: "example.com", port: 443, using: parameters)无需应用干预,自动在网络间迁移。
swift
let parameters = NWParameters.tcp
parameters.multipathServiceType = .handover // Seamless network transition
let connection = NWConnection(host: "example.com", port: 443, using: parameters)Multipath TCP modes
多路径TCP模式
- — Seamless handoff between networks (WiFi ↔ cellular)
.handover - — Use multiple paths simultaneously (lowest latency)
.interactive - — Use multiple paths simultaneously (highest throughput)
.aggregate
- — 网络间无缝切换(WiFi ↔ 蜂窝网络)
.handover - — 同时使用多个路径(最低延迟)
.interactive - — 同时使用多个路径(最高吞吐量)
.aggregate
Benefits
优势
- Automatic network transition (no viability handlers needed)
- No connection interruption when switching networks
- Fallback to single-path if MPTCP unavailable
- 自动网络切换(无需可用性处理程序)
- 切换网络时连接不中断
- 若MPTCP不可用,回退到单路径
NWPathMonitor (WWDC 2018 lines 489-496)
NWPathMonitor(WWDC 2018 lines 489-496)
Monitor network state changes (replaces SCNetworkReachability).
swift
import Network
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
print("✅ Network available")
// Check interface types
if path.usesInterfaceType(.wifi) {
print("Using WiFi")
} else if path.usesInterfaceType(.cellular) {
print("Using cellular")
}
// Check if expensive
if path.isExpensive {
print("⚠️ Expensive path (cellular/hotspot)")
}
} else {
print("❌ No network")
}
}
monitor.start(queue: .main)监控网络状态变化(替代SCNetworkReachability)。
swift
import Network
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
print("✅ Network available")
// Check interface types
if path.usesInterfaceType(.wifi) {
print("Using WiFi")
} else if path.usesInterfaceType(.cellular) {
print("Using cellular")
}
// Check if expensive
if path.isExpensive {
print("⚠️ Expensive path (cellular/hotspot)")
}
} else {
print("❌ No network")
}
}
monitor.start(queue: .main)Use cases
适用场景
- Show "No network" UI when path.status == .unsatisfied
- Disable high-bandwidth features when path.isExpensive
- Adjust quality based on interface type
- 全局网络状态监控
- 当“等待连接”不足以满足需求时
- 需要在连接前了解可用接口
When to use
不适用场景
- Global network state monitoring
- When "waiting for connectivity" isn't enough
- Need to know available interfaces before connecting
- 连接前检查(使用waiting状态替代)
- 单连接监控(使用可用性处理程序替代)
When NOT to use
安全配置
—
TLS版本
- Checking before connecting (use waiting state instead)
- Per-connection monitoring (use viability handlers instead)
swift
// iOS 13+ requires TLS 1.2+ by default
let tlsOptions = NWProtocolTLS.Options()
// Allow TLS 1.2 and 1.3
tlsOptions.minimumTLSProtocolVersion = .TLSv12
// Require TLS 1.3 only
tlsOptions.minimumTLSProtocolVersion = .TLSv13
let parameters = NWParameters(tls: tlsOptions)
let connection = NWConnection(host: "example.com", port: 443, using: parameters)Security Configuration
证书绑定
TLS Version
—
swift
// iOS 13+ requires TLS 1.2+ by default
let tlsOptions = NWProtocolTLS.Options()
// Allow TLS 1.2 and 1.3
tlsOptions.minimumTLSProtocolVersion = .TLSv12
// Require TLS 1.3 only
tlsOptions.minimumTLSProtocolVersion = .TLSv13
let parameters = NWParameters(tls: tlsOptions)
let connection = NWConnection(host: "example.com", port: 443, using: parameters)swift
// Production-grade certificate pinning
let tlsOptions = NWProtocolTLS.Options()
sec_protocol_options_set_verify_block(
tlsOptions.securityProtocolOptions,
{ (metadata, trust, complete) in
// Get server certificate
let serverCert = sec_protocol_metadata_copy_peer_public_key(metadata)
// Compare with pinned certificate
let pinnedCertData = Data(/* your pinned cert */)
let serverCertData = SecCertificateCopyData(serverCert) as Data
if serverCertData == pinnedCertData {
complete(true) // Accept
} else {
complete(false) // Reject (prevents MITM attacks)
}
},
.main
)
let parameters = NWParameters(tls: tlsOptions)Certificate Pinning
证书绑定 + 企业代理
swift
// Production-grade certificate pinning
let tlsOptions = NWProtocolTLS.Options()
sec_protocol_options_set_verify_block(
tlsOptions.securityProtocolOptions,
{ (metadata, trust, complete) in
// Get server certificate
let serverCert = sec_protocol_metadata_copy_peer_public_key(metadata)
// Compare with pinned certificate
let pinnedCertData = Data(/* your pinned cert */)
let serverCertData = SecCertificateCopyData(serverCert) as Data
if serverCertData == pinnedCertData {
complete(true) // Accept
} else {
complete(false) // Reject (prevents MITM attacks)
}
},
.main
)
let parameters = NWParameters(tls: tlsOptions)企业网络通常使用TLS检查代理,这些代理会呈现自己的证书。严格的绑定会导致这些环境下应用无法正常工作。
策略:针对公钥(SPKI)进行绑定,而不是完整证书,并提供配置逃生舱:
swift
sec_protocol_options_set_verify_block(
tlsOptions.securityProtocolOptions,
{ (metadata, trust, complete) in
// 1. Check if system trusts the certificate chain (handles corporate CAs)
let secTrust = sec_trust_copy_ref(trust).takeRetainedValue()
SecTrustEvaluateAsyncWithError(secTrust, .main) { _, result, _ in
guard result else { complete(false); return }
// 2. If pinning enabled, also verify public key
if PinningConfig.isEnabled {
let serverKey = SecTrustCopyKey(secTrust)
let matches = pinnedKeys.contains { $0 == serverKey }
complete(matches)
} else {
complete(true) // System trust only (enterprise mode)
}
}
},
.main
)规则:
- 始终先验证系统信任链()—— 这会尊重企业安装的根CA
SecTrustEvaluateAsyncWithError - 使用公钥绑定而非证书绑定(可在证书轮换时继续工作)
- 提供托管配置(MDM配置文件或应用配置)以在企业环境中禁用绑定
- 至少绑定2个密钥(当前 + 备用)以应对轮换
Certificate Pinning + Corporate Proxies
密码套件
Corporate networks often use TLS inspection proxies that present their own certificates. Strict pinning breaks these environments.
Strategy: Pin against the public key (SPKI) rather than the full certificate, and provide a configuration escape hatch:
swift
sec_protocol_options_set_verify_block(
tlsOptions.securityProtocolOptions,
{ (metadata, trust, complete) in
// 1. Check if system trusts the certificate chain (handles corporate CAs)
let secTrust = sec_trust_copy_ref(trust).takeRetainedValue()
SecTrustEvaluateAsyncWithError(secTrust, .main) { _, result, _ in
guard result else { complete(false); return }
// 2. If pinning enabled, also verify public key
if PinningConfig.isEnabled {
let serverKey = SecTrustCopyKey(secTrust)
let matches = pinnedKeys.contains { $0 == serverKey }
complete(matches)
} else {
complete(true) // System trust only (enterprise mode)
}
}
},
.main
)Rules:
- Always validate system trust first () — this respects enterprise-installed root CAs
SecTrustEvaluateAsyncWithError - Use public key pinning over certificate pinning (survives cert rotation)
- Provide a managed configuration (MDM profile or app config) to disable pinning in enterprise environments
- Pin at least 2 keys (current + backup) to survive rotation
swift
let tlsOptions = NWProtocolTLS.Options()
// Specify allowed cipher suites
tlsOptions.tlsCipherSuites = [
tls_ciphersuite_t(rawValue: 0x1301), // TLS_AES_128_GCM_SHA256
tls_ciphersuite_t(rawValue: 0x1302), // TLS_AES_256_GCM_SHA384
]
// iOS defaults to secure modern ciphers, only customize if requiredCipher Suites
性能优化
—
用户态网络(WWDC 2018 lines 409-441)
swift
let tlsOptions = NWProtocolTLS.Options()
// Specify allowed cipher suites
tlsOptions.tlsCipherSuites = [
tls_ciphersuite_t(rawValue: 0x1301), // TLS_AES_128_GCM_SHA256
tls_ciphersuite_t(rawValue: 0x1302), // TLS_AES_256_GCM_SHA384
]
// iOS defaults to secure modern ciphers, only customize if required在iOS/tvOS上自动启用。Network.framework将TCP/UDP栈移至应用进程中。
Performance Optimization
优势
User-Space Networking (WWDC 2018 lines 409-441)
—
Automatic on iOS/tvOS. Network.framework moves TCP/UDP stack into your app process.
- CPU占用降低约30%(通过Instruments测量)
- 无内核→用户空间拷贝(使用内存映射区域)
- 减少上下文切换
Benefits
传统方式 vs 用户态网络
- ~30% lower CPU usage (measured with Instruments)
- No kernel→userspace copy (memory-mapped regions)
- Reduced context switches
| 传统套接字 | 用户态网络 |
|---|---|
| 数据包 → 驱动 → 内核 → 拷贝 → 用户空间 | 数据包 → 驱动 → 内存映射区域 → 用户空间(无拷贝) |
| 100个数据报 = 100次系统调用 | 100个数据报 = ~1次系统调用(带批量发送) |
| CPU占用高约30% | 基准CPU占用 |
WWDC演示 实时UDP视频流显示了30%的CPU差异(套接字 vs Network.framework)。
Legacy vs User-Space
UDP的ECN(WWDC 2018 lines 365-378)
| Traditional Sockets | User-Space Networking |
|---|---|
| Packet → driver → kernel → copy → userspace | Packet → driver → memory-mapped region → userspace (no copy) |
| 100 datagrams = 100 syscalls | 100 datagrams = ~1 syscall (with batching) |
| ~30% higher CPU | Baseline CPU |
WWDC demo Live UDP video streaming showed 30% CPU difference (sockets vs Network.framework).
显式拥塞通知实现平滑UDP传输。
swift
// Create IP metadata with ECN
let ipMetadata = NWProtocolIP.Metadata()
ipMetadata.ecnFlag = .congestionEncountered // Or .ect0, .ect1
// Attach to send context
let context = NWConnection.ContentContext(
identifier: "video_frame",
metadata: [ipMetadata]
)
connection.send(content: data, contentContext: context, completion: .contentProcessed { _ in })ECN for UDP (WWDC 2018 lines 365-378)
ECN标志
Explicit Congestion Notification for smooth UDP transmission.
swift
// Create IP metadata with ECN
let ipMetadata = NWProtocolIP.Metadata()
ipMetadata.ecnFlag = .congestionEncountered // Or .ect0, .ect1
// Attach to send context
let context = NWConnection.ContentContext(
identifier: "video_frame",
metadata: [ipMetadata]
)
connection.send(content: data, contentContext: context, completion: .contentProcessed { _ in })- /
.ect0— 支持ECN的传输.ect1 - — 收到拥塞通知
.congestionEncountered
优势 网络可在不丢包的情况下发出拥塞信号。
ECN flags
服务类别(WWDC 2018 lines 379-388)
- /
.ect0— ECN-capable transport.ect1 - — Congestion notification received
.congestionEncountered
Benefits Network can signal congestion without dropping packets.
标记流量优先级。
swift
// Connection-wide service class
let parameters = NWParameters.tcp
parameters.serviceClass = .background // Low priority
let connection = NWConnection(host: "example.com", port: 443, using: parameters)
// Per-packet service class (UDP)
let ipMetadata = NWProtocolIP.Metadata()
ipMetadata.serviceClass = .realTimeInteractive // High priority (voice)
let context = NWConnection.ContentContext(identifier: "voip", metadata: [ipMetadata])
connection.send(content: audioData, contentContext: context, completion: .contentProcessed { _ in })Service Class (WWDC 2018 lines 379-388)
服务类别
Mark traffic priority.
swift
// Connection-wide service class
let parameters = NWParameters.tcp
parameters.serviceClass = .background // Low priority
let connection = NWConnection(host: "example.com", port: 443, using: parameters)
// Per-packet service class (UDP)
let ipMetadata = NWProtocolIP.Metadata()
ipMetadata.serviceClass = .realTimeInteractive // High priority (voice)
let context = NWConnection.ContentContext(identifier: "voip", metadata: [ipMetadata])
connection.send(content: audioData, contentContext: context, completion: .contentProcessed { _ in })- — 低优先级(大文件下载、同步)
.background - — 正常优先级
.default - — 交互式数据(API调用)
.responsiveData - — 时间敏感型(语音、游戏)
.realTimeInteractive
Service classes
TCP快速打开(WWDC 2018 lines 389-406)
- — Low priority (large downloads, sync)
.background - — Normal priority
.default - — Interactive data (API calls)
.responsiveData - — Time-sensitive (voice, gaming)
.realTimeInteractive
在TCP SYN包中发送初始数据(节省往返时间)。
swift
let parameters = NWParameters.tcp
parameters.allowFastOpen = true
let connection = NWConnection(host: "example.com", port: 443, using: parameters)
// Send initial data BEFORE calling start()
let initialData = Data("GET / HTTP/1.1\r\n".utf8)
connection.send(
content: initialData,
contentContext: .defaultMessage,
isComplete: false,
completion: .idempotent // Data is safe to replay
)
// Now start connection (initial data sent in SYN)
connection.start(queue: .main)优势 减少连接建立时间1个RTT。
要求 数据必须是幂等的(若SYN重传,数据可安全重放)。
TCP Fast Open (WWDC 2018 lines 389-406)
迁移策略
—
从BSD套接字迁移到NWConnection
Send initial data in TCP SYN packet (saves round trip).
swift
let parameters = NWParameters.tcp
parameters.allowFastOpen = true
let connection = NWConnection(host: "example.com", port: 443, using: parameters)
// Send initial data BEFORE calling start()
let initialData = Data("GET / HTTP/1.1\r\n".utf8)
connection.send(
content: initialData,
contentContext: .defaultMessage,
isComplete: false,
completion: .idempotent // Data is safe to replay
)
// Now start connection (initial data sent in SYN)
connection.start(queue: .main)Benefits Reduces connection establishment time by 1 RTT.
Requirements Data must be idempotent (safe to replay if SYN retransmitted).
| BSD套接字 | NWConnection | 说明 |
|---|---|---|
| | 默认非阻塞 |
| | 异步回调 |
| | 异步回调 |
| | 自动端口绑定 |
| | 每个连接触发回调 |
| 使用 | 自动DNS解析 |
| | 无竞争条件 |
| | 类型安全选项 |
Migration Strategies
迁移示例
From BSD Sockets to NWConnection
迁移前(阻塞套接字)
| BSD Sockets | NWConnection | Notes |
|---|---|---|
| | Non-blocking by default |
| | Async callback |
| | Async callback |
| | Automatic port binding |
| | Callback per connection |
| Use | DNS automatic |
| | No race conditions |
| | Type-safe options |
c
int sock = socket(AF_INET, SOCK_STREAM, 0);
connect(sock, &addr, addrlen); // BLOCKS
send(sock, data, len, 0);Migration example
迁移后(NWConnection)
Before (blocking sockets)
—
c
int sock = socket(AF_INET, SOCK_STREAM, 0);
connect(sock, &addr, addrlen); // BLOCKS
send(sock, data, len, 0);swift
let connection = NWConnection(host: "example.com", port: 443, using: .tls)
connection.stateUpdateHandler = { state in
if case .ready = state {
connection.send(content: data, completion: .contentProcessed { _ in })
}
}
connection.start(queue: .main)After (NWConnection)
从URLSession StreamTask迁移到NetworkConnection
—
何时迁移
swift
let connection = NWConnection(host: "example.com", port: 443, using: .tls)
connection.stateUpdateHandler = { state in
if case .ready = state {
connection.send(content: data, completion: .contentProcessed { _ in })
}
}
connection.start(queue: .main)- 需要UDP支持(StreamTask仅支持TCP)
- 需要自定义协议
- 需要底层控制
From URLSession StreamTask to NetworkConnection
何时保留URLSession
When to migrate
—
- Need UDP (StreamTask only supports TCP)
- Need custom protocols
- Need low-level control
- HTTP/HTTPS(URLSession针对这些场景优化)
- WebSocket支持
- 内置缓存、Cookie
When to STAY with URLSession
迁移示例
—
迁移前(URLSession StreamTask)
- HTTP/HTTPS (URLSession optimized for this)
- WebSocket support
- Built-in caching, cookies
swift
let task = URLSession.shared.streamTask(withHostName: "example.com", port: 443)
task.resume()
task.write(Data("Hello".utf8), timeout: 10) { _ in }Migration example
迁移后(NetworkConnection iOS 26+)
Before (URLSession StreamTask)
—
swift
let task = URLSession.shared.streamTask(withHostName: "example.com", port: 443)
task.resume()
task.write(Data("Hello".utf8), timeout: 10) { _ in }swift
let connection = NetworkConnection(to: .hostPort(host: "example.com", port: 443)) { TLS() }
try await connection.send(Data("Hello".utf8))After (NetworkConnection iOS 26+)
从NWConnection迁移到NetworkConnection
—
迁移优势
swift
let connection = NetworkConnection(to: .hostPort(host: "example.com", port: 443)) { TLS() }
try await connection.send(Data("Hello".utf8))- Async/await(无回调嵌套)
- 无需
[weak self] - 内置TLV帧
- 针对Codable类型的Coder协议
From NWConnection to NetworkConnection
迁移映射
Benefits of migration
—
- Async/await (no callback nesting)
- No needed
[weak self] - TLV framing built-in
- Coder protocol for Codable types
| NWConnection | NetworkConnection |
|---|---|
| |
| |
| |
| 手动JSON编解码 | |
| 自定义帧处理器 | |
处处使用 | 无需 |
Migration mapping
迁移示例
—
迁移前(NWConnection)
| NWConnection | NetworkConnection |
|---|---|
| |
| |
| |
| Manual JSON | |
| Custom framer | |
| No |
swift
connection.stateUpdateHandler = { [weak self] state in
if case .ready = state {
self?.sendData()
}
}
func sendData() {
connection.send(content: data, completion: .contentProcessed { [weak self] error in
self?.receiveData()
})
}Migration example
迁移后(NetworkConnection)
Before (NWConnection)
—
swift
connection.stateUpdateHandler = { [weak self] state in
if case .ready = state {
self?.sendData()
}
}
func sendData() {
connection.send(content: data, completion: .contentProcessed { [weak self] error in
self?.receiveData()
})
}swift
Task {
for await state in connection.states {
if case .ready = state {
try await connection.send(data)
let received = try await connection.receive(exactly: 10).content
}
}
}After (NetworkConnection)
测试清单
swift
Task {
for await state in connection.states {
if case .ready = state {
try await connection.send(data)
let received = try await connection.receive(exactly: 10).content
}
}
}发布网络代码前需完成:
Testing Checklist
设备测试
Before shipping networking code:
- 在真实设备上测试(不仅是模拟器)
- 在多个iOS版本上测试(12、15、26)
- 在iPhone和iPad上测试(不同网络特性)
Device Testing
网络条件
- Tested on real device (not just simulator)
- Tested on multiple iOS versions (12, 15, 26)
- Tested on iPhone and iPad (different network characteristics)
- WiFi(家庭网络)
- 蜂窝网络(禁用WiFi)
- 飞行模式 → WiFi(测试waiting状态)
- WiFi → 蜂窝网络切换(走出建筑)
- 蜂窝网络 → WiFi切换(走进建筑)
- 弱信号(地下室、电梯)
- 网络链路调节器(100ms延迟,3%丢包)
Network Conditions
网络类型
- WiFi (home network)
- Cellular (disable WiFi)
- Airplane Mode → WiFi (test waiting state)
- WiFi → cellular transition (walk out of building)
- Cellular → WiFi transition (walk into building)
- Weak signal (basement, elevator)
- Network Link Conditioner (100ms latency, 3% packet loss)
- 仅IPv4网络
- 仅IPv6网络(部分蜂窝运营商)
- 双栈(IPv4 + IPv6)
- 企业VPN激活
- 个人热点(高成本路径)
Network Types
性能
- IPv4-only network
- IPv6-only network (some cellular carriers)
- Dual-stack (IPv4 + IPv6)
- Corporate VPN active
- Personal hotspot (expensive path)
- 连接建立时间 < 500ms(检查日志)
- UDP使用批量发送(通过Instruments验证)
- 使用contentProcessed进行pacing(检查发送时序)
- 使用Instruments网络模板分析性能
- CPU占用可接受(网络相关 < 10%)
- 内存稳定(无泄漏,检查)
[weak self]
Performance
错误处理
- Connection establishment < 500ms (check logs)
- Using batch for UDP (verify with Instruments)
- Using contentProcessed for pacing (check send timing)
- Profiled with Instruments Network template
- CPU usage acceptable (< 10% for networking)
- Memory stable (no leaks, check [weak self])
- 处理.waiting状态(显示“等待中...”UI)
- 处理.failed状态(具体错误信息)
- 记录TLS握手错误
- 超时处理(不要无限等待)
- 用户可见错误可操作(如“检查网络”而非“POSIX 61”)
Error Handling
iOS 26+特性(若使用NetworkConnection)
- Handling .waiting state (show "Waiting..." UI)
- Handling .failed state (specific error messages)
- TLS handshake errors logged
- Timeout handling (don't wait forever)
- User-facing errors actionable ("Check network" not "POSIX 61")
- 若需要消息边界则使用TLV帧
- 若发送Codable类型则使用Coder协议
- 使用NetworkListener而非NWListener
- 若需点对点通信则使用NetworkBrowser的Wi-Fi Aware
iOS 26+ Features (if using NetworkConnection)
API速查
—
NetworkConnection(iOS 26+)
- Using TLV framing if need message boundaries
- Using Coder protocol if sending Codable types
- Using NetworkListener instead of NWListener
- Using NetworkBrowser for Wi-Fi Aware if peer-to-peer
swift
// Create connection
NetworkConnection(to: .hostPort(host: "example.com", port: 443)) { TLS() }
// Send
try await connection.send(data)
// Receive
try await connection.receive(exactly: n).content
// States
for await state in connection.states { }
// TLV framing
NetworkConnection(to: endpoint) { TLV { TLS() } }
// Coder protocol
NetworkConnection(to: endpoint) { Coder(MyType.self, using: .json) { TLS() } }
// Listener
NetworkListener { TLS() }.run { connection in }
// Browser
NetworkBrowser(for: .wifiAware(...)).run { endpoints in }API Quick Reference
NWConnection(iOS 12-25)
NetworkConnection (iOS 26+)
—
swift
// Create connection
NetworkConnection(to: .hostPort(host: "example.com", port: 443)) { TLS() }
// Send
try await connection.send(data)
// Receive
try await connection.receive(exactly: n).content
// States
for await state in connection.states { }
// TLV framing
NetworkConnection(to: endpoint) { TLV { TLS() } }
// Coder protocol
NetworkConnection(to: endpoint) { Coder(MyType.self, using: .json) { TLS() } }
// Listener
NetworkListener { TLS() }.run { connection in }
// Browser
NetworkBrowser(for: .wifiAware(...)).run { endpoints in }swift
// Create connection
let connection = NWConnection(host: "example.com", port: 443, using: .tls)
// State handler
connection.stateUpdateHandler = { [weak self] state in }
// Start
connection.start(queue: .main)
// Send
connection.send(content: data, completion: .contentProcessed { [weak self] error in })
// Receive
connection.receive(minimumIncompleteLength: min, maximumLength: max) { [weak self] data, context, isComplete, error in }
// Viability
connection.viabilityUpdateHandler = { isViable in }
// Better path
connection.betterPathUpdateHandler = { betterPathAvailable in }
// Cancel
connection.cancel()NWConnection (iOS 12-25)
NWListener(iOS 12-25)
swift
// Create connection
let connection = NWConnection(host: "example.com", port: 443, using: .tls)
// State handler
connection.stateUpdateHandler = { [weak self] state in }
// Start
connection.start(queue: .main)
// Send
connection.send(content: data, completion: .contentProcessed { [weak self] error in })
// Receive
connection.receive(minimumIncompleteLength: min, maximumLength: max) { [weak self] data, context, isComplete, error in }
// Viability
connection.viabilityUpdateHandler = { isViable in }
// Better path
connection.betterPathUpdateHandler = { betterPathAvailable in }
// Cancel
connection.cancel()swift
let listener = try NWListener(using: .tcp, on: 1029)
listener.newConnectionHandler = { newConnection in }
listener.start(queue: .main)NWListener (iOS 12-25)
NWBrowser(iOS 12-25)
swift
let listener = try NWListener(using: .tcp, on: 1029)
listener.newConnectionHandler = { newConnection in }
listener.start(queue: .main)swift
let browser = NWBrowser(for: .bonjour(type: "_http._tcp", domain: nil), using: .tcp)
browser.browseResultsChangedHandler = { results, changes in }
browser.start(queue: .main)NWBrowser (iOS 12-25)
NWPathMonitor
swift
let browser = NWBrowser(for: .bonjour(type: "_http._tcp", domain: nil), using: .tcp)
browser.browseResultsChangedHandler = { results, changes in }
browser.start(queue: .main)swift
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in }
monitor.start(queue: .main)NWPathMonitor
资源
swift
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in }
monitor.start(queue: .main)WWDC: 2018-715, 2025-250
文档: /network, /network/nwconnection, /network/networkconnection
技能: axiom-networking, axiom-networking-diag
最后更新 2025-12-02
状态 基于WWDC 2018和WWDC 2025的生产就绪参考文档
覆盖范围 NWConnection(iOS 12-25)、NetworkConnection(iOS 26+)、WWDC 2025全部12个代码示例
Resources
—
WWDC: 2018-715, 2025-250
Docs: /network, /network/nwconnection, /network/networkconnection
Skills: axiom-networking, axiom-networking-diag
Last Updated 2025-12-02
Status Production-ready reference from WWDC 2018 and WWDC 2025
Coverage NWConnection (iOS 12-25), NetworkConnection (iOS 26+), all 12 WWDC 2025 code examples
—