axiom-network-framework-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Network.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
    axiom-networking
    for anti-patterns, common patterns, pressure scenarios
  • Use
    axiom-networking-diag
    for systematic troubleshooting of connection failures

  • 使用
    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

时间线

YeariOS VersionKey Features
2018iOS 12NWConnection, NWListener, NWBrowser introduced
2019iOS 13User-space networking (30% CPU reduction), TLS 1.3 default
2021iOS 15WebSocket support in URLSession
2025iOS 26NetworkConnection (async/await), TLV framing, Coder protocol, Wi-Fi Aware
年份iOS版本核心特性
2018iOS 12推出NWConnection、NWListener、NWBrowser
2019iOS 13用户态网络(CPU占用降低30%),默认启用TLS 1.3
2021iOS 15URLSession支持WebSocket
2025iOS 26NetworkConnection(async/await)、TLV帧、Coder协议、Wi-Fi Aware

NWConnection (iOS 12-25) vs NetworkConnection (iOS 26+)

NWConnection(iOS 12-25)与NetworkConnection(iOS 26+)对比

FeatureNWConnection (iOS 12-25)NetworkConnection (iOS 26+)
Async modelCompletion handlersasync/await structured concurrency
State updates
stateUpdateHandler
callback
states
AsyncSequence
Send
send(content:completion:)
callback
try await send(content)
suspending
Receive
receive(minimumIncompleteLength:maximumLength:completion:)
try await receive(exactly:)
suspending
FramingManual or custom NWFramerTLV built-in (
TLV { TLS() }
)
CodableManual JSON encode/decodeCoder protocol (
Coder(MyType.self, using: .json)
)
MemoryRequires
[weak self]
in all closures
No
[weak self]
needed (Task cancellation automatic)
Error handlingCheck error in completion
throws
with natural propagation
State machineCallbacks on state changes
for await state in connection.states
DiscoveryNWBrowser (Bonjour only)NetworkBrowser (Bonjour + Wi-Fi Aware)
特性NWConnection(iOS 12-25)NetworkConnection(iOS 26+)
异步模型完成处理程序async/await结构化并发
状态更新
stateUpdateHandler
回调
states
AsyncSequence
发送数据
send(content:completion:)
回调
try await send(content)
挂起函数
接收数据
receive(minimumIncompleteLength:maximumLength:completion:)
try await receive(exactly:)
挂起函数
帧处理手动或自定义NWFramer内置TLV(
TLV { TLS() }
可编码支持手动JSON编解码Coder协议(
Coder(MyType.self, using: .json)
内存管理所有闭包中需使用
[weak self]
无需
[weak self]
(任务取消自动处理)
错误处理在完成处理程序中检查错误
throws
自然传播错误
状态机状态变化时触发回调
for await state in connection.states
发现功能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

核心要点

  • TLS()
    infers
    TCP()
    and
    IP()
    automatically
  • 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

  • .fragmentationEnabled(false)
    — For protocols that handle fragmentation themselves (QUIC)
  • .ipVersion(.v6)
    — Force IPv6 only (testing)
  • .fragmentationEnabled(false)
    — 适用于自行处理分片的协议(如QUIC)
  • .ipVersion(.v6)
    — 强制仅使用IPv6(测试场景)

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

常见参数

  • .constrainedPathsProhibited(true)
    — Respect low data mode
  • .expensivePathsProhibited(true)
    — Don't use cellular/hotspot
  • .multipathServiceType(.handover)
    — Enable Multipath TCP
  • .constrainedPathsProhibited(true)
    — 遵守低数据模式
  • .expensivePathsProhibited(true)
    — 不使用蜂窝网络/热点
  • .multipathServiceType(.handover)
    — 启用多路径TCP

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 cancelled
NetworkConnection会经历以下状态转换:
setup
preparing (DNS, TCP握手, TLS握手)
┌─ waiting (无网络,重试中)
│    ↓
└→ ready (可发送/接收数据)
  failed (错误) 或 cancelled

Monitoring 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()变体

  • receive(exactly: n)
    — Wait for exactly n bytes
  • receive(atLeast: min, atMost: max)
    — Get between min and max bytes
  • receive(as: UInt32.self)
    — Read fixed-size type (network byte order)

  • receive(exactly: n)
    — 等待接收恰好n字节
  • receive(atLeast: min, atMost: max)
    — 接收介于min和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
    — JSON encoding (human-readable, widely compatible)
  • .propertyList
    — Property list (faster, smaller)
  • .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)
  • connection.messages
    async sequence for receiving
  • 每个连接自动分配子任务(无需手动管理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
[weak self]
in stateUpdateHandler to prevent retain cycles.
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/cancelled
NWConnection状态机(与NetworkConnection相同):
setup → preparing → waiting/ready → failed/cancelled

State 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

接收参数

  • minimumIncompleteLength
    : Minimum bytes before callback (1 = return any data)
  • maximumLength
    : Maximum bytes per callback
  • For "exactly n bytes": Set both to n

  • minimumIncompleteLength
    : 触发回调的最小字节数(1 = 返回任意数据)
  • 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模式

  • .handover
    — Seamless handoff between networks (WiFi ↔ cellular)
  • .interactive
    — Use multiple paths simultaneously (lowest latency)
  • .aggregate
    — Use multiple paths simultaneously (highest throughput)
  • .handover
    — 网络间无缝切换(WiFi ↔ 蜂窝网络)
  • .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
)
规则:
  • 始终先验证系统信任链(
    SecTrustEvaluateAsyncWithError
    )—— 这会尊重企业安装的根CA
  • 使用公钥绑定而非证书绑定(可在证书轮换时继续工作)
  • 提供托管配置(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 (
    SecTrustEvaluateAsyncWithError
    ) — this respects enterprise-installed root CAs
  • 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 required

Cipher 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 SocketsUser-Space Networking
Packet → driver → kernel → copy → userspacePacket → driver → memory-mapped region → userspace (no copy)
100 datagrams = 100 syscalls100 datagrams = ~1 syscall (with batching)
~30% higher CPUBaseline 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
    /
    .ect1
    — 支持ECN的传输
  • .congestionEncountered
    — 收到拥塞通知
优势 网络可在不丢包的情况下发出拥塞信号。

ECN flags

服务类别(WWDC 2018 lines 379-388)

  • .ect0
    /
    .ect1
    — ECN-capable transport
  • .congestionEncountered
    — Congestion notification received
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
    — 正常优先级
  • .responsiveData
    — 交互式数据(API调用)
  • .realTimeInteractive
    — 时间敏感型(语音、游戏)

Service classes

TCP快速打开(WWDC 2018 lines 389-406)

  • .background
    — Low priority (large downloads, sync)
  • .default
    — Normal priority
  • .responsiveData
    — Interactive data (API calls)
  • .realTimeInteractive
    — Time-sensitive (voice, gaming)
在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说明
socket() + connect()
NWConnection(host:port:using:) + start()
默认非阻塞
send() / sendto()
connection.send(content:completion:)
异步回调
recv() / recvfrom()
connection.receive(min:max:completion:)
异步回调
bind() + listen()
NWListener(using:on:)
自动端口绑定
accept()
listener.newConnectionHandler
每个连接触发回调
getaddrinfo()
使用
NWEndpoint.Host(hostname)
自动DNS解析
SCNetworkReachability
connection.stateUpdateHandler
waiting状态
无竞争条件
setsockopt()
NWParameters
类型安全选项

Migration Strategies

迁移示例

From BSD Sockets to NWConnection

迁移前(阻塞套接字)

BSD SocketsNWConnectionNotes
socket() + connect()
NWConnection(host:port:using:) + start()
Non-blocking by default
send() / sendto()
connection.send(content:completion:)
Async callback
recv() / recvfrom()
connection.receive(min:max:completion:)
Async callback
bind() + listen()
NWListener(using:on:)
Automatic port binding
accept()
listener.newConnectionHandler
Callback per connection
getaddrinfo()
Use
NWEndpoint.Host(hostname)
DNS automatic
SCNetworkReachability
connection.stateUpdateHandler
waiting state
No race conditions
setsockopt()
NWParameters
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
    [weak self]
    needed
  • TLV framing built-in
  • Coder protocol for Codable types
NWConnectionNetworkConnection
connection.stateUpdateHandler = { }
for await state in connection.states { }
connection.send(content:completion:)
try await connection.send(content)
connection.receive(min:max:completion:)
try await connection.receive(exactly:)
手动JSON编解码
Coder(MyType.self, using: .json)
自定义帧处理器
TLV { TLS() }
处处使用
[weak self]
无需
[weak self]

Migration mapping

迁移示例

迁移前(NWConnection)

NWConnectionNetworkConnection
connection.stateUpdateHandler = { }
for await state in connection.states { }
connection.send(content:completion:)
try await connection.send(content)
connection.receive(min:max:completion:)
try await connection.receive(exactly:)
Manual JSON
Coder(MyType.self, using: .json)
Custom framer
TLV { TLS() }
[weak self]
everywhere
No
[weak self]
needed
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