ai/ios-skills/ios-axiom-networking-migration/SKILL.md
Network framework migration guides. Use when migrating from BSD sockets to NWConnection, NWConnection to NetworkConnection (iOS 26+), or URLSession StreamTask to NetworkConnection.
npx skillsauth add kurko/dotfiles axiom-networking-migrationInstall 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.
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
| BSD Sockets | NWConnection | Notes |
|-------------|--------------|-------|
| socket() + connect() | NWConnection(host:port:using:) + start() | Non-blocking by default |
| send() / sendto() | connection.send(content:completion:) | Async, returns immediately |
| recv() / recvfrom() | connection.receive(minimumIncompleteLength:maximumLength:completion:) | Async, returns immediately |
| bind() + listen() | NWListener(using:on:) | Automatic port binding |
| accept() | listener.newConnectionHandler | Callback for each connection |
| getaddrinfo() | Let NWConnection handle DNS | Smart resolution with racing |
| SCNetworkReachability | connection.stateUpdateHandler waiting state | No race conditions |
| setsockopt() | NWParameters configuration | Type-safe options |
// 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)
}
// 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)
| NWConnection (iOS 12-25) | NetworkConnection (iOS 26+) | Notes |
|-------------------------|----------------------------|-------|
| connection.stateUpdateHandler = { state in } | for await state in connection.states { } | Async sequence |
| connection.send(content:completion:) | try await connection.send(content) | Suspending function |
| connection.receive(minimumIncompleteLength:maximumLength:completion:) | try await connection.receive(exactly:) | Suspending function |
| Manual JSON encode/decode | Coder(MyType.self, using: .json) | Built-in Codable support |
| Custom framer | TLV { TLS() } | Built-in Type-Length-Value |
| [weak self] everywhere | No [weak self] needed | Task cancellation automatic |
// 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)")
}
}
}
// 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)")
}
// 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)")
}
}
// 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)")
}
Skills: axiom-ios-networking, axiom-networking-legacy
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".