Repository navigation
Conversation
The Rust core now fails an inline stream whose payload does not match the length in its header (Incomplete or LengthExceeded), as it already did for chunked streams. The readers no longer count bytes, check the length at the end, or run their reads one at a time to keep that count right after a cancel. Removes the tests of that Swift check. The Rust tests now cover it: v2_inline_payload_is_held_to_its_declared_length, v2_compressed_errors_when_too_few_bytes / _too_many_bytes and v1_errors_when_too_few_bytes / _too_many_bytes (livekit-data-stream), and outgoing_text_stream_declares_total_length (livekit-ffi), whose receiver fails a short stream with Incomplete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
The FFI readers now have close(), which fails a pending read at once and ends the stream in the incoming manager. A cancelled read calls it and throws CancellationError, so the stream ends, as a v1 reader's AsyncThrowingStream did when its reading task was cancelled. A later read throws StreamError.alreadyClosed. This replaces two workarounds: the read that a cancel abandoned in its own task, and readAll() as a loop over next(), which existed only so a cancel could stop it. readAll() calls the core's readAll() again. Tests: cancelledReadEndsTheStream replaces cancellingWaitingReadReturns, cancelledReadAllLeavesRestOfStream (the stream no longer stays readable) and cancelledReadLeavesNoOpenStream. The Rust side of the close is covered by incoming_reader_close_ends_its_stream_without_more_packets and incoming_byte_reader_close_ends_its_stream (livekit-ffi). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
A reader's info now reads the core's info on each access, so once the stream has ended its attributes also hold the attributes the sender set in the trailer, with trailer keys replacing header keys, as in JS. The property changes from a stored let to a computed var, which keeps the API and ABI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
TextStreamWriter and ByteStreamWriter gain close(reason:attributes:), which puts the attributes in the stream's trailer through the FFI writer's close(reason:attributes:). Receivers see them in the reader's info once the stream has ended. close(reason:) stays as a plain overload with its signature and Objective-C selector, and forwards to the new method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
StreamProgress holds the payload bytes sent or received so far, the total when it is known, and their fraction. As in JS, a send counts bytes before compression and ends with a report of the whole payload, and a reader counts bytes as they arrive, with no total for a stream that declared no size. - LocalParticipant: sendText(_:options:onProgress:), sendBytes(_:options:onProgress:) and sendFile(_:options:onProgress:) pass the closure to the FFI send as a StreamProgressDelegate. A one-shot text or byte send reports once, when it completes; a file reports as it goes out. The earlier signatures stay as plain overloads that forward. - TextStreamReader and ByteStreamReader: progress, an AsyncStream over the FFI reader's nextProgress(). A cancel ends the iteration at once. - Objective-C: the send overloads are exposed under their own selectors, and readProgress(onProgress:) wraps the sequence the way readChunks wraps the chunks. The progress closure is a trailing parameter rather than an options field: the options classes are shared with streamText and streamBytes, where progress does not apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
StreamTextOptions gains totalLength, the size of the text in UTF-8 bytes, named like StreamInfo.totalLength. streamText sends it in the header through the FFI options' totalLength, so receivers see it in the info and as the progress total, and the core fails a stream that does not match it. A one-shot sendText keeps declaring the size of its text. A negative value reads as nil, as StreamByteOptions.totalSize does. The initializer without totalLength stays as a convenience overload with its signature and Objective-C selector. Because Int? does not bridge, Objective-C gets an initializer that takes totalLengthNumber, deprecated for Swift callers like the totalSizeNumber initializer of StreamByteOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
Adds an example to registerByteStreamHandler that follows a reader's progress and reads the attributes the stream closed with, and documents readProgress(onProgress:) for Objective-C callers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
The core now ends a stream whose trailer carries a reason with AbnormalEnd(reason), even when the stream is shorter than its declared length; before, it reported Incomplete. abnormalClosure checks both a stream without a declared length and one that stops short of it. The core side is trailer_reason_wins_over_a_short_declared_length (livekit-data-stream). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
Renames StreamTextOptions.totalLength to totalSize, with the initializer label, the Objective-C totalSizeNumber: initializer, the docs, the tests and the changeset, so both option types and JS and Flutter use the same name. StreamInfo.totalLength keeps its name, and the option still maps to the FFI totalLength. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
A cancelled progress iteration abandons the core's nextProgress() wait, because UniFFI does not cancel the Rust future. That pending call holds the FFI reader, so the core's own release, which ends the stream, never ran: after a cancelled progress observation and the release of the reader, a stalled stream stayed open. The readers now close the FFI reader in deinit. Rust then ends the pending progress wait with nil, the call returns and releases the FFI reader, and the manager drops the stream. Cancelling progress still does not end the stream while the reader is alive, and the progress docs now say so. releasingReaderAfterCancelledProgressEndsTheStream covers both readers. It failed before this change with openAfterRelease == 1 for .byte and .text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
cancelledReadEndsTheStream now runs for the text reader too, for a pending next() and readAll(), cancelled while waiting in the core or before the read starts: CancellationError, no stream left open, then .alreadyClosed. It uses the both-kind stand-in readers of the progress test, so the byte-only SignallingReader and the unused entered and totalLength parameters of openReader are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
StreamProgress.bytesTotal (Int?) and fraction (Double?) do not bridge to Objective-C. bytesTotalNumber and fractionNumber return them as NSNumber?, like DataStreamOptions.maxPayloadByteLengthNumber. A bridge test pins the mapping and the accessors, and testStreamAdditionsAreExposed checks both selectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
SwiftLint's large_tuple rule rejects the four-member tuple that 72b67fe7 passed to progressMapsCountsAndObjCAccessors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
The progress sequence held only the FFI reader, so the deinit close from aa01ec06 ended the stream as soon as the app released its last reader reference, even while it still iterated progress: the stream closed and the iteration returned nil. The sequence's producer now holds the reader. AsyncStream(unfolding:) drops the producer when the sequence finishes or its iteration is cancelled, so an observation keeps the reader only for its own lifetime. Each nextProgress() wait still captures only the FFI reader, so a cancelled observation lets the reader deinit and wake the orphaned wait, which releasingReaderAfterCancelledProgressEndsTheStream keeps checking. observedProgressKeepsTheStreamOpen covers both readers: it consumes one update, releases the reader, sends the rest of the stream, and expects the stream to stay open and the iteration to reach the end. Before this change it failed for .byte and .text with openAfterRelease == 0 (expected 1) and a last update of 0 bytes (expected 5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
rust-sdks 7d0b8d90 (PR #1558) moved the progress delegate of sendText, sendBytes and sendFile from a trailing parameter into an optional progress field of the FFI StreamTextOptions and StreamByteOptions records. The sends now set that field on the record. sendFile builds its record from options.ffi too, which keeps the file under SwiftLint's length limit. No code relied on the records being Equatable or Hashable, which they no longer are, and the public API does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft until LiveKitFFI ships a release that includes livekit/rust-sdks#1558. CI stays red until
Package.swiftmoves the LiveKitFFI pin from0.13.1to that release.Added
infoafter the stream ends.close(reason:attributes:).sendText,sendBytes, andsendFiletake anonProgresscallback, and readers exposeprogressas anAsyncStream.StreamTextOptions.totalSizedeclares the size of an incremental text stream.StreamProgresshasbytesProcessed,bytesTotal, andfraction, withNSNumberaccessors for Objective-C.Removed
.alreadyClosed.Kept
Int.maxconversion, the sender retirement after a reconnect, theperformRpccancellation fix, the zero receive limit, and the error mapping.