axiom-networking-legacy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLegacy iOS 12-25 NWConnection Patterns
iOS 12-25 遗留NWConnection模式
These patterns use NWConnection with completion handlers for apps supporting iOS 12-25. If your app targets iOS 26+, use NetworkConnection with async/await instead (see axiom-network-framework-ref skill).
这些模式使用带完成处理程序的NWConnection,适用于支持iOS 12-25的应用。如果你的应用目标版本为iOS 26+,请改用带async/await的NetworkConnection(详见axiom-network-framework-ref技能)。
Pattern 2a: NWConnection with TLS (iOS 12-25)
模式2a:带TLS的NWConnection(iOS 12-25)
Use when Supporting iOS 12-25, need TLS encryption, can't use async/await yet
Time cost 10-15 minutes
适用场景 支持iOS 12-25、需要TLS加密、暂无法使用async/await
耗时 10-15分钟
GOOD: NWConnection with Completion Handlers
最佳实践:带完成处理程序的NWConnection
swift
import Network
// Create connection with TLS
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?.sendInitialData()
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)
// Send data with 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 callback = network stack consumed data
// This is when you should send next chunk (pacing)
self?.sendNextChunk()
})
}
// Receive exact byte count
func receiveData() {
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...
self?.receiveData() // Continue receiving
}
}
}swift
import Network
// Create connection with TLS
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?.sendInitialData()
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)
// Send data with 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 callback = network stack consumed data
// This is when you should send next chunk (pacing)
self?.sendNextChunk()
})
}
// Receive exact byte count
func receiveData() {
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...
self?.receiveData() // Continue receiving
}
}
}Key differences from NetworkConnection
与NetworkConnection的主要区别
- Must use in all completion handlers to prevent retain cycles
[weak self] - stateUpdateHandler receives state, not async sequence
- send/receive use completion callbacks, not async/await
- 所有完成处理程序中必须使用以避免循环引用
[weak self] - stateUpdateHandler接收状态,而非异步序列
- 发送/接收使用完成回调,而非async/await
When to use
适用场景
- Supporting iOS 12-15 (70% of devices as of 2024)
- Codebases not yet using async/await
- Libraries needing backward compatibility
- 支持iOS 12-15(截至2024年占设备总量的70%)
- 尚未使用async/await的代码库
- 需要向后兼容性的库
Migration to NetworkConnection (iOS 26+)
迁移至NetworkConnection(iOS 26+)
- stateUpdateHandler -> connection.states async sequence
- Completion handlers -> try await calls
- [weak self] -> No longer needed (async/await handles cancellation)
- stateUpdateHandler → connection.states异步序列
- 完成处理程序 → try await调用
- → 不再需要(async/await处理取消逻辑)
[weak self]
Pattern 2b: NWConnection UDP Batch (iOS 12-25)
模式2b:NWConnection UDP批量处理(iOS 12-25)
Use when Supporting iOS 12-25, sending multiple UDP datagrams efficiently, need ~30% CPU reduction
Time cost 10-15 minutes
Background Traditional UDP sockets send one datagram per syscall. If you're sending 100 small packets, that's 100 context switches. Batching reduces this to ~1 syscall.
适用场景 支持iOS 12-25、需要高效发送多个UDP数据报、需降低约30%的CPU占用
耗时 10-15分钟
背景 传统UDP套接字每次系统调用仅发送一个数据报。如果发送100个小数据包,就会产生100次上下文切换。批量处理可将其减少至约1次系统调用。
BAD: Individual UDP Sends (High CPU)
不良实践:单独发送UDP数据包(高CPU占用)
swift
// WRONG — 100 context switches for 100 packets
for frame in videoFrames {
sendto(socket, frame.bytes, frame.count, 0, &addr, addrlen)
// Each send = context switch to kernel
}swift
// WRONG — 100 context switches for 100 packets
for frame in videoFrames {
sendto(socket, frame.bytes, frame.count, 0, &addr, addrlen)
// Each send = context switch to kernel
}GOOD: Batched UDP Sends (30% Lower CPU)
最佳实践:批量发送UDP数据包(CPU占用降低30%)
swift
import Network
// UDP connection
let connection = NWConnection(
host: NWEndpoint.Host("stream-server.example.com"),
port: NWEndpoint.Port(integerLiteral: 9000),
using: .udp
)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Ready to send UDP")
}
}
connection.start(queue: .main)
// Batch sending for efficiency
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
// 30% lower CPU usage vs individual sends
}
// Receive UDP datagrams
func receiveFrames() {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
// Process video frame
self?.displayFrame(data)
self?.receiveFrames() // Continue receiving
}
}
}swift
import Network
// UDP connection
let connection = NWConnection(
host: NWEndpoint.Host("stream-server.example.com"),
port: NWEndpoint.Port(integerLiteral: 9000),
using: .udp
)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Ready to send UDP")
}
}
connection.start(queue: .main)
// Batch sending for efficiency
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
// 30% lower CPU usage vs individual sends
}
// Receive UDP datagrams
func receiveFrames() {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] (data, context, isComplete, error) in
if let error = error {
print("Receive error: \(error)")
return
}
if let data = data {
// Process video frame
self?.displayFrame(data)
self?.receiveFrames() // Continue receiving
}
}
}Performance characteristics
性能特点
- Without batch 100 datagrams = 100 syscalls = 100 context switches
- With batch 100 datagrams = ~1 syscall = 1 context switch
- Result ~30% lower CPU usage (measured with Instruments)
- 未批量处理 100个数据报 = 100次系统调用 = 100次上下文切换
- 批量处理 100个数据报 = 约1次系统调用 = 1次上下文切换
- 结果 与单独发送相比,CPU占用降低约30%(通过Instruments测量)
When to use
适用场景
- Real-time video/audio streaming
- Gaming with frequent updates (player position)
- High-frequency sensor data (IoT)
WWDC 2018 demo Live video streaming showed 30% lower CPU on receiver with user-space networking + batching
- 实时视频/音频流
- 需频繁更新的游戏(如玩家位置)
- 高频传感器数据(物联网)
WWDC 2018演示 实时视频流在用户空间网络+批量处理的情况下,接收端CPU占用降低30%
Pattern 2c: NWListener (iOS 12-25)
模式2c:NWListener(iOS 12-25)
Use when Need to accept incoming connections, building servers or peer-to-peer apps, supporting iOS 12-25
Time cost 20-25 minutes
适用场景 需要接受传入连接、构建服务器或点对点应用、支持iOS 12-25
耗时 20-25分钟
BAD: Manual Socket Listening
不良实践:手动套接字监听
swift
// WRONG — Manual socket management
let sock = socket(AF_INET, SOCK_STREAM, 0)
bind(sock, &addr, addrlen)
listen(sock, 5)
while true {
let client = accept(sock, nil, nil) // Blocks thread
// Handle client...
}swift
// WRONG — Manual socket management
let sock = socket(AF_INET, SOCK_STREAM, 0)
bind(sock, &addr, addrlen)
listen(sock, 5)
while true {
let client = accept(sock, nil, nil) // Blocks thread
// Handle client...
}GOOD: NWListener with Automatic Connection Handling
最佳实践:NWListener自动连接处理
swift
import Network
// Create listener with default parameters
let listener = try NWListener(using: .tcp, on: 1029)
// Advertise Bonjour service
listener.service = NWListener.Service(name: "MyApp", type: "_myservice._tcp")
// Handle service registration updates
listener.serviceRegistrationUpdateHandler = { update in
switch update {
case .add(let endpoint):
if case .service(let name, let type, let domain, _) = endpoint {
print("Advertising as: \(name).\(type)\(domain)")
}
default:
break
}
}
// Handle incoming connections
listener.newConnectionHandler = { [weak self] newConnection in
print("New connection from: \(newConnection.endpoint)")
// Configure connection
newConnection.stateUpdateHandler = { state in
switch state {
case .ready:
print("Client connected")
self?.handleClient(newConnection)
case .failed(let error):
print("Client connection failed: \(error)")
default:
break
}
}
// Start handling this connection
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)
// Handle client data
func handleClient(_ connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [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")
// Echo back
connection.send(content: data, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
self?.handleClient(connection) // Continue receiving
}
}
}swift
import Network
// Create listener with default parameters
let listener = try NWListener(using: .tcp, on: 1029)
// Advertise Bonjour service
listener.service = NWListener.Service(name: "MyApp", type: "_myservice._tcp")
// Handle service registration updates
listener.serviceRegistrationUpdateHandler = { update in
switch update {
case .add(let endpoint):
if case .service(let name, let type, let domain, _) = endpoint {
print("Advertising as: \(name).\(type)\(domain)")
}
default:
break
}
}
// Handle incoming connections
listener.newConnectionHandler = { [weak self] newConnection in
print("New connection from: \(newConnection.endpoint)")
// Configure connection
newConnection.stateUpdateHandler = { state in
switch state {
case .ready:
print("Client connected")
self?.handleClient(newConnection)
case .failed(let error):
print("Client connection failed: \(error)")
default:
break
}
}
// Start handling this connection
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)
// Handle client data
func handleClient(_ connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [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")
// Echo back
connection.send(content: data, completion: .contentProcessed { error in
if let error = error {
print("Send error: \(error)")
}
})
self?.handleClient(connection) // Continue receiving
}
}
}When to use
适用场景
- Peer-to-peer apps (file sharing, messaging)
- Local network services
- Development/testing servers
- 点对点应用(文件共享、即时通讯)
- 本地网络服务
- 开发/测试服务器
Bonjour advertising
Bonjour服务广播
- Automatic service discovery on local network
- No hardcoded IPs needed
- Works with NWBrowser for discovery
- 本地网络自动服务发现
- 无需硬编码IP地址
- 可与NWBrowser配合使用实现发现功能
Security considerations
安全注意事项
- Use TLS parameters for encryption:
NWListener(using: .tls, on: port) - Validate client connections before processing data
- Set connection limits to prevent DoS
- 使用TLS参数进行加密:
NWListener(using: .tls, on: port) - 处理数据前验证客户端连接
- 设置连接限制以防止拒绝服务攻击
Pattern 2d: Network Discovery (iOS 12-25)
模式2d:网络发现(iOS 12-25)
Use when Discovering services on local network (Bonjour), building peer-to-peer apps, supporting iOS 12-25
Time cost 25-30 minutes
适用场景 发现本地网络上的服务(Bonjour)、构建点对点应用、支持iOS 12-25
耗时 25-30分钟
BAD: Hardcoded IP Addresses
不良实践:硬编码IP地址
swift
// WRONG — Brittle, requires manual configuration
let connection = NWConnection(host: "192.168.1.100", port: 9000, using: .tcp)
// What if IP changes? What if multiple devices?swift
// WRONG — Brittle, requires manual configuration
let connection = NWConnection(host: "192.168.1.100", port: 9000, using: .tcp)
// What if IP changes? What if multiple devices?GOOD: NWBrowser for Service Discovery
最佳实践:使用NWBrowser进行服务发现
swift
import Network
// Browse for services on local network
let browser = NWBrowser(for: .bonjour(type: "_myservice._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
self.connectToService(result.endpoint)
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)
// Connect to discovered service
func connectToService(_ endpoint: NWEndpoint) {
let connection = NWConnection(to: endpoint, using: .tcp)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Connected to service")
}
}
connection.start(queue: .main)
}swift
import Network
// Browse for services on local network
let browser = NWBrowser(for: .bonjour(type: "_myservice._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
self.connectToService(result.endpoint)
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)
// Connect to discovered service
func connectToService(_ endpoint: NWEndpoint) {
let connection = NWConnection(to: endpoint, using: .tcp)
connection.stateUpdateHandler = { state in
if case .ready = state {
print("Connected to service")
}
}
connection.start(queue: .main)
}When to use
适用场景
- Peer-to-peer discovery (AirDrop-like features)
- Local network printers, media servers
- Development/testing (find test servers automatically)
- 点对点发现(类似AirDrop的功能)
- 本地网络打印机、媒体服务器
- 开发/测试(自动查找测试服务器)
Performance characteristics
性能特点
- mDNS-based (multicast DNS, no central server)
- Near-instant discovery on same subnet
- Automatic updates when services appear/disappear
- 基于mDNS(多播DNS,无中央服务器)
- 同一子网内近乎即时发现
- 服务出现/消失时自动更新
iOS 26+ alternative
iOS 26+替代方案
- Use NetworkBrowser with Wi-Fi Aware for peer-to-peer without infrastructure
- See Pattern 1d in axiom-network-framework-ref skill
- 使用NetworkBrowser结合Wi-Fi Aware实现无需基础设施的点对点通信
- 详见axiom-network-framework-ref技能中的模式1d
Resources
资源
Skills: axiom-networking, axiom-network-framework-ref, axiom-networking-migration
技能: axiom-networking, axiom-network-framework-ref, axiom-networking-migration