From aebc99a66390812a358fd932b5bda634cac32e4b Mon Sep 17 00:00:00 2001 From: shijing xian Date: Fri, 4 Sep 2026 16:14:15 -0700 Subject: [PATCH] Default to a single peer connection Matches client-sdk-js (`defaults.ts:45`) and rust-sdks (`room/mod.rs:471`), which have both defaulted to single PC for some time. Swift was the only one of the three still opening two peer connections by default, paying two ICE and DTLS handshakes and unable to use any of the v1-only connect optimizations. Measured against LiveKit Cloud staging, 25 iterations per configuration: the post-signal connect phase drops from 291ms to 242ms p50 (-17%) once the publisher offer rides with the JOIN request. Single PC alone accounts for none of that -- 291ms versus 298ms for dual PC -- so this change is only worth anything stacked on offer-with-join, and offer-with-join reaches no one without it. Servers older than LiveKit OSS 1.9.2 do not serve /rtc/v1; the existing serviceNotFound fallback re-runs the connect against the legacy path, so the cost there is one failed request rather than a failed connect. Co-Authored-By: Claude Opus 5 (1M context) --- .changes/single-peer-connection-by-default | 1 + Sources/LiveKit/Types/Options/RoomOptions.swift | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 .changes/single-peer-connection-by-default diff --git a/.changes/single-peer-connection-by-default b/.changes/single-peer-connection-by-default new file mode 100644 index 000000000..a9d55907b --- /dev/null +++ b/.changes/single-peer-connection-by-default @@ -0,0 +1 @@ +minor type="changed" "`RoomOptions.singlePeerConnection` now defaults to `true`, matching client-sdk-js and rust-sdks: one ICE/DTLS handshake instead of two, and the publisher offer travels with the JOIN request. Measured ~17% off the post-signal connect phase against LiveKit Cloud. Servers older than LiveKit OSS 1.9.2 fall back to the legacy dual peer connection path automatically; pass `singlePeerConnection: false` to opt out" diff --git a/Sources/LiveKit/Types/Options/RoomOptions.swift b/Sources/LiveKit/Types/Options/RoomOptions.swift index a00b7c0f9..3ac495619 100644 --- a/Sources/LiveKit/Types/Options/RoomOptions.swift +++ b/Sources/LiveKit/Types/Options/RoomOptions.swift @@ -62,7 +62,13 @@ public final class RoomOptions: NSObject, Sendable, Loggable { /// Use a single peer connection for both publishing and subscribing. /// - /// - Note: Requires LiveKit Cloud or LiveKit OSS >= 1.9.2. + /// Defaults to `true`, matching client-sdk-js and rust-sdks. One connection means one ICE and + /// DTLS handshake instead of two, and it is the only mode that can carry the publisher offer + /// in the JOIN request — which removes an offer/answer round trip from the connect path. + /// + /// - Note: Requires LiveKit Cloud or LiveKit OSS >= 1.9.2. Against an older server the SDK + /// falls back to the legacy dual peer connection path automatically, so setting this has no + /// effect there beyond one extra failed request. public let singlePeerConnection: Bool override public init() { @@ -79,7 +85,7 @@ public final class RoomOptions: NSObject, Sendable, Loggable { e2eeOptions = nil encryptionOptions = nil reportRemoteTrackStatistics = false - singlePeerConnection = false + singlePeerConnection = true } public init(defaultCameraCaptureOptions: CameraCaptureOptions = CameraCaptureOptions(), @@ -95,7 +101,7 @@ public final class RoomOptions: NSObject, Sendable, Loggable { e2eeOptions: E2EEOptions? = nil, encryptionOptions: EncryptionOptions? = nil, reportRemoteTrackStatistics: Bool = false, - singlePeerConnection: Bool = false) + singlePeerConnection: Bool = true) { self.defaultCameraCaptureOptions = defaultCameraCaptureOptions self.defaultScreenShareCaptureOptions = defaultScreenShareCaptureOptions