ChannelDelegate
Register withchannel.delegates.add(delegate: self).
Connection events
User events
Track events
The most common entry point for your app is
didAddTrack, because that’s usually where you decide whether to subscribe to remote video or remote audio.
didChangeReceiveStreamStatus is judged per track: after subscribing, if no frames of that video arrive for a period of time, it reports a timeout (timedOut == true), and as soon as frames resume it reports again (timedOut == false). Use it to toggle a “loading / remote network issue” indicator on a given video tile. When the first frame arrives after subscribing, you first receive a recovery report, which you can use to hide the initial loading indicator. You can also query RemoteVideoTrack.isReceiveTimedOut at any time.
Message and channel events
Call quality events
Quality uses two event streams; pick one by purpose:
didReceiveQualityReport delivers raw values every report cycle, suited for signal-strength icons and diagnostics panels; didChangeConnectionQuality fires only when the level changes, suited for “poor network” prompts and proactive downgrading. Don’t use the former to drive prompts or downgrading—it flashes repeatedly when the level jitters.
didChangeActiveSpeakers delivers a full snapshot (already sorted by volume in descending order), so your app just overwrites the UI without merging increments itself; when nobody is speaking, it’s an empty array.
These four events exist only on the SeaStart (SFU) engine—they travel over the signaling DataChannel on the subscribing PeerConnection, and the Wangsu (CDN) engine has no such path. See Call quality and active speakers for details.
TrackDelegate
Register withtrack.delegates.add(delegate: self).
Track-level events
trackDidBindRtcTrack(_:) is especially useful for video rendering, because the remote track object may appear first and the underlying media track finishes binding a bit later—receiving this event is what means you can render.
The two screenBroadcast events fire only with iOS full-screen capture (ScreenCaptureMode.broadcast):
full-screen sharing is started by the user from the system UI and may be stopped directly from the system pill; these actions happen outside the app,
so you can only detect them through events. A successful startCapture() only means the SDK is ready; receiving screenBroadcastDidStart
is what really means “sharing”. See Screen sharing for details.
AudioRouteSessionDelegate
iOS only. Register withAudioRouteSession.shared.delegates.add(delegate: self).
All callbacks run on the main thread, and the protocol provides default empty implementations, so implement only the methods you care about.
reason distinguishes the source of the change (.oldDeviceUnavailable unplugged, .newDeviceAvailable plugged in,
.override actively overridden by the app); when troubleshooting route issues, it’s often more valuable than the result itself.
audioRouteSessionDidRecoverFromInterruption(_:) is not equivalent to the system’s
AVAudioSession.interruptionNotification(.ended): at the moment a system phone call hangs up, the audio hardware hasn’t been released yet,
so the SDK waits until the call has really ended and the app is back in the foreground before rebuilding the session, and retries on failure—this callback fires only after a real, successful recovery.
Your app just refreshes the UI here and doesn’t need to handle this timing itself.
See Audio routing for details.