CtrlK
BlogDocsLog inGet started
Tessl Logo

dpearson2699/swift-ios-skills

Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.

71

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

network-framework.mdskills/ios-networking/references/

Network.framework

Low-level networking with the Network framework. Use when you need TCP/UDP connections, WebSockets, Bonjour service discovery, local servers, or network path monitoring beyond what URLSession provides. All examples target Swift 6.3 / iOS 26+.

Contents

  • NWConnection
  • NWListener
  • NWBrowser
  • NWPathMonitor
  • NWParameters Configuration
  • TLS Configuration
  • WebSocket Support
  • NetworkConnection (iOS 26+)

NWConnection

A bidirectional data connection between a local and remote endpoint. Supports TCP, UDP, TLS, DTLS, QUIC, and WebSocket protocols.

Documentation: sosumi.ai/documentation/network/nwconnection

TCP Connection

import Network

let connection = NWConnection(
    host: "api.example.com",
    port: 443,
    using: .tls
)

connection.stateUpdateHandler = { state in
    switch state {
    case .ready:
        print("Connected")
    case .waiting(let error):
        print("Waiting: \(error.localizedDescription)")
    case .failed(let error):
        print("Failed: \(error.localizedDescription)")
    case .cancelled:
        print("Cancelled")
    default:
        break
    }
}

connection.start(queue: .main)

Sending Data

func send(_ data: Data, on connection: NWConnection) {
    connection.send(
        content: data,
        contentContext: .defaultMessage,
        isComplete: true,
        completion: .contentProcessed { error in
            if let error {
                print("Send error: \(error)")
            }
        }
    )
}

Receiving Data

func receive(on connection: NWConnection) {
    connection.receive(
        minimumIncompleteLength: 1,
        maximumLength: 65536
    ) { content, contentContext, isComplete, error in
        if let data = content {
            handleData(data)
        }
        if isComplete {
            connection.cancel()
        } else if let error {
            print("Receive error: \(error)")
        } else {
            // Continue receiving
            receive(on: connection)
        }
    }
}

UDP Connection

let udpConnection = NWConnection(
    host: "239.0.0.1",
    port: 5000,
    using: .udp
)

udpConnection.stateUpdateHandler = { state in
    if case .ready = state {
        let message = "Hello".data(using: .utf8)!
        udpConnection.send(
            content: message,
            completion: .contentProcessed { _ in }
        )
    }
}

udpConnection.start(queue: .main)

Connection Lifecycle

States flow in order: setuppreparingreadycancelled/failed. A connection may enter waiting(NWError) if the network path is unavailable and retry when connectivity returns.

Always cancel connections when done:

connection.cancel()  // Releases resources, transitions to .cancelled

NWListener

An object that listens for incoming network connections. Use to create local TCP/UDP servers.

Documentation: sosumi.ai/documentation/network/nwlistener

TCP Server

import Network

func startServer(port: UInt16) throws -> NWListener {
    let listener = try NWListener(using: .tcp, on: NWEndpoint.Port(rawValue: port)!)

    listener.stateUpdateHandler = { state in
        switch state {
        case .ready:
            print("Listening on port \(listener.port?.rawValue ?? 0)")
        case .failed(let error):
            print("Listener failed: \(error)")
            listener.cancel()
        default:
            break
        }
    }

    listener.newConnectionHandler = { connection in
        print("New connection from \(connection.endpoint)")
        connection.stateUpdateHandler = { state in
            if case .ready = state {
                receive(on: connection)
            }
        }
        connection.start(queue: .main)
    }

    listener.start(queue: .main)
    return listener
}

Advertising via Bonjour

let listener = try NWListener(using: .tcp)
listener.service = NWListener.Service(
    name: "MyApp",
    type: "_myapp._tcp"
)
listener.serviceRegistrationUpdateHandler = { change in
    switch change {
    case .add(let endpoint):
        print("Service registered: \(endpoint)")
    default:
        break
    }
}
listener.start(queue: .main)

NWBrowser

Discovers Bonjour/mDNS services on the local network.

Documentation: sosumi.ai/documentation/network/nwbrowser

Service Discovery

import Network

let browser = NWBrowser(
    for: .bonjour(type: "_myapp._tcp", domain: nil),
    using: .tcp
)

