axiom-networking-migration について
Swift 開発者向けに、ネットワーク API の選択と移行方法をまとめたガイドです。BSD Sockets、NWConnection、NetworkConnection、URLSession StreamTask を扱い、HTTP では URLSession を継続する判断も示します。
主な機能
- 01BSD Sockets から NWConnection への移行
- 02NWConnection から NetworkConnection への移行
- 03URLSession StreamTask の移行案内
- 04API 対応表とコード例
- 05ネットワーク API の選択指針
活用シーン
- 01ソケットコードを NWConnection に置き換える
- 02接続処理に async/await を導入する
- 03UDP や独自プロトコル向けの移行
- 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 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() } | 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
コメント
Markdown 対応