SSE

0.1.0

A minimal parser for Server-Sent Event streams
DePasqualeOrg/swift-sse

What's New

0.1.0

2026-03-07T20:44:07Z

Initial release

Swift SSE

Swift SSE is a minimal parser for Server-Sent Events streams.

It implements the SSE wire-format parsing rules from the WHATWG HTML spec and is intended as a low-level building block. It does not implement a browser-style EventSource networking client.

Installation

Add the package dependency:

dependencies: [
    .package(url: "https://github.com/DePasqualeOrg/swift-sse.git", from: "0.1.0")
]

Then add the product to your target:

dependencies: [
    .product(name: "SSE", package: "swift-sse")
]

Usage

Parse a buffered response body

import SSE

let events = Parser.parse(data)

for event in events {
    print(event.eventType)
    print(event.data)
}

Choosing an API

Use the event-only APIs when you only care about dispatched SSE events:

  • Parser.parse(_:)
  • parser.nextEvent()
  • byteStream.events

These surfaces filter out terminated SSE blocks that only update parser state, such as:

  • id\n\n
  • retry: 3000\n\n

Use the block-aware APIs when you need to observe those state-only updates:

  • Parser.parseBlocks(_:)
  • parser.nextBlock()
  • byteStream.sseBlocks

This matters for clients that need to react to:

  • Last-Event-ID changes even when no event was dispatched
  • retry: updates even when no event was dispatched

Parse state-aware SSE blocks

import SSE

for block in Parser.parseBlocks(data) {
    if let event = block.dispatchedEvent {
        print(event.data)
    } else if let retry = block.retry {
        print("retry:", retry)
    } else if let id = block.id {
        print("last-event-id:", block.lastEventId)
        print("raw id field:", id)
    }
}

Parse incrementally

import SSE

var parser = Parser()

for byte in bytes {
    parser.consume(byte)

    while let event = parser.nextEvent() {
        print(event.data)
    }
}

parser.finish()

Parse an async byte stream

import SSE

for try await event in byteStream.events {
    print(event.eventType)
    print(event.data)
}

Parse an async byte stream with control updates

import SSE

for try await block in byteStream.sseBlocks {
    if let retry = block.retry {
        updateRetryDelay(milliseconds: retry)
    }

    if let event = block.dispatchedEvent {
        handleEvent(event)
    }
}

API Overview

ServerSentEvent

Represents a dispatched SSE event.

Property Meaning
id The raw id: field from this event block, or nil if the block omitted id:
event The raw custom event type from event:, or nil when the default "message" type applies
eventType The effective event type, defaulting to "message"
data The dispatched payload. This is always present on ServerSentEvent
retry The valid retry: value from this same block, if present
lastEventId The committed stream-level Last-Event-ID value after this event was processed

Important distinctions:

  • id is event-local
  • lastEventId is stream-level committed state
  • id == "" is valid and means the stream ID was reset

ServerSentEventBlock

Represents the observable result of a terminated SSE block, including blocks that did not dispatch an event.

Property Meaning
id The raw id: field from this block, if present
event The raw custom event type from this block, if present
data The dispatched payload, or nil if this block did not dispatch an event
retry The valid retry: value from this block, if present
lastEventId The committed stream-level Last-Event-ID value after this block was processed
reconnectionTime The current stream-level reconnection time after this block was processed
dispatchedEvent A ServerSentEvent view of this block when data is non-nil

Important distinctions:

  • data == nil means no event was dispatched
  • data == "" means an event was dispatched with an empty payload

ServerSentEventsParser

Incremental parser state.

API Meaning
consume(_:) Feed one byte into the parser
consume(_ bytes:) Feed a sequence of bytes into the parser
finish() Signal end-of-stream; incomplete trailing events are discarded per the SSE spec
nextBlock() Pop the next parsed ServerSentEventBlock
nextEvent() Pop the next dispatched ServerSentEvent, filtering out state-only blocks
parseBlocks(_:) Parse a complete buffered byte sequence into blocks
parse(_:) Parse a complete buffered byte sequence into dispatched events only
lastEventId Current committed stream-level Last-Event-ID
reconnectionTime Current stream-level reconnection time from retry:

Async parsing

API Meaning
byteStream.sseBlocks Async block-aware parsing surface
byteStream.events Async event-only parsing surface

Behavior notes:

  • upstream async sequence failures are rethrown
  • EOF does not dispatch an incomplete trailing event
  • .events filters out state-only blocks

Short aliases

The package also exposes these shorthand aliases:

  • Event for ServerSentEvent
  • Block for ServerSentEventBlock
  • Parser for ServerSentEventsParser

Scope

This package is intentionally parser-only.

Out of scope:

  • opening HTTP connections
  • reconnect scheduling
  • readyState or DOM-style event handlers
  • a full EventSource client abstraction

Those concerns should stay in the consuming transport/client library.

Description

  • Swift Tools 6.1.0
View More Packages from this Author

Dependencies

  • None
Last updated: Thu Apr 09 2026 16:57:15 GMT-0900 (Hawaii-Aleutian Daylight Time)