diff --git a/Sources/LiveKit/Core/Engine+SignalClientDelegate.swift b/Sources/LiveKit/Core/Engine+SignalClientDelegate.swift index f573c60d7..20cbd3fb8 100644 --- a/Sources/LiveKit/Core/Engine+SignalClientDelegate.swift +++ b/Sources/LiveKit/Core/Engine+SignalClientDelegate.swift @@ -69,7 +69,7 @@ extension Engine: SignalClientDelegate { log("Received offer, creating & sending answer...") guard let subscriber else { - log("failed to send answer, subscriber is nil", .error) + log("Failed to send answer, subscriber is nil", .error) return } diff --git a/Sources/LiveKit/Core/Engine.swift b/Sources/LiveKit/Core/Engine.swift index 1892061a0..08cae6566 100644 --- a/Sources/LiveKit/Core/Engine.swift +++ b/Sources/LiveKit/Core/Engine.swift @@ -96,7 +96,9 @@ class Engine: Loggable { guard let self else { return } - assert(!(newState.connectionState == .reconnecting && newState.isReconnectingWithMode == .none), "reconnectMode should not be .none") + if newState.connectionState == .reconnecting, newState.isReconnectingWithMode == nil { + log("reconnectMode should not be .none", .error) + } if (newState.connectionState != oldState.connectionState) || (newState.isReconnectingWithMode != oldState.isReconnectingWithMode) { self.log("connectionState: \(oldState.connectionState) -> \(newState.connectionState), reconnectMode: \(String(describing: newState.isReconnectingWithMode))") @@ -220,9 +222,14 @@ class Engine: Loggable { try await ensurePublisherConnected() // At this point publisher should be .connected and dc should be .open - assert(publisher?.isConnected ?? false, "publisher is not .connected") + if !(publisher?.isConnected ?? false) { + log("publisher is not .connected", .error) + } + let dataChannelIsOpen = await publisherDataChannel.isOpen - assert(dataChannelIsOpen, "publisher data channel is not .open") + if !dataChannelIsOpen { + log("publisher data channel is not .open", .error) + } // Should return true if successful try await publisherDataChannel.send(userPacket: userPacket, kind: kind) @@ -586,12 +593,20 @@ extension Engine { extension Engine { func requireRoom() throws -> Room { - guard let room = _room else { throw LiveKitError(.invalidState, message: "Room is nil") } + guard let room = _room else { + log("Room is nil", .error) + throw LiveKitError(.invalidState, message: "Room is nil") + } + return room } func requirePublisher() throws -> Transport { - guard let publisher else { throw LiveKitError(.invalidState, message: "Publisher is nil") } + guard let publisher else { + log("Publisher is nil", .error) + throw LiveKitError(.invalidState, message: "Publisher is nil") + } + return publisher } } diff --git a/Sources/LiveKit/Core/SignalClient.swift b/Sources/LiveKit/Core/SignalClient.swift index 6c7f4196b..4ff46b07f 100644 --- a/Sources/LiveKit/Core/SignalClient.swift +++ b/Sources/LiveKit/Core/SignalClient.swift @@ -632,7 +632,7 @@ extension Livekit_SignalRequest { private extension SignalClient { func requireWebSocket() async throws -> WebSocket { guard let result = _webSocket else { - log("WebSocket is nil", .warning) + log("WebSocket is nil", .error) throw LiveKitError(.invalidState, message: "WebSocket is nil") } diff --git a/Sources/LiveKit/Core/Transport.swift b/Sources/LiveKit/Core/Transport.swift index f8f690127..9f690d596 100644 --- a/Sources/LiveKit/Core/Transport.swift +++ b/Sources/LiveKit/Core/Transport.swift @@ -139,7 +139,7 @@ actor Transport: NSObject, Loggable { func createAndSendOffer(iceRestart: Bool = false) async throws { guard let _onOffer else { - log("_onOffer is nil", .warning) + log("_onOffer is nil", .error) return } diff --git a/Sources/LiveKit/Extensions/RTCRtpTransceiver.swift b/Sources/LiveKit/Extensions/RTCRtpTransceiver.swift index 8bb9bd0f8..59a81c11c 100644 --- a/Sources/LiveKit/Extensions/RTCRtpTransceiver.swift +++ b/Sources/LiveKit/Extensions/RTCRtpTransceiver.swift @@ -40,6 +40,8 @@ extension LKRTCRtpTransceiver: Loggable { log("codecPreferences set: \(codecPreferences.map { String(describing: $0) }.joined(separator: ", "))") - assert(codecPreferences.first?.name.lowercased() == codec.id, "Preferred codec is not first in the list") + if codecPreferences.first?.name.lowercased() != codec.id { + log("Preferred codec is not first of codecPreferences", .error) + } } } diff --git a/Sources/LiveKit/LiveKit+DeviceHelpers.swift b/Sources/LiveKit/LiveKit+DeviceHelpers.swift index ff2f343f7..328716466 100644 --- a/Sources/LiveKit/LiveKit+DeviceHelpers.swift +++ b/Sources/LiveKit/LiveKit+DeviceHelpers.swift @@ -19,10 +19,10 @@ import AVFoundation public extension LiveKitSDK { /// Helper method to ensure authorization for video(camera) / audio(microphone) permissions in a single call. static func ensureDeviceAccess(for types: Set) async -> Bool { - assert(!types.isEmpty, "Please specify at least 1 type") - for type in types { - assert([.video, .audio].contains(type), "types must be .video or .audio") + if ![.video, .audio].contains(type) { + logger.log("types must be .video or .audio", .error, type: LiveKitSDK.self) + } let status = AVCaptureDevice.authorizationStatus(for: type) switch status { diff --git a/Sources/LiveKit/Participant/LocalParticipant.swift b/Sources/LiveKit/Participant/LocalParticipant.swift index 4877c4762..9943af0c7 100644 --- a/Sources/LiveKit/Participant/LocalParticipant.swift +++ b/Sources/LiveKit/Participant/LocalParticipant.swift @@ -40,10 +40,7 @@ public class LocalParticipant: Participant { log("[publish] \(track) options: \(String(describing: options ?? nil))...", .info) let room = try requireRoom() - - guard let publisher = room.engine.publisher else { - throw LiveKitError(.invalidState, message: "Publisher is nil") - } + let publisher = try room.engine.requirePublisher() guard _state.trackPublications.values.first(where: { $0.track === track }) == nil else { throw LiveKitError(.invalidState, message: "This track has already been published.") diff --git a/Sources/LiveKit/Participant/Participant.swift b/Sources/LiveKit/Participant/Participant.swift index 98025fbdf..e788ee5d4 100644 --- a/Sources/LiveKit/Participant/Participant.swift +++ b/Sources/LiveKit/Participant/Participant.swift @@ -243,7 +243,11 @@ public extension Participant { extension Participant { func requireRoom() throws -> Room { - guard let room = _room else { throw LiveKitError(.invalidState, message: "Room is nil") } + guard let room = _room else { + log("Room is nil", .error) + throw LiveKitError(.invalidState, message: "Room is nil") + } + return room } } diff --git a/Sources/LiveKit/Participant/RemoteParticipant.swift b/Sources/LiveKit/Participant/RemoteParticipant.swift index 0d9435cd4..3a8facd96 100644 --- a/Sources/LiveKit/Participant/RemoteParticipant.swift +++ b/Sources/LiveKit/Participant/RemoteParticipant.swift @@ -118,7 +118,10 @@ public class RemoteParticipant: Participant { await publication.set(track: track) publication.set(subscriptionAllowed: true) - assert(room.engine.subscriber != nil, "Subscriber is nil") + if room.engine.subscriber == nil { + log("Subscriber is nil", .error) + } + if let transport = room.engine.subscriber { await track.set(transport: transport, rtpReceiver: rtpReceiver) } diff --git a/Sources/LiveKit/Track/Capturers/VideoCapturer.swift b/Sources/LiveKit/Track/Capturers/VideoCapturer.swift index a9d30379c..f0ec811bd 100644 --- a/Sources/LiveKit/Track/Capturers/VideoCapturer.swift +++ b/Sources/LiveKit/Track/Capturers/VideoCapturer.swift @@ -106,7 +106,9 @@ public class VideoCapturer: NSObject, Loggable, VideoCapturerProtocol { } deinit { - assert(captureState == .stopped, "captureState is not .stopped, capturer must be stopped before deinit.") + if captureState != .stopped { + log("captureState is not .stopped, capturer must be stopped before deinit.", .error) + } } /// Requests video capturer to start generating frames. ``Track/start()-dk8x`` calls this automatically. diff --git a/Sources/LiveKit/Track/Support/Extensions.swift b/Sources/LiveKit/Track/Support/Extensions.swift index aac12094f..45552cfbc 100644 --- a/Sources/LiveKit/Track/Support/Extensions.swift +++ b/Sources/LiveKit/Track/Support/Extensions.swift @@ -103,8 +103,9 @@ public extension CGImage { static func show(for preferredExtension: String? = nil, showsMicrophoneButton: Bool = true) { - // Must be called on main thread - assert(Thread.current.isMainThread, "must be called on main thread") + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } let view = RPSystemBroadcastPickerView() view.preferredExtension = preferredExtension diff --git a/Sources/LiveKit/Track/Track.swift b/Sources/LiveKit/Track/Track.swift index 65dd537fb..cdd90f9c9 100644 --- a/Sources/LiveKit/Track/Track.swift +++ b/Sources/LiveKit/Track/Track.swift @@ -364,8 +364,9 @@ extension Track { return } - // must always be called on main thread - assert(Thread.current.isMainThread, "must be called on main thread") + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } videoRenderers.add(videoRenderer) rtcVideoTrack.add(VideoRendererAdapter(target: videoRenderer)) @@ -377,8 +378,9 @@ extension Track { return } - // must always be called on main thread - assert(Thread.current.isMainThread, "must be called on main thread") + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } videoRenderers.remove(videoRenderer) rtcVideoTrack.remove(VideoRendererAdapter(target: videoRenderer)) diff --git a/Sources/LiveKit/TrackPublications/RemoteTrackPublication.swift b/Sources/LiveKit/TrackPublications/RemoteTrackPublication.swift index 9eba316f2..8137e784f 100644 --- a/Sources/LiveKit/TrackPublications/RemoteTrackPublication.swift +++ b/Sources/LiveKit/TrackPublications/RemoteTrackPublication.swift @@ -280,9 +280,8 @@ extension RemoteTrackPublication { let state = _state.copy() - assert(!state.isSendingTrackSettings, "send(trackSettings:) called while previous send not completed") - if state.isSendingTrackSettings { + log("send(trackSettings:) called while previous send not completed", .error) // Previous send hasn't completed yet... throw LiveKitError(.invalidState, message: "Already busy sending new track settings") } @@ -343,9 +342,6 @@ extension RemoteTrackPublication { // executed on .main @MainActor private func onAdaptiveStreamTimer() async { - // this should never happen - assert(Thread.current.isMainThread, "this method must be called from main thread") - // don't continue if the engine is disconnected guard engineConnectionState != .disconnected else { log("engine is disconnected") diff --git a/Sources/LiveKit/TrackPublications/TrackPublication.swift b/Sources/LiveKit/TrackPublications/TrackPublication.swift index fe3d7575b..84ce6ecdc 100644 --- a/Sources/LiveKit/TrackPublications/TrackPublication.swift +++ b/Sources/LiveKit/TrackPublications/TrackPublication.swift @@ -229,6 +229,7 @@ extension TrackPublication: TrackDelegateInternal { extension TrackPublication { func requireParticipant() async throws -> Participant { guard let participant else { + log("Participant is nil", .error) throw LiveKitError(.invalidState, message: "Participant is nil") } diff --git a/Sources/LiveKit/Types/Dimensions.swift b/Sources/LiveKit/Types/Dimensions.swift index 076fc1118..5d6318f44 100644 --- a/Sources/LiveKit/Types/Dimensions.swift +++ b/Sources/LiveKit/Types/Dimensions.swift @@ -20,7 +20,7 @@ import Foundation @_implementationOnly import WebRTC @objc -public class Dimensions: NSObject { +public class Dimensions: NSObject, Loggable { @objc public let width: Int32 @@ -121,7 +121,10 @@ extension Dimensions { } func computeSuggestedPreset(in presets: [VideoParameters]) -> VideoEncoding { - assert(!presets.isEmpty) + if presets.isEmpty { + log("presets is empty", .error) + } + var result = presets[0].encoding for preset in presets { result = preset.encoding diff --git a/Sources/LiveKit/Types/TrackStatistics.swift b/Sources/LiveKit/Types/TrackStatistics.swift index 83ad14edf..310dff340 100644 --- a/Sources/LiveKit/Types/TrackStatistics.swift +++ b/Sources/LiveKit/Types/TrackStatistics.swift @@ -19,7 +19,7 @@ import Foundation @_implementationOnly import WebRTC @objc -public class TrackStatistics: NSObject { +public class TrackStatistics: NSObject, Loggable { public let codec: [CodecStatistics] public let transportStats: TransportStatistics? public let videoSource: [VideoSourceStatistics] @@ -48,17 +48,28 @@ public class TrackStatistics: NSObject { remoteInboundRtpStream = stats.compactMap { $0 as? RemoteInboundRtpStreamStatistics } remoteOutboundRtpStream = stats.compactMap { $0 as? RemoteOutboundRtpStreamStatistics } - let t = stats.compactMap { $0 as? TransportStatistics } - assert(t.count <= 1, "More than 1 TransportStatistics exists") - transportStats = t.first + let transportStatistics = stats.compactMap { $0 as? TransportStatistics } + transportStats = transportStatistics.first - let l = stats.compactMap { $0 as? LocalIceCandidateStatistics } - assert(l.count <= 1, "More than 1 LocalIceCandidateStatistics exists") - localIceCandidate = l.first + let localIceCandidates = stats.compactMap { $0 as? LocalIceCandidateStatistics } + localIceCandidate = localIceCandidates.first - let r = stats.compactMap { $0 as? RemoteIceCandidateStatistics } - assert(r.count <= 1, "More than 1 RemoteIceCandidateStatistics exists") - remoteIceCandidate = r.first + let remoteIceCandidates = stats.compactMap { $0 as? RemoteIceCandidateStatistics } + remoteIceCandidate = remoteIceCandidates.first + + super.init() + + if transportStatistics.count > 1 { + log("More than 1 TransportStatistics exists", .warning) + } + + if localIceCandidates.count > 1 { + log("More than 1 LocalIceCandidateStatistics exists", .warning) + } + + if remoteIceCandidates.count > 1 { + log("More than 1 RemoteIceCandidateStatistics exists", .warning) + } } } diff --git a/Sources/LiveKit/Types/VideoParameters.swift b/Sources/LiveKit/Types/VideoParameters.swift index 0342b90f6..718f04286 100644 --- a/Sources/LiveKit/Types/VideoParameters.swift +++ b/Sources/LiveKit/Types/VideoParameters.swift @@ -20,10 +20,14 @@ extension Collection { func suggestedPresetIndex(dimensions: Dimensions? = nil, videoEncoding: VideoEncoding? = nil) -> Int { - // self must at lease have 1 element - assert(!isEmpty) - // dimensions or videoEndocing is required - assert(dimensions != nil || videoEncoding != nil) + if isEmpty { + // Must have at least 1 element + logger.log("isEmpty", .error, type: (any Collection).self) + } + + if dimensions == nil, videoEncoding == nil { + logger.log("dimensions or videoEncoding parameter is required", .error, type: (any Collection).self) + } var result = 0 for preset in self { diff --git a/Sources/LiveKit/Views/VideoView.swift b/Sources/LiveKit/Views/VideoView.swift index 1d72c75ed..795efca93 100644 --- a/Sources/LiveKit/Views/VideoView.swift +++ b/Sources/LiveKit/Views/VideoView.swift @@ -194,14 +194,15 @@ public class VideoView: NativeView, Loggable { private var _frameCount: Int = 0 override public init(frame: CGRect = .zero) { - // should always be on main thread - assert(Thread.current.isMainThread, "must be on the main thread") - // initial state _state = StateSync(State(viewSize: frame.size)) super.init(frame: frame) + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } + #if os(iOS) clipsToBounds = true #endif @@ -349,8 +350,9 @@ public class VideoView: NativeView, Loggable { override func performLayout() { super.performLayout() - // should always be on main thread - assert(Thread.current.isMainThread, "must be called on main thread") + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } let state = _state.copy() @@ -457,8 +459,9 @@ private extension VideoView { @discardableResult func reCreateNativeRenderer() -> NativeRendererView { - // should always be on main thread - assert(Thread.current.isMainThread, "must be called on main thread") + if !Thread.current.isMainThread { + log("Must be called on main thread", .error) + } // create a new rendererView let newView = VideoView.createNativeRendererView(for: _state.renderMode)