browser.stateUpdateHandler = { state in
    if case .failed(let error) = state {
        print("Browser failed: \(error)")
    }
}

browser.browseResultsChangedHandler = { results, changes in
    for result in results {
        switch result.endpoint {
        case .service(let name, let type, let domain, _):
            print("Found: \(name).\(type)\(domain)")
        default:
            break
        }
    }

    for change in changes {
        switch change {
        case .added(let result):
            print("Added: \(result.endpoint)")
        case .removed(let result):
            print("Removed: \(result.endpoint)")
        default:
            break
        }
    }
}

browser.start(queue: .main)

Connecting to a Discovered Service

// result is an NWBrowser.Result from browseResultsChangedHandler
let connection = NWConnection(to: result.endpoint, using: .tcp)
connection.start(queue: .main)

NWPathMonitor

Monitors network path changes. Replaces the deprecated SCNetworkReachability API. Use to detect connectivity changes, expensive paths (cellular), and constrained paths (Low Data Mode).

Documentation: sosumi.ai/documentation/network/nwpathmonitor

Basic Reachability with AsyncStream

import Network

func networkStatusStream() -> AsyncStream<NWPath> {
    AsyncStream { continuation in
        let monitor = NWPathMonitor()
        monitor.pathUpdateHandler = { path in
            continuation.yield(path)
        }
        continuation.onTermination = { _ in
            monitor.cancel()
        }
        monitor.start(queue: DispatchQueue(label: "NetworkMonitor"))
    }
}

// Usage in a view model
@MainActor @Observable
class ConnectivityModel {
    var isConnected = true
    var isExpensive = false
    var isConstrained = false

    func startMonitoring() async {
        for await path in networkStatusStream() {
            isConnected = path.status == .satisfied
            isExpensive = path.isExpensive       // Cellular
            isConstrained = path.isConstrained   // Low Data Mode
        }
    }
}

Monitor Specific Interface

// Monitor only Wi-Fi
let wifiMonitor = NWPathMonitor(requiredInterfaceType: .wifi)
wifiMonitor.pathUpdateHandler = { path in
    print("Wi-Fi: \(path.status)")
}
wifiMonitor.start(queue: .global())

Key NWPath Properties

PropertyDescription
status.satisfied, .unsatisfied, or .requiresConnection
isExpensivetrue on cellular or personal hotspot
isConstrainedtrue when Low Data Mode is enabled
availableInterfacesArray of available NWInterface objects
supportsDNSWhether DNS resolution is available
supportsIPv4 / supportsIPv6Protocol family support

Adapting Behavior

for await path in networkStatusStream() {
    if path.isConstrained {
        // Low Data Mode: reduce image quality, skip prefetch
        imageQuality = .low
    } else if path.isExpensive {
        // Cellular: use standard quality, skip video preload
        imageQuality = .standard
    } else {
        // Wi-Fi: full quality, prefetch aggressively
        imageQuality = .high
    }
}

NWParameters Configuration

NWParameters defines the protocols and options for connections and listeners.

import Network

// TCP with custom options
let tcpParams = NWParameters.tcp
tcpParams.requiredInterfaceType = .wifi
tcpParams.prohibitExpensivePaths = true
tcpParams.prohibitConstrainedPaths = true

let tcpOptions = tcpParams.defaultProtocolStack
    .transportProtocol as! NWProtocolTCP.Options
tcpOptions.connectionTimeout = 10
tcpOptions.enableKeepalive = true
tcpOptions.keepaliveInterval = 30

let connection = NWConnection(
    host: "api.example.com",
    port: 8080,
    using: tcpParams
)

UDP Parameters

let udpParams = NWParameters.udp
let udpOptions = udpParams.defaultProtocolStack
    .transportProtocol as! NWProtocolUDP.Options
udpOptions.preferNoChecksum = true

let connection = NWConnection(
    host: "239.0.0.1",
    port: 5000,
    using: udpParams
)

TLS Configuration

Configure TLS for secure connections using NWProtocolTLS.Options.

import Network

let tlsParams = NWParameters(tls: NWProtocolTLS.Options())

// Access TLS options for customization
let tlsOptions = tlsParams.defaultProtocolStack
    .applicationProtocols.first as! NWProtocolTLS.Options

sec_protocol_options_set_min_tls_protocol_version(
    tlsOptions.securityProtocolOptions,
    .TLSv13
)

