Repository navigation
Report the sender identity alongside received data - #1172
xianshijing-lk wants to merge 3 commits into
Conversation
RoomDelegate's data callback carries an optional RemoteParticipant, which is nil whenever the sender is not in the room - data broadcast from the server, or sent by a participant that has already left. In that case the receiver had no way to tell who sent the data, even though Room+EngineDelegate already builds the identity to look the participant up and then discards it. Add an optional delegate method carrying participantIdentity, notified alongside the existing one. Both are @objc optional, so this is purely additive: existing conformers are unaffected. Port of livekit/client-sdk-js#2092. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The new delegate method takes six parameters, one over the limit, so disable function_parameter_count for it and for the conformance in the test. Replace the recorder's three-member tuple with a small struct, and drop the redundant argument-less @suite attribute. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses review feedback. The handler read UserPacket.participantIdentity, which is the deprecated copy. Newer servers set the identity only on the enclosing DataPacket and leave that field empty, so the new callback would have reported an empty identity for exactly the packets it was added to describe. Room now forwards the outer identity and the handler prefers it, falling back to the deprecated field for older servers. Also use StateSync in the test recorder rather than a bare NSLock, per the repository's synchronisation rule, and add the usage example the public API rule asks for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pblazej
left a comment
There was a problem hiding this comment.
The concurrency side is clean, and the additive API shape is right. Two blocking points remain, the server-data claim in the docstring and a new compiler warning in the test.
| /// | ||
| /// `participant` is nil whenever the sender is not in the room — data broadcast from the | ||
| /// server, or sent by a participant that has already left — and in that case this is the | ||
| /// only way to tell who sent it. `participantIdentity` is reported either way. |
There was a problem hiding this comment.
issue (blocking): Data sent from the Server API carries no identity, so this callback receives an empty stringValue in that case.
RoomManager.SendData builds the DataPacket without one, and the SFU blanks it for hidden senders.
| /// | ||
| /// `participant` is nil whenever the sender is not in the room — data broadcast from the | ||
| /// server, or sent by a participant that has already left — and in that case this is the | ||
| /// only way to tell who sent it. `participantIdentity` is reported either way. |
There was a problem hiding this comment.
suggestion (blocking): We describe the join race instead, a packet that arrives before participantDidConnect or after the sender left.
The docstring must also state what the identity is when the server gives none.
| /// ``` | ||
| @objc optional | ||
| // swiftlint:disable:next function_parameter_count | ||
| func room(_ room: Room, participant: RemoteParticipant?, participantIdentity: Participant.Identity, didReceiveData data: Data, forTopic topic: String, encryptionType: EncryptionType) |
There was a problem hiding this comment.
suggestion: Make participantIdentity optional and map an empty string to nil in Room+EngineDelegate.swift.
An empty key merges server data and hidden senders into one bucket for callers that key buffers by identity.
| let recorder = DataRecorder() | ||
| room.delegates.add(delegate: recorder) | ||
|
|
||
| await room.engine(room, |
There was a problem hiding this comment.
issue (blocking): This await produces warning: no 'async' operations occur within 'await' expression.
AGENTS.md does not allow new warnings.
| let recorder = DataRecorder() | ||
| room.delegates.add(delegate: recorder) | ||
|
|
||
| await room.engine(room, |
There was a problem hiding this comment.
todo (blocking): Remove the await.
The method is synchronous.
|
|
||
| func engine(_ engine: Room, didReceiveUserPacket packet: Livekit_UserPacket, encryptionType: EncryptionType) { | ||
| func engine(_ engine: Room, didReceiveUserPacket packet: Livekit_UserPacket, participantIdentity: String, encryptionType: EncryptionType) { | ||
| // The enclosing DataPacket carries the authoritative identity. UserPacket's own copy is |
There was a problem hiding this comment.
todo: Keep the fallback and remove the comment.
It only matters for servers older than that sync.
| @@ -0,0 +1 @@ | |||
| patch type="added" "RoomDelegate can now receive the sender identity alongside data packets" | |||
There was a problem hiding this comment.
question: This adds a public API but is marked patch.
The example in scripts/create_version.swift pairs added with minor.
|
|
||
| /// `participant` is nil whenever the sender is not in the room, so without the identity the | ||
| /// receiver cannot tell who sent the data at all. | ||
| struct DataReceivedIdentityTests { |
There was a problem hiding this comment.
nitpick: Sibling suites use @Suite and the data suites carry .tags(.dataChannel).
A .spec trait that links to client-sdk-js#2092 records the port source.
| /// } | ||
| /// ``` | ||
| @objc optional | ||
| // swiftlint:disable:next function_parameter_count |
There was a problem hiding this comment.
nitpick: Siblings place // swiftlint:disable:next above the attributes.
Here it sits between @objc optional and func.
| /// ``` | ||
| @objc optional | ||
| // swiftlint:disable:next function_parameter_count | ||
| func room(_ room: Room, participant: RemoteParticipant?, participantIdentity: Participant.Identity, didReceiveData data: Data, forTopic topic: String, encryptionType: EncryptionType) |
There was a problem hiding this comment.
praise: The additive shape was the right call over @available(*, unavailable, renamed:).
Existing conformers do not break, and swift-api-digester passed the same pattern in PR #1093.
Port of livekit/client-sdk-js#2092.
The gap
RoomDelegate's data callback takes an optional participant:It is nil whenever the sender is not in the room — data broadcast from the Server API, or sent by a participant who has already left. In that case the receiver cannot tell who sent the data at all.
The identity is right there and simply dropped.
Room+EngineDelegatebuilds it to do the lookup:The fix
Add an optional delegate method that also carries
participantIdentity, notified alongside the existing one. Implement whichever you need.On the API surface
I kept this purely additive rather than following the
@available(*, unavailable, renamed:)pattern used elsewhere in this protocol, since that form is source-breaking for conformers:RoomDelegatemethods are@objc optional, so adding one does not break any existing conformer.Participant.Identityis already anNSObjectsubclass with@objcmembers, so it is representable in the@objcprotocol — noStringfallback needed.If you would rather converge on a single callback and retire the old signature the way the earlier rename did, say so and I will rework it that way — that felt like your call rather than mine, given
swift-api-digestergates this protocol.Verification
Added
DataReceivedIdentityTests, which drives a user packet whose sender is not a room participant and asserts the identity still arrives whileparticipantis nil.swift test --filter DataReceivedIdentityTestspasses; before the change it does not compile, since the delegate method does not exist.I could not run the whole suite locally — much of
LiveKitCoreTestsis.tags(.e2e)and needs a live LiveKit server.🤖 Generated with Claude Code