swift-nio/SKILL.md
Expert guidance on SwiftNIO best practices, patterns, and implementation. Use when developers mention: (1) SwiftNIO, NIO, ByteBuffer, Channel, ChannelPipeline, ChannelHandler, EventLoop, NIOAsyncChannel, or NIOFileSystem, (2) EventLoopFuture, ServerBootstrap, or DatagramBootstrap, (3) TCP/UDP server or client implementation, (4) ByteToMessageDecoder or wire protocol codecs, (5) binary protocol parsing or serialization, (6) blocking the event loop issues.
npx skillsauth add wendylabsinc/claude-skills swift-nioInstall 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.
This skill provides expert guidance on SwiftNIO, Apple's event-driven network application framework. Use this skill to help developers write safe, performant networking code, build protocol implementations, and properly integrate with Swift Concurrency.
Package.swift to determine which SwiftNIO packages are used.EventLoopFuture chains.NIOAsyncChannel and structured concurrency over legacy ChannelHandler patterns for new code.EventLoopFuture/EventLoopPromise only for low-level protocol implementations.ByteBuffer, always consider memory ownership and avoid unnecessary copies.When a developer needs SwiftNIO guidance, follow this decision tree:
Building a TCP/UDP server or client?
references/Channels.md for Channel concepts and NIOAsyncChannelServerBootstrap for TCP servers, DatagramBootstrap for UDPUnderstanding EventLoops?
references/EventLoops.md for event loop conceptsWorking with binary data?
references/ByteBuffer.md for buffer operationsImplementing a binary protocol?
references/ByteToMessageCodecs.md for codec patternsByteToMessageDecoder and MessageToByteEncoderMigrating from EventLoopFuture to async/await?
.get() to bridge futures to asyncNIOAsyncChannel for channel-based async code"Blocking the EventLoop"
NIOThreadPoolreferences/EventLoops.mdType mismatch crash in ChannelPipeline
InboundOut of handler N matches InboundIn of handler N+1OutboundOut of handler N matches OutboundIn of handler N-1references/Channels.mdImplementing binary protocol serialization
ByteToMessageDecoder for parsing bytes into messagesMessageToByteEncoder for serializing messages to bytesreadLengthPrefixedSlice and writeLengthPrefixed helpersreferences/ByteToMessageCodecs.mdMemory issues with ByteBuffer
readSlice instead of readBytes when possiblereferences/ByteBuffer.mdDeadlock when waiting for EventLoopFuture
.wait() on a future from within the same EventLoop.get() from async contexts or chain with .flatMaplet server = try await ServerBootstrap(group: MultiThreadedEventLoopGroup.singleton)
.bind(host: "0.0.0.0", port: 8080) { channel in
channel.eventLoop.makeCompletedFuture {
try NIOAsyncChannel(
wrappingChannelSynchronously: channel,
configuration: .init(
inboundType: ByteBuffer.self,
outboundType: ByteBuffer.self
)
)
}
}
try await withThrowingDiscardingTaskGroup { group in
try await server.executeThenClose { clients in
for try await client in clients {
group.addTask {
try await handleClient(client)
}
}
}
}
// Preferred: Use the singleton
let group = MultiThreadedEventLoopGroup.singleton
// Get any EventLoop from the group
let eventLoop = group.any()
// From EventLoopFuture to async
let result = try await someFuture.get()
// From async to EventLoopFuture
let future = eventLoop.makeFutureWithTask {
try await someAsyncOperation()
}
var buffer = ByteBufferAllocator().buffer(capacity: 1024)
// Writing
buffer.writeString("Hello")
buffer.writeInteger(UInt32(42))
// Reading
let string = buffer.readString(length: 5)
let number = buffer.readInteger(as: UInt32.self)
Load these files as needed for specific topics:
EventLoops.md - EventLoop concepts, nonblocking I/O, why blocking is badChannels.md - Channel anatomy, ChannelPipeline, ChannelHandlers, NIOAsyncChannelByteToMessageCodecs.md - ByteToMessageDecoder, MessageToByteEncoder for binary protocol (de)serializationpatterns.md - Advanced integration patterns: ServerChildChannel abstraction, state machines, noncopyable ResponseWriter, graceful shutdown, ByteBuffer patternsNIOAsyncChannel over legacy handlersMultiThreadedEventLoopGroup.singletonWhen parsing or serializing binary data (especially for network protocols), use SwiftNIO's ByteBuffer instead of Foundation's Data. ByteBuffer provides:
When converting between ByteBuffer and Data, use NIOFoundationCompat:
import NIOFoundationCompat
// ByteBuffer to Data
let data = Data(buffer: byteBuffer)
// Data to ByteBuffer - use writeData for better performance
var buffer = ByteBuffer()
buffer.writeData(data) // Faster than writeBytes(data)
Bad - Using Data with manual byte manipulation:
var buffer = Data()
var messageLength: UInt32?
for try await message in inbound {
buffer.append(message)
if messageLength == nil && buffer.count >= 4 {
messageLength = UInt32(buffer[0]) << 24
| UInt32(buffer[1]) << 16
| UInt32(buffer[2]) << 8
| UInt32(buffer[3])
buffer = Data(buffer.dropFirst(4)) // Copies data!
}
}
Good - Using ByteBuffer:
var buffer = ByteBuffer()
for try await message in inbound {
buffer.writeBytes(message)
if buffer.readableBytes >= 4 {
let readerIndex = buffer.readerIndex
guard let messageLength = buffer.readInteger(endianness: .big, as: UInt32.self) else {
continue
}
if buffer.readableBytes >= messageLength {
guard let bytes = buffer.readBytes(length: Int(messageLength)) else { continue }
// Process bytes...
} else {
// Not enough data yet, reset reader index
buffer.moveReaderIndex(to: readerIndex)
}
}
}
There are several "bag of bytes" data structures in Swift:
| Type | Source | Platform | Notes |
|------|--------|----------|-------|
| Array<UInt8> | stdlib | All | Safe, growable, good for Embedded Swift |
| InlineArray<N, UInt8> | stdlib (6.1+) | All | Fixed-size, stack-allocated, no heap allocation |
| Data | Foundation | All (large binary) | Not always contiguous on Apple platforms |
| ByteBuffer | SwiftNIO | All (requires NIO) | Best for network protocols, not Embedded |
| Span<UInt8> | stdlib (6.2+) | All | Zero-copy view, requires Swift 6.2+ |
| UnsafeBufferPointer<UInt8> | stdlib | All | Unsafe, manual memory management |
Recommendations:
Data is fine due to framework integrationByteBuffer is required for I/O operations[UInt8] and InlineArraySpan<UInt8> (Swift 6.2+) allows any backing typeUse executeThenClose to get inbound/outbound streams from NIOAsyncChannel:
return try await channel.executeThenClose { inbound, outbound in
let socket = Client(inbound: inbound, outbound: outbound, channel: channel.channel)
return try await perform(client)
}
When exposing async sequences that wrap NIO types:
AsyncSequence wrapper struct with internal NIO streamAsyncIterator transforms NIO types to public typesDatagramBootstrap for UDP socketsAddressedEnvelope<ByteBuffer> containing remote address and dataMulticastChannel protocolSocketOptionValue (Int32) typeso_reuseport is only available on Linuxdevelopment
Autonomous continuous integration loop for the Wendy Cloud repository. Use when asked to: (1) iterate on the cloud stack, (2) find and fix bugs in Wendy Cloud, (3) run the cloud test loop, (4) continuously test the Swift broker, (5) scan for regressions after new features. This skill manages the full local dev stack autonomously including starting Docker, the Swift broker, pki-core, running tests, diagnosing failures, and applying fixes.
development
Expert guidance on Swift best practices, patterns, and implementation. Use when developers mention: (1) Swift configuration or environment variables, (2) swift-log or logging patterns, (3) OpenTelemetry or swift-otel, (4) Swift Testing framework or @Test macro, (5) Foundation avoidance or cross-platform Swift, (6) platform-specific code organization, (7) Span or memory safety patterns, (8) non-copyable types (~Copyable), (9) API design patterns or access modifiers.
tools
Expert guidance on building and deploying apps to WendyOS edge devices. Use when developers mention: (1) Wendy or WendyOS, (2) wendy CLI commands, (3) wendy.json or entitlements, (4) deploying apps to edge devices, (5) remote debugging Swift on ARM64, (6) NVIDIA Jetson or Raspberry Pi apps, (7) cross-compiling Swift for ARM64.
development
Curated Swift package ecosystem for WendyOS and Linux. Use when developers mention: (1) Swift packages for Linux or ARM64/AMD64, (2) choosing a Swift library, (3) Swift Package Index, (4) swiftpackageindex.com, (5) what Swift library to use, (6) Swift on WendyOS dependencies, (7) edge computing Swift libraries.