关于 axiom-networking-migration
面向 Swift 开发者的网络 API 迁移指南,说明 BSD Sockets、NWConnection、NetworkConnection 与 URLSession StreamTask 之间的选择和迁移方式。
主要功能
- 01BSD Sockets 迁移至 NWConnection
- 02NWConnection 迁移至 NetworkConnection
- 03URLSession StreamTask 迁移参考
- 04API 对照与代码示例
- 05网络 API 选型说明
适用场景
- 01将套接字代码改为 NWConnection
- 02采用 async/await 更新连接代码
- 03为 UDP 或自定义协议迁移
- 04判断 HTTP 项目是否应保留 URLSession
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-networking-migration安装后在 Claude Code 中输入/axiom-networking-migration确认已加载。
详细介绍
网络框架迁移指南
我需要迁移吗?
What networking API are you using?
├─ URLSession for HTTP/HTTPS REST APIs?
│ └─ Stay with URLSession — it's the RIGHT tool for HTTP
│ URLSession handles caching, cookies, auth challenges,
│ HTTP/2/3, and is heavily optimized for web APIs.
│ Network.framework is for custom protocols, NOT HTTP.
│
├─ BSD Sockets (socket, connect, send, recv)?
│ └─ Migrate to NWConnection (iOS 12+)
│ → See Migration 1 below
│
├─ NWConnection / NWListener?
│ ├─ Need async/await? → Migrate to NetworkConnection (iOS 26+)
│ │ → See Migration 2 below
│ └─ Callback-based code working fine? → Stay (not deprecated)
│
├─ URLSession StreamTask for TCP/TLS?
│ └─ Need UDP or custom protocols? → NetworkConnection
│ Need just TCP/TLS for HTTP? → Stay with URLSession
│ → See Migration 3 below
│
├─ SCNetworkReachability?
│ └─ DEPRECATED — Replace with NWPathMonitor (iOS 12+)
│ let monitor = NWPathMonitor()
│ monitor.pathUpdateHandler = { path in
│ print(path.status == .satisfied ? "Online" : "Offline")
│ }
│
└─ CFSocket / NSStream?
└─ DEPRECATED — Replace with NWConnection (iOS 12+)
→ See Migration 1 below
迁移 1:从 BSD 套接字迁移到 NWConnection
迁移映射
| BSD Sockets | NWConnection | 说明 |
|---|---|---|
socket() + connect() | NWConnection(host:port:using:) + start() | 默认非阻塞 |
send() / sendto() | connection.send(content:completion:) | 异步,立即返回 |
recv() / recvfrom() | connection.receive(minimumIncompleteLength:maximumLength:completion:) | 异步,立即返回 |
bind() + listen() | NWListener(using:on:) | 自动绑定端口 |
accept() | listener.newConnectionHandler | 为每个连接调用回调 |
getaddrinfo() | 让 NWConnection 处理 DNS | 通过竞速进行智能解析 |
SCNetworkReachability | connection.stateUpdateHandler 等待状态 | 不存在竞态条件 |
setsockopt() | NWParameters 配置 | 类型安全的选项 |
迁移示例
迁移前(BSD Sockets)
// BEFORE — Blocking, manual DNS, error-prone
var hints = addrinfo()
hints.ai_family = AF_INET
hints.ai_socktype = SOCK_STREAM
var results: UnsafeMutablePointer<addrinfo>?
getaddrinfo("example.com", "443", &hints, &results)
let sock = socket(results.pointee.ai_family, results.pointee.ai_socktype, 0)
connect(sock, results.pointee.ai_addr, results.pointee.ai_addrlen) // BLOCKS
let data = "Hello".data(using: .utf8)!
data.withUnsafeBytes { ptr in
send(sock, ptr.baseAddress, data.count, 0)
}
迁移后(NWConnection)
// AFTER — Non-blocking, automatic DNS, type-safe
let connection = NWConnection(
host: NWEndpoint.Host("example.com"),
port: NWEndpoint.Port(integerLiteral: 443),
using: .tls
)
connection.stateUpdateHandler = { state in
if case .ready = state {
let data = Data("Hello".utf8)
connection.send(content: data, completion: .contentProcessed { error in
if let error = error {
print("Send failed: \(error)")
}
})
}
}
connection.start(queue: .main)
优势
- 20 行 → 10 行
- 无需手动处理 DNS、阻塞或不安全指针
- 自动支持 Happy Eyeballs、代理和 WiFi Assist
迁移 2:从 NWConnection 迁移到 NetworkConnection(iOS 26+)
迁移原因
- async/await 可消除回调地狱
- 内置 TLV 分帧和 Coder 协议
- 无需使用 [weak self](async/await 会处理取消)
- 通过异步序列监控状态
迁移映射
| NWConnection(iOS 12-25) | NetworkConnection(iOS 26+) | 说明 |
|---|---|---|
connection.stateUpdateHandler = { state in } | for await state in connection.states { } | 异步序列 |
connection.send(content:completion:) | try await connection.send(content) | 挂起函数 |
connection.receive(minimumIncompleteLength:maximumLength:completion:) | try await connection.receive(exactly:) | 挂起函数 |
| 手动进行 JSON 编码/解码 | Coder(MyType.self, using: .json) | 内置 Codable 支持 |
| 自定义分帧器 | TLV { TLS() } | 内置类型-长度-值 |
到处使用 [weak self] | 无需使用 [weak self] | 自动取消任务 |
迁移示例
迁移前(NWConnection)
// BEFORE — Completion handlers, manual memory management
let connection = NWConnection(host: "example.com", port: 443, using: .tls)
connection.stateUpdateHandler = { [weak self] state in
switch state {
case .ready:
self?.sendData()
case .waiting(let error):
print("Waiting: \(error)")
case .failed(let error):
print("Failed: \(error)")
default:
break
}
}
connection.start(queue: .main)
func sendData() {
let data = Data("Hello".utf8)
connection.send(content: data, completion: .contentProcessed { [weak self] error in
if let error = error {
print("Send error: \(error)")
return
}
self?.receiveData()
})
}
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)")
}
}
}
迁移后(NetworkConnection)
// AFTER — Async/await, automatic memory management
let connection = NetworkConnection(
to: .hostPort(host: "example.com", port: 443)
) {
TLS()
}
// Monitor states in background task
Task {
for await state in connection.states {
switch state {
case .preparing:
print("Connecting...")
case .ready:
print("Ready")
case .waiting(let error):
print("Waiting: \(error)")
case .failed(let error):
print("Failed: \(error)")
default:
break
}
}
}
// Send and receive with async/await
func sendAndReceive() async throws {
let data = Data("Hello".utf8)
try await connection.send(data)
let received = try await connection.receive(exactly: 10).content
print("Received: \(received)")
}
优势
- 30 行 → 15 行
- 无需嵌套回调,也无需使用 [weak self]
- 错误通过 throws 自然传播
- Task 退出时自动取消
迁移 3:从 URLSession StreamTask 迁移到 NetworkConnection
适合迁移的情况
- 需要使用 UDP(StreamTask 仅支持 TCP)
- 需要使用 TCP/TLS 之外的自定义协议
- 需要底层控制(数据包节奏、ECN、服务类别)
适合继续使用 URLSession 的情况
- 处理 HTTP/HTTPS(URLSession 针对此场景进行了优化)
- 需要 WebSocket 支持
- 需要内置缓存和 Cookie 处理
迁移示例
迁移前(URLSession StreamTask)
// BEFORE — URLSession for TCP/TLS stream
let task = URLSession.shared.streamTask(withHostName: "example.com", port: 443)
task.resume()
task.write(Data("Hello".utf8), timeout: 10) { error in
if let error = error {
print("Write error: \(error)")
}
}
task.readData(ofMinLength: 10, maxLength: 10, timeout: 10) { data, atEOF, error in
if let error = error {
print("Read error: \(error)")
return
}
if let data = data {
print("Received: \(data)")
}
}
迁移后(NetworkConnection)
// AFTER — NetworkConnection for TCP/TLS
let connection = NetworkConnection(
to: .hostPort(host: "example.com", port: 443)
) {
TLS()
}
func sendAndReceive() async throws {
try await connection.send(Data("Hello".utf8))
let data = try await connection.receive(exactly: 10).content
print("Received: \(data)")
}
资源
技能:axiom-ios-networking、axiom-networking-legacy
评论
支持 Markdown