Structures

The following structures are available globally.

  • A data structure for processing addressed datagrams, such as those used by UDP.

    The AddressedEnvelope is used extensively on DatagramChannels in order to keep track of source or destination address metadata: that is, where some data came from or where it is going.

    See more

    Declaration

    Swift

    public struct AddressedEnvelope<DataType>
  • ByteBuffer stores contiguously allocated raw bytes. It is a random and sequential accessible sequence of zero or more bytes (octets).

    Allocation

    Use allocator.buffer(capacity: desiredCapacity) to allocate a new ByteBuffer.

    Supported types

    A variety of types can be read/written from/to a ByteBuffer. Using Swift’s extension mechanism you can easily create ByteBuffer support for your own data types. Out of the box, ByteBuffer supports for example the following types (non-exhaustive list):

    • String/StaticString
    • Swift’s various (unsigned) integer types
    • Foundation‘s Data
    • [UInt8] and generally any Collection (& ContiguousCollection) of UInt8

    Random Access

    For every supported type ByteBuffer usually contains two methods for random access:

    1. get<type>(at: Int, length: Int) where <type> is for example String, Data, Bytes (for [UInt8])
    2. set(<type>: Type, at: Int)

    Example:

    var buf = ...
    buf.set(string: "Hello World", at: 0)
    let helloWorld = buf.getString(at: 0, length: 11)
    
    buf.set(integer: 17 as Int, at: 11)
    let seventeen: Int = buf.getInteger(at: 11)
    

    If needed, ByteBuffer will automatically resize its storage to accommodate your set request.

    Sequential Access

    ByteBuffer provides two properties which are indices into the ByteBuffer to support sequential access:

    • readerIndex, the index of the next readable byte
    • writerIndex, the index of the next byte to write

    For every supported type ByteBuffer usually contains two methods for sequential access:

    1. read<type>(length: Int) to read length bytes from the current readerIndex (and then advance the reader index by length bytes)
    2. write(<type>: Type) to write, advancing the writerIndex by the appropriate amount

    Example:

     var buf = ...
     buf.write(string: "Hello World")
     buf.write(integer: 17 as Int)
     let helloWorld = buf.readString(length: 11)
     let seventeen: Int = buf.readInteger()
    

    Layout

    +-------------------+------------------+------------------+
    | discardable bytes |  readable bytes  |  writable bytes  |
    |                   |     (CONTENT)    |                  |
    +-------------------+------------------+------------------+
    |                   |                  |                  |
    0      <=      readerIndex   <=   writerIndex    <=    capacity
    

    The 'discardable bytes’ are usually bytes that have already been read, they can however still be accessed using the random access methods. ‘Readable bytes’ are the bytes currently available to be read using the sequential access interface (read<Type>/write<Type>). Getting writableBytes (bytes beyond the writer index) is undefined behaviour and might yield arbitrary bytes (not 0 initialised).

    Slicing

    ByteBuffer supports slicing a ByteBuffer without copying the underlying storage.

    Example:

    var buf = ...
    let dataBytes: [UInt8] = [0xca, 0xfe, 0xba, 0xbe]
    let dataBytesLength = UInt32(dataBytes.count)
    buf.write(integer: dataBytesLength) /* the header */
    buf.write(bytes: dataBytes) /* the data */
    let bufDataBytesOnly = buf.getSlice(at: 4, length: dataBytes.count)
    /* `bufDataByteOnly` and `buf` will share their storage */
    

    Important usage notes

    Each method that is prefixed with get is considered unsafe as it allows the user to read uninitialized memory if the index or index + length points outside of the previous written range of the ByteBuffer. Because of this it’s strongly advised to prefer the usage of methods that start with the read prefix and only use the get prefixed methods if there is a strong reason for doing so. In any case, if you use the get prefixed methods you are responsible for ensuring that you do not reach into uninitialized memory by taking the readableBytes and readerIndex into account, and ensuring that you have previously written into the area covered by the index itself.

    See more

    Declaration

    Swift

    public struct ByteBuffer
  • The preferred allocator for ByteBuffer values. The allocation strategy is opaque but is currently libc’s malloc, realloc and free.

    Note

    ByteBufferAllocator is thread-safe.
    See more

    Declaration

    Swift

    public struct ByteBufferAllocator
  • A Channel user event that is sent when the Channel has been asked to quiesce.

    The action(s) that should be taken after receiving this event are both application and protocol dependent. If the protocol supports a notion of requests and responses, it might make sense to stop accepting new requests but finish processing the request currently in flight.

    See more

    Declaration

    Swift

    public struct ChannelShouldQuiesceEvent
  • An automatically expanding ring buffer implementation backed by a ContiguousArray. Even though this implementation will automatically expand if more elements than initialRingCapacity are stored, it’s advantageous to prevent expansions from happening frequently. Expansions will always force an allocation and a copy to happen.

    See more

    Declaration

    Swift

    public struct CircularBuffer<E> : CustomStringConvertible, AppendableCollection
  • An Error that represents multiple errors that occurred during an operation.

    Because SwiftNIO frequently does multiple jobs at once, it is possible some operations out of a set of operations may fail. This is most noticeable with Channel.flush on the DatagramChannel, where some, but not all, of the datagram writes may fail. In this case the flush should accurately represent that set of possibilities. The NIOCompositeError is an attempt to do so.

    See more

    Declaration

    Swift

    public struct NIOCompositeError : Error
  • Returned once a task was scheduled on the EventLoop for later execution.

    A Scheduled allows the user to either cancel() the execution of the scheduled task (if possible) or obtain a reference to the EventLoopFuture that will be notified once the execution is complete.

    See more

    Declaration

    Swift

    public struct Scheduled<T>
  • An iterator over the EventLoops forming an EventLoopGroup.

    Usually returned by an EventLoopGroup‘s makeIterator() method.

    let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
    group.makeIterator()?.forEach { loop in
        // Do something with each loop
    }
    
    See more

    Declaration

    Swift

    public struct EventLoopIterator : Sequence, IteratorProtocol
  • Represents a time interval.

    Note

    TimeAmount should not be used to represent a point in time.
    See more

    Declaration

    Swift

    public struct TimeAmount
  • A promise to provide a result later.

    This is the provider API for EventLoopFuture<T>. If you want to return an unfulfilled EventLoopFuture<T> – presumably because you are interfacing to some asynchronous service that will return a real result later, follow this pattern:

    func someAsyncOperation(args) -> EventLoopFuture<ResultType> {
        let promise = eventLoop.newPromise(of: ResultType.self)
        someAsyncOperationWithACallback(args) { result -> Void in
            // when finished...
            promise.succeed(result: result)
            // if error...
            promise.fail(error: error)
        }
        return promise.futureResult
    }
    

    Note that the future result is returned before the async process has provided a value.

    It’s actually not very common to use this directly. Usually, you really want one of the following:

  • If you have an EventLoopFuture and want to do something else after it completes, use .then()
  • If you just want to get a value back after running something on another thread, use EventLoopFuture<ResultType>.async()
  • If you already have a value and need an EventLoopFuture<> object to plug into some other API, create an already-resolved object with eventLoop.newSucceededFuture(result) or eventLoop.newFailedFuture(error:).

  • Note

    EventLoopPromise has reference semantics.

    See more

    Declaration

    Swift

    public struct EventLoopPromise<T>
  • A FileRegion represent a readable portion usually created to be sent over the network.

    Usually a FileRegion will allow the underlying transport to use sendfile to transfer its content and so allows transferring the file content without copying it into user-space at all. If the actual transport implementation really can make use of sendfile or if it will need to copy the content to user-space first and use write / writev is an implementation detail. That said using FileRegion is the recommended way to transfer file content if possible.

    One important note, depending your ChannelPipeline setup it may not be possible to use a FileRegion as a ChannelHandler may need access to the bytes (in a ByteBuffer) to transform these.

    Note

    It is important to manually manage the lifetime of the FileHandle used to create a FileRegion.
    See more

    Declaration

    Swift

    public struct FileRegion
  • An error that occurred during connection to a given target.

    See more

    Declaration

    Swift

    public struct SingleConnectionFailure
  • A representation of all the errors that happened during an attempt to connect to a given host and port.

    See more

    Declaration

    Swift

    public struct NIOConnectionError
  • An Error for an IO operation.

    See more

    Declaration

    Swift

    public struct IOError : Swift.Error
  • A circular buffer that allows one object at a time to be marked and easily identified and retrieved later.

    This object is used extensively within SwiftNIO to handle flushable buffers. It can be used to store buffered writes and mark how far through the buffer the user has flushed, and therefore how far through the buffer is safe to write.

    See more

    Declaration

    Swift

    public struct MarkedCircularBuffer<E> : CustomStringConvertible, AppendableCollection
  • NIOAny is an opaque container for values of any type, similar to Swift’s builtin Any type. Contrary to Any the overhead of NIOAny depends on the the type of the wrapped value. Certain types that are important for the performance of a SwiftNIO application like ByteBuffer, FileRegion and AddressEnvelope<ByteBuffer> can be expected to be wrapped almost without overhead. All others will have similar performance as if they were passed as an Any as NIOAny just like Any will contain them within an existential container.

    The most important use-cases for NIOAny are values travelling through the ChannelPipeline whose type can’t be calculated at compile time. For example:

    The abstraction that delivers a NIOAny to user code must provide a mechanism to unwrap a NIOAny as a certain type known at run-time. Canonical example:

    class SandwichHandler: ChannelInboundHandler {
        typealias InboundIn = Bacon /* we expected to be delivered `Bacon` ... */
        typealias InboundOut = Sandwich /* ... and we will make and deliver a `Sandwich` from that */
    
        func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
             /* we receive the `Bacon` as a `NIOAny` as at compile-time the exact configuration of the channel
                pipeline can't be computed. The pipeline can't be computed at compile time as it can change
                dynamically at run-time. Yet, we assert that in any configuration the channel handler before
                `SandwichHandler` does actually send us a stream of `Bacon`.
             */
             let bacon = self.unwrapInboundIn(data) /* `Bacon` or crash */
             let sandwich = makeSandwich(bacon)
             ctx.fireChannelRead(self.wrapInboundOut(sandwich)) /* as promised we deliver a wrapped `Sandwich` */
        }
    }
    
    See more

    Declaration

    Swift

    public struct NIOAny
  • NonBlockingFileIO is a helper that allows you to read files without blocking the calling thread.

    It is worth noting that kqueue, epoll or poll returning claiming a file is readable does not mean that the data is already available in the kernel’s memory. In other words, a read from a file can still block even if reported as readable. This behaviour is also documented behaviour:

    • poll: Regular files shall always poll TRUE for reading and writing.
    • epoll: epoll is simply a faster poll(2), and can be used wherever the latter is used since it shares the same semantics.
    • kqueue: Returns when the file pointer is not at the end of file.

    NonBlockingFileIO helps to work around this issue by maintaining its own thread pool that is used to read the data from the files into memory. It will then hand the (in-memory) data back which makes it available without the possibility of blocking.

    See more

    Declaration

    Swift

    public struct NonBlockingFileIO
  • A ThreadSpecificVariable is a variable that can be read and set like a normal variable except that it holds different variables per thread.

    ThreadSpecificVariable is thread-safe so it can be used with multiple threads at the same time but the value returned by currentValue is defined per thread.

    Note

    ThreadSpecificVariable has reference semantics.
    See more

    Declaration

    Swift

    public struct ThreadSpecificVariable<T> where T : AnyObject