axiom-networking-legacy

Legacy iOS 12-25 NWConnection Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "axiom-networking-legacy" with this command: npx skills add fotescodev/ios-agent-skills/fotescodev-ios-agent-skills-axiom-networking-legacy

Legacy iOS 12-25 NWConnection Patterns

These patterns use NWConnection with completion handlers for apps supporting iOS 12-25. If your app targets iOS 26+, use NetworkConnection with async/await instead (see axiom-network-framework-ref skill).

Pattern 2a: NWConnection with TLS (iOS 12-25)

Use when Supporting iOS 12-25, need TLS encryption, can't use async/await yet

Time cost 10-15 minutes

GOOD: NWConnection with Completion Handlers

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
    }
}

}

Key differences from NetworkConnection

  • Must use [weak self] in all completion handlers to prevent retain cycles

  • stateUpdateHandler receives state, not async sequence

  • send/receive use completion callbacks, not async/await

When to use

  • Supporting iOS 12-15 (70% of devices as of 2024)

  • Codebases not yet using async/await

  • Libraries needing backward compatibility

Migration to NetworkConnection (iOS 26+)

  • stateUpdateHandler -> connection.states async sequence

  • Completion handlers -> try await calls

  • [weak self] -> No longer needed (async/await handles cancellation)

Pattern 2b: NWConnection UDP Batch (iOS 12-25)

Use when Supporting iOS 12-25, sending multiple UDP datagrams efficiently, need ~30% CPU reduction

Time cost 10-15 minutes

Background Traditional UDP sockets send one datagram per syscall. If you're sending 100 small packets, that's 100 context switches. Batching reduces this to ~1 syscall.

BAD: Individual UDP Sends (High 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: Batched UDP Sends (30% Lower CPU)

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
    }
}

}

Performance characteristics

  • Without batch 100 datagrams = 100 syscalls = 100 context switches

  • With batch 100 datagrams = ~1 syscall = 1 context switch

  • Result ~30% lower CPU usage (measured with Instruments)

When to use

  • Real-time video/audio streaming

  • Gaming with frequent updates (player position)

  • High-frequency sensor data (IoT)

WWDC 2018 demo Live video streaming showed 30% lower CPU on receiver with user-space networking + batching

Pattern 2c: NWListener (iOS 12-25)

Use when Need to accept incoming connections, building servers or peer-to-peer apps, supporting iOS 12-25

Time cost 20-25 minutes

BAD: Manual Socket Listening

// 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 with Automatic Connection Handling

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
    }
}

}

When to use

  • Peer-to-peer apps (file sharing, messaging)

  • Local network services

  • Development/testing servers

Bonjour advertising

  • Automatic service discovery on local network

  • No hardcoded IPs needed

  • Works with NWBrowser for discovery

Security considerations

  • Use TLS parameters for encryption: NWListener(using: .tls, on: port)

  • Validate client connections before processing data

  • Set connection limits to prevent DoS

Pattern 2d: Network Discovery (iOS 12-25)

Use when Discovering services on local network (Bonjour), building peer-to-peer apps, supporting iOS 12-25

Time cost 25-30 minutes

BAD: Hardcoded IP Addresses

// 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 for Service Discovery

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)

}

When to use

  • Peer-to-peer discovery (AirDrop-like features)

  • Local network printers, media servers

  • Development/testing (find test servers automatically)

Performance characteristics

  • mDNS-based (multicast DNS, no central server)

  • Near-instant discovery on same subnet

  • Automatic updates when services appear/disappear

iOS 26+ alternative

  • Use NetworkBrowser with Wi-Fi Aware for peer-to-peer without infrastructure

  • See Pattern 1d in axiom-network-framework-ref skill

Resources

Skills: axiom-networking, axiom-network-framework-ref, axiom-networking-migration

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

axiom-swiftui-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-avfoundation-ref

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-testflight-triage

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-app-discoverability

No summary provided by upstream source.

Repository SourceNeeds Review