For several occasions now, I’ve found myself in the need to spin up a simple TCP server when writing tests. Usually my needs are very simple, I just need to accept some connection and stub some responses.
There are plenty of libraries out there that serve this purpose but it feels such an overkill to add another dependency just for running my tests. So I usually just end up calling socket
, bind
and listen
even though that’s still a lot of code for something so simple and feels out of place in Cocoa.
More recently I discovered NSSocketPort
and it sure looked like it could replace most of that low level socket code, so I took it for a spin:
let serverSocket = SocketPort()
let serverHandle = FileHandle(fileDescriptor: serverSocket.socket, closeOnDealloc: true)
NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
object: serverHandle,
queue: OperationQueue.main) { (note) in
guard let acceptedFileHandle = note.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else {
return
}
// read and write to acceptedFileHandle
// to communicate with your client
}
serverHandle.acceptConnectionInBackgroundAndNotify()
With the example code above you’re now ready to accept and respond to TCP connections. And you get to do that using NSFileHandle
where you can read and write NSData
directly and install a readabilityHandler
to be notified when data arrives.
Note no port was specified, this was on purpose, I wanted the OS to automatically choose any available port. To find out which port was chosen you have to look at the NSSocketPort
’s address a delve down to C for a bit.
var serverPort: UInt16 = 0
serverSocket.address.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
var addr = sockaddr_in()
memcpy(&addr, bytes, MemoryLayout<sockaddr_in>.size)
serverPort = addr.sin_port.bigEndian
}
And voilá. Next time you need to do something similar perhaps you’ll think twice before jumping on GitHub 🙃.