mcpField
A
axiom-networking-migration

axiom-networking-migration

github.com/charleswiltgen開発1.2k163 回インストール今日更新
リポジトリ

axiom-networking-migration について

Swift 開発者向けに、ネットワーク API の選択と移行方法をまとめたガイドです。BSD Sockets、NWConnection、NetworkConnection、URLSession StreamTask を扱い、HTTP では URLSession を継続する判断も示します。

主な機能

  1. 01BSD Sockets から NWConnection への移行
  2. 02NWConnection から NetworkConnection への移行
  3. 03URLSession StreamTask の移行案内
  4. 04API 対応表とコード例
  5. 05ネットワーク API の選択指針

活用シーン

  1. 01ソケットコードを NWConnection に置き換える
  2. 02接続処理に async/await を導入する
  3. 03UDP や独自プロトコル向けの移行
  4. 04HTTP で 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 Sockets から 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() }Type-Length-Value が組み込まれています
あらゆる箇所で [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 によってエラーが自然に伝播します
  • タスク終了時に自動的にキャンセルされます

移行 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

コメント