ai/ios-skills/ios-axiom-networking-legacy/SKILL.md
This skill should be used when working with NWConnection patterns for iOS 12-25, supporting apps that can't use async/await yet, or maintaining backward compatibility with completion handler networking.
npx skillsauth add kurko/dotfiles axiom-networking-legacyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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).
Use when Supporting iOS 12-25, need TLS encryption, can't use async/await yet
Time cost 10-15 minutes
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
}
}
}
[weak self] in all completion handlers to prevent retain cyclesUse 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.
// 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
}
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
}
}
}
WWDC 2018 demo Live video streaming showed 30% lower CPU on receiver with user-space networking + batching
Use when Need to accept incoming connections, building servers or peer-to-peer apps, supporting iOS 12-25
Time cost 20-25 minutes
// 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...
}
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
}
}
}
NWListener(using: .tls, on: port)Use when Discovering services on local network (Bonjour), building peer-to-peer apps, supporting iOS 12-25
Time cost 25-30 minutes
// 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?
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)
}
Skills: axiom-networking, axiom-network-framework-ref, axiom-networking-migration
tools
Create a GitHub pull request from the current branch. Use when user asks to create a PR, open a PR, submit a PR, push and create PR, or similar pull request workflows. Activates for phrases like "create a PR", "open a pull request", "submit PR", "push and PR", "make a PR for this", "open a draft PR".
data-ai
Merge the current worktree branch into main and sync main back. Use when the user says "merge to main", "ship it", "merge and continue", or after completing a task in a worktree and wanting to continue with the next one.
tools
Synchronize AI agent skills, commands, configs, permissions, hooks, and instructions across Claude Code, Codex CLI, and other Agent Skills-compatible tools. Use when the user asks to pull skills from Claude into Codex, sync Codex work back to Claude, migrate agent commands, reconcile frontmatter, update permissions, or keep agent setup files in parity.
testing
Write or update UI-independent use cases for QA. Use when the user says "write use cases", "add use cases", "QA use cases", "update use cases", "compose use cases", or when starting implementation of a new feature (after plan approval). Also activates for "what should we test", "regression cases", or "use cases for QA".