mcpField
A
axiom-networking-legacy

axiom-networking-legacy

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

axiom-networking-legacy について

iOS 12〜25対応アプリ向けのSwiftネットワークリファレンススキルです。コールバック方式のNWConnectionを使ったTLS、UDPの一括送信、接続待ち受け、Bonjourによる検出を扱います。

主な機能

  1. 01NWConnectionによるTLS接続
  2. 02UDPデータグラムの一括送信
  3. 03NWListenerによる接続受け入れ
  4. 04Bonjourサービスの検出

活用シーン

  1. 01iOS 12〜25向けアプリの保守
  2. 02TLSによるネットワーク接続の暗号化
  3. 03頻繁なUDP送信の負荷軽減
  4. 04ローカルネットワーク上のピア検出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-networking-legacy

インストール後、Claude Code で/axiom-networking-legacyを入力して読み込まれたか確認します。

詳細

レガシー iOS 12-25 の NWConnection パターン

これらのパターンでは、iOS 12-25 をサポートするアプリで、完了ハンドラーを使って NWConnection を使用します。アプリのターゲットが iOS 26 以降の場合は、代わりに async/await を使う NetworkConnection を使用してください(axiom-network-framework-ref スキルを参照)。

パターン 2a:TLS を使った NWConnection(iOS 12-25)

使用する場面 iOS 12-25 をサポートしており、TLS 暗号化が必要で、まだ async/await を使用できない場合

所要時間 10-15 分

GOOD:完了ハンドラーを使った NWConnection
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
        }
    }
}
NetworkConnection との主な違い
  • 保持サイクルを防ぐため、すべての完了ハンドラーで [weak self] を使用する必要があります
  • stateUpdateHandler は非同期シーケンスではなく、状態を受け取ります
  • send/receive は async/await ではなく、完了コールバックを使用します
使用する場面
  • iOS 12-15 をサポートする場合(2024 年時点でデバイスの 70%)
  • まだ async/await を使用していないコードベース
  • 下位互換性が必要なライブラリ
NetworkConnection への移行(iOS 26+)
  • stateUpdateHandler -> connection.states 非同期シーケンス
  • 完了ハンドラー -> try await 呼び出し
  • [weak self] -> 不要になります(async/await がキャンセルを処理します)

パターン 2b:NWConnection による UDP バッチ処理(iOS 12-25)

使用する場面 iOS 12-25 をサポートし、複数の UDP データグラムを効率的に送信して、CPU 使用率を約 30%削減する必要がある場合

所要時間 10-15 分

背景 従来の UDP ソケットでは、システムコールごとに 1 つのデータグラムを送信します。小さなパケットを 100 個送信すると、コンテキストスイッチが 100 回発生します。バッチ処理を行うと、これを約 1 回のシステムコールに削減できます。

BAD:UDP を個別に送信(CPU 使用率が高い)
// 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:UDP をバッチ送信(CPU 使用率を 30%削減)
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
        }
    }
}
パフォーマンス特性
  • バッチ処理なし 100 個のデータグラム = 100 回のシステムコール = 100 回のコンテキストスイッチ
  • バッチ処理あり 100 個のデータグラム = 約 1 回のシステムコール = 1 回のコンテキストスイッチ
  • 結果 CPU 使用率を約 30%削減(Instruments で測定)
使用する場面
  • リアルタイムの動画/音声ストリーミング
  • 頻繁に更新されるゲーム(プレイヤーの位置など)
  • 高頻度のセンサーデータ(IoT)

WWDC 2018 のデモ ユーザー空間ネットワークとバッチ処理を使ったライブ動画ストリーミングでは、受信側の CPU 使用率が 30%低下しました

パターン 2c:NWListener(iOS 12-25)

使用する場面 着信接続を受け入れる必要があり、サーバーやピアツーピアアプリを構築していて、iOS 12-25 をサポートする場合

所要時間 20-25 分

BAD:ソケットの手動リスニング
// 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
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
        }
    }
}
使用する場面
  • ピアツーピアアプリ(ファイル共有、メッセージング)
  • ローカルネットワークサービス
  • 開発/テスト用サーバー
Bonjour のアドバタイズ
  • ローカルネットワーク上のサービスを自動検出します
  • IP アドレスをハードコードする必要はありません
  • 検出には NWBrowser を使用できます
セキュリティに関する考慮事項
  • 暗号化には TLS パラメーターを使用します:NWListener(using: .tls, on: port)
  • データを処理する前に、クライアント接続を検証します
  • DoS を防ぐため、接続数の上限を設定します

パターン 2d:ネットワーク検出(iOS 12-25)

使用する場面 ローカルネットワーク上のサービス(Bonjour)を検出する必要があり、ピアツーピアアプリを構築していて、iOS 12-25 をサポートする場合

所要時間 25-30 分

BAD:ハードコードされた IP アドレス
// 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 を使用
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)
}
使用する場面
  • ピアツーピア検出(AirDrop のような機能)
  • ローカルネットワーク上のプリンター、メディアサーバー
  • 開発/テスト(テストサーバーを自動検出)
パフォーマンス特性
  • mDNS ベース(マルチキャスト DNS。中央サーバーは不要)
  • 同じサブネット上ではほぼ瞬時に検出
  • サービスの出現/消失に応じて自動更新
iOS 26 以降の代替手段
  • インフラなしのピアツーピア通信には、Wi-Fi Aware を使う NetworkBrowser を使用します
  • axiom-network-framework-ref スキルのパターン 1d を参照してください

リソース

スキル:axiom-networking、axiom-network-framework-ref、axiom-networking-migration

コメント