I am working on a Server-Sent-Event parser which has newlines as part of the protocol:
https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
If the line is empty (a blank line)
Dispatch the event, as defined below.
Unfortunately, when using:
let (asyncBytes, _) = try await self.session.bytes(from: sseStreamURL)
for try await line in asyncBytes.lines {
//parse line
}
The iterator actually skips empty lines entirely, which seems destructive and therefore can't be the only way? Can it? With split there is a omittingEmptySubsequences: false
, but I didn't find any trace of anything like that for .lines
. I couldn't find the var definition in the Swift repo either, if anyone could point me at it.
I did write something that works for now, but I'd love if anyone had any comments on how to improve it / point me at any exiting language features that I missed.
extension AsyncSequence where Element == UInt8 {
var allLines:AsyncThrowingStream<String, Error> {
AsyncThrowingStream { continuation in
Task {
var buffer:[UInt8] = []
var iterator = self.makeAsyncIterator()
while let byte = try await iterator.next() {
// b/c 10 == \n
if byte != 10 { buffer.append(byte) }
else {
if buffer.isEmpty { continuation.yield("") }
else {
if let line = String(data: Data(buffer), encoding: .utf8) { continuation.yield(line) }
else {
//Is there a different AsyncSequence error I could use?
throw SSEError("allLines: Couldn't make string from [UInt8] chunk")
}
buffer = []
}
}
}
}
}
}
}
usage:
for try await line in asyncBytes.allLines {
//parse line
}
or of course also
var iterator = asyncBytes.allLines.makeAsyncIterator()
while let line = try await iterator.next() {
//parse line
}