mcpField
A
axiom-networking-migration

axiom-networking-migration

github.com/charleswiltgen开发1.2k163 次安装今天更新
代码仓库

关于 axiom-networking-migration

面向 Swift 开发者的网络 API 迁移指南,说明 BSD Sockets、NWConnection、NetworkConnection 与 URLSession StreamTask 之间的选择和迁移方式。

主要功能

  1. 01BSD Sockets 迁移至 NWConnection
  2. 02NWConnection 迁移至 NetworkConnection
  3. 03URLSession StreamTask 迁移参考
  4. 04API 对照与代码示例
  5. 05网络 API 选型说明

适用场景

  1. 01将套接字代码改为 NWConnection
  2. 02采用 async/await 更新连接代码
  3. 03为 UDP 或自定义协议迁移
  4. 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 SocketsNWConnection说明
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通过竞速进行智能解析
SCNetworkReachabilityconnection.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