What it is
SwiftNIO is Apple's event-driven network application framework, providing the non-blocking I/O foundation for high-performance Swift servers.
A channel pipeline of handlers processes inbound and outbound data. EventLoops are the threads, and work must never block them.
Installation
.package(url: "https://github.com/apple/swift-nio.git", from: "2.76.0")Getting started
The smallest useful thing you can do with it, and what each part means.
swift
final class EchoHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let buffer = unwrapInboundIn(data)
context.write(wrapOutboundOut(buffer), promise: nil)
}
func channelReadComplete(context: ChannelHandlerContext) {
context.flush() // batch writes, then flush once
}
}
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
defer { try! group.syncShutdownGracefully() }
let channel = try ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.backlog, value: 256)
.childChannelInitializer { $0.pipeline.addHandler(EchoHandler()) }
.bind(host: "0.0.0.0", port: 8080)
.wait()
try channel.closeFuture.wait()Advanced usage
Where the library earns its place over a simpler alternative.
swift
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let request = unwrapInboundIn(data)
// Blocking here stalls every other connection on this event loop.
threadPool.runIfActive(eventLoop: context.eventLoop) {
try expensiveBlockingWork(request)
}.whenComplete { result in
// Back on the event loop — safe to touch the context.
switch result {
case .success(let response):
context.writeAndFlush(self.wrapOutboundOut(response), promise: nil)
case .failure:
context.close(promise: nil)
}
}
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Throughput collapses under concurrency
- Something is blocking an event loop. Audit handlers for synchronous I/O, locks or heavy computation.
- Precondition failure about the wrong event loop
- A ChannelHandlerContext was touched from another thread. Hop back with eventLoop.execute before using it.
Best practices
- Never block an EventLoop; offload to a NIOThreadPool and hop back.
- Batch writes and flush once in channelReadComplete.
- Use Vapor or Hummingbird unless you need protocol-level control.
- Shut the EventLoopGroup down gracefully so connections close cleanly.
Background
Why it exists, and what it was reacting to.
Modelled on Netty, SwiftNIO is what made server-side Swift viable. Vapor, Hummingbird and Swift's gRPC implementation all build on it.