let connection = NWConnection(
    host: "secure.example.com",
    port: 443,
    using: tlsParams
)

Inspecting TLS Metadata

connection.stateUpdateHandler = { state in
    if case .ready = state {
        if let metadata = connection.metadata(
            definition: NWProtocolTLS.definition
        ) as? NWProtocolTLS.Metadata {
            let secMetadata = metadata.securityProtocolMetadata
            let negotiatedProtocol = sec_protocol_metadata_get_negotiated_tls_protocol_version(secMetadata)
            print("TLS version: \(negotiatedProtocol)")
        }
    }
}

WebSocket Support

Network.framework supports WebSocket via NWProtocolWebSocket.

WebSocket Client

import Network

func createWebSocketConnection(url: String) -> NWConnection {
    let wsOptions = NWProtocolWebSocket.Options()
    wsOptions.autoReplyPing = true

    let params = NWParameters.tls
    params.defaultProtocolStack.applicationProtocols.insert(
        wsOptions, at: 0
    )

    let connection = NWConnection(
        host: NWEndpoint.Host(url),
        port: 443,
        using: params
    )

    return connection
}

Sending WebSocket Messages

func sendWebSocketMessage(_ text: String, on connection: NWConnection) {
    let metadata = NWProtocolWebSocket.Metadata(opcode: .text)
    let context = NWConnection.ContentContext(
        identifier: "textMessage",
        metadata: [metadata]
    )

    let data = text.data(using: .utf8)
    connection.send(
        content: data,
        contentContext: context,
        isComplete: true,
        completion: .contentProcessed { error in
            if let error {
                print("WebSocket send error: \(error)")
            }
        }
    )
}

Receiving WebSocket Messages

func receiveWebSocketMessage(on connection: NWConnection) {
    connection.receiveMessage { data, context, isComplete, error in
        if let data,
           let metadata = context?.protocolMetadata(
               definition: NWProtocolWebSocket.definition
           ) as? NWProtocolWebSocket.Metadata {
            switch metadata.opcode {
            case .text:
                let text = String(data: data, encoding: .utf8) ?? ""
                print("Received text: \(text)")
            case .binary:
                print("Received binary: \(data.count) bytes")
            case .close:
                print("Connection closed")
                return
            default:
                break
            }
        }
        // Continue receiving
        receiveWebSocketMessage(on: connection)
    }
}

NetworkConnection (iOS 26+)

NetworkConnection is a new Swift-native API introduced in iOS 26 that provides a modern, type-safe alternative to NWConnection. It uses generics over protocol stacks, supports structured concurrency patterns, and integrates with async/await via closure-based state handlers.

Documentation: sosumi.ai/documentation/network/networkconnection

Key Differences from NWConnection

NWConnectionNetworkConnection
AvailabilityiOS 12+iOS 26+
Type safetyUntyped protocol stackGeneric ApplicationProtocol parameter
StreamsSingle connectionBuilt-in QUIC stream multiplexing
State updatesstateUpdateHandler callbackonStateUpdate, onPathUpdate closures
Wi-Fi AwareNot supportedwifiAware property
API styleCallback-basedClosure-based with Sendable support

Basic Usage

import Network

// Create with protocol stack parameters
let connection = NetworkConnection(
    to: .hostPort(host: "api.example.com", port: 443),
    using: .tls
)

connection.onStateUpdate { state in
    switch state {
    case .ready:
        print("Connected")
    case .failed(let error):
        print("Failed: \(error)")
    case .cancelled:
        print("Cancelled")
    default:
        break
    }
}

connection.start()

QUIC Multiplexed Streams

NetworkConnection supports QUIC stream multiplexing natively:

// Open a new bidirectional stream
let stream = connection.openStream(directionality: .bidirectional)

// Handle inbound streams
connection.inboundStreams { stream in
    // Process incoming stream
}

Migration Guidance

For new iOS 26+ projects, prefer NetworkConnection over NWConnection:

  • It provides stronger type safety through its generic ApplicationProtocol.
  • Stream multiplexing is a first-class concept.
  • The API is designed for modern Swift with Sendable conformance.

For projects supporting iOS versions before 26, continue using NWConnection. Both APIs coexist and NWConnection is not deprecated.

skills

CHANGELOG.md

README.md

tile.json