diff --git a/.github/workflows/cron-checks.yml b/.github/workflows/cron-checks.yml index c157eb623..2c8b80b4e 100644 --- a/.github/workflows/cron-checks.yml +++ b/.github/workflows/cron-checks.yml @@ -219,5 +219,5 @@ jobs: - uses: actions/checkout@v3.1.0 - uses: ./.github/actions/bootstrap - run: bundle exec fastlane rubocop - - run: bundle exec fastlane run_linter + - run: bundle exec fastlane run_swift_format lint:true - run: bundle exec fastlane pod_lint diff --git a/.github/workflows/smoke-checks.yml b/.github/workflows/smoke-checks.yml index 28a3e09f5..35aeeeaff 100644 --- a/.github/workflows/smoke-checks.yml +++ b/.github/workflows/smoke-checks.yml @@ -49,7 +49,7 @@ jobs: - name: Run Fastlane Linting run: bundle exec fastlane rubocop - name: Run SwiftFormat Linting - run: bundle exec fastlane run_linter + run: bundle exec fastlane run_swift_format lint:true - name: Run Podspec Linting run: bundle exec fastlane pod_lint diff --git a/Sources/StreamVideo/Call.swift b/Sources/StreamVideo/Call.swift index 6eab7ec93..02e56ecd2 100644 --- a/Sources/StreamVideo/Call.swift +++ b/Sources/StreamVideo/Call.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Combine +import Foundation /// Observable object that provides info about the call state, as well as methods for updating it. public class Call: @unchecked Sendable, WSEventsSubscriber { @@ -44,25 +44,25 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { self.callType = callType self.coordinatorClient = coordinatorClient self.callController = callController - self.microphone = MicrophoneManager( + microphone = MicrophoneManager( callController: callController, initialStatus: .enabled ) - self.camera = CameraManager( + camera = CameraManager( callController: callController, initialStatus: .enabled, initialDirection: .front ) - self.speaker = SpeakerManager( + speaker = SpeakerManager( callController: callController, initialSpeakerStatus: .enabled, initialAudioOutputStatus: .enabled ) self.callController.call = self - self.subscribeToLocalCallSettingsChanges() + subscribeToLocalCallSettingsChanges() } - convenience internal init( + internal convenience init( from response: CallStateResponseFields, coordinatorClient: DefaultAPI, callController: CallController @@ -221,7 +221,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { @discardableResult public func reject() async throws -> RejectCallResponse { let response = try await coordinatorClient.rejectCall(type: callType, id: callId) - if streamVideo.state.ringingCall?.cId == self.cId { + if streamVideo.state.ringingCall?.cId == cId { streamVideo.state.ringingCall = nil } return response @@ -231,14 +231,14 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Parameter blockedUser: The user to add to the list of blocked users. @discardableResult public func block(user: User) async throws -> BlockUserResponse { - return try await blockUser(with: user.id) + try await blockUser(with: user.id) } /// Removes the given user from the list of blocked users for the call. /// - Parameter blockedUser: The user to remove from the list of blocked users. @discardableResult - public func unblock(user: User) async throws -> UnblockUserResponse{ - return try await unblockUser(with: user.id) + public func unblock(user: User) async throws -> UnblockUserResponse { + try await unblockUser(with: user.id) } /// Changes the track visibility for a participant (not visible if they go off-screen). @@ -251,14 +251,14 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { @discardableResult public func addMembers(members: [MemberRequest]) async throws -> UpdateCallMembersResponse { - try await self.updateCallMembers( + try await updateCallMembers( updateMembers: members ) } @discardableResult public func updateMembers(members: [MemberRequest]) async throws -> UpdateCallMembersResponse { - try await self.updateCallMembers( + try await updateCallMembers( updateMembers: members ) } @@ -268,7 +268,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Throws: An error if the members could not be added to the call. @discardableResult public func addMembers(ids: [String]) async throws -> UpdateCallMembersResponse { - try await self.updateCallMembers( + try await updateCallMembers( updateMembers: ids.map { MemberRequest(userId: $0) } ) } @@ -278,7 +278,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Throws: An error if the members could not be removed from the call. @discardableResult public func removeMembers(ids: [String]) async throws -> UpdateCallMembersResponse { - try await self.updateCallMembers( + try await updateCallMembers( removedIds: ids ) } @@ -326,7 +326,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Parameter event: the type of the event you are subscribing to. /// - Returns: `AsyncStream` of web socket events from the provided type. public func subscribe(for event: WSEvent.Type) -> AsyncStream { - return AsyncStream(event) { [weak self] continuation in + AsyncStream(event) { [weak self] continuation in let eventHandler: EventHandling = { event in guard case let .coordinatorEvent(event) = event else { return @@ -352,7 +352,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { streamVideo.state.activeCall = nil } - //MARK: - Permissions + // MARK: - Permissions /// Checks if the current user can request permissions. /// - Parameter permissions: The permissions to request. @@ -366,10 +366,10 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { && callSettings.audio.accessRequestEnabled == false { return false } else if permission.rawValue == Permission.sendVideo.rawValue - && callSettings.video.accessRequestEnabled == false { + && callSettings.video.accessRequestEnabled == false { return false } else if permission.rawValue == Permission.screenshare.rawValue - && callSettings.screensharing.accessRequestEnabled == false { + && callSettings.screensharing.accessRequestEnabled == false { return false } } @@ -487,7 +487,6 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { ) } - /// Ends a call. /// - Throws: error if ending the call fails. @discardableResult @@ -501,7 +500,11 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Throws: error if blocking the user fails. @discardableResult public func blockUser(with userId: String) async throws -> BlockUserResponse { - let response = try await coordinatorClient.blockUser(type: callType, id: callId, blockUserRequest: BlockUserRequest(userId: userId)) + let response = try await coordinatorClient.blockUser( + type: callType, + id: callId, + blockUserRequest: BlockUserRequest(userId: userId) + ) await state.blockUser(id: userId) return response } @@ -512,7 +515,11 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// - Throws: error if unblocking the user fails. @discardableResult public func unblockUser(with userId: String) async throws -> UnblockUserResponse { - let response = try await coordinatorClient.unblockUser(type: callType, id: callId, unblockUserRequest: UnblockUserRequest(userId: userId)) + let response = try await coordinatorClient.unblockUser( + type: callType, + id: callId, + unblockUserRequest: UnblockUserRequest(userId: userId) + ) await state.unblockUser(id: userId) return response } @@ -544,10 +551,10 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// Stops an ongoing live call. @discardableResult public func stopLive() async throws -> StopLiveResponse { - return try await coordinatorClient.stopLive(type: callType, id: callId) + try await coordinatorClient.stopLive(type: callType, id: callId) } - //MARK: - Recording + // MARK: - Recording /// Starts recording for the call. @discardableResult @@ -572,12 +579,12 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { return response.recordings } - //MARK: - Broadcasting + // MARK: - Broadcasting /// Starts HLS broadcasting of the call. @discardableResult public func startHLS() async throws -> StartHLSBroadcastingResponse { - return try await coordinatorClient.startHLSBroadcasting(type: callType, id: callId) + try await coordinatorClient.startHLSBroadcasting(type: callType, id: callId) } /// Stops HLS broadcasting of the call. @@ -586,7 +593,7 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { try await coordinatorClient.stopHLSBroadcasting(type: callType, id: callId) } - //MARK: - Events + // MARK: - Events /// Sends a custom event to the call. /// - Throws: An error if the sending fails. @@ -602,7 +609,11 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// Sends a reaction to the call. /// - Throws: An error if the sending fails. @discardableResult - public func sendReaction(type: String, custom: [String: RawJSON]? = nil, emojiCode: String? = nil) async throws -> SendReactionResponse { + public func sendReaction( + type: String, + custom: [String: RawJSON]? = nil, + emojiCode: String? = nil + ) async throws -> SendReactionResponse { try await coordinatorClient.sendVideoReaction( type: callType, id: callId, @@ -610,19 +621,26 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { ) } - //MARK: - Query members methods + // MARK: - Query members methods internal func queryMembers( filters: [String: RawJSON]? = nil, limit: Int? = nil, next: String? = nil, sort: [SortParamRequest]? = nil ) async throws -> QueryMembersResponse { - let request = QueryMembersRequest(filterConditions: filters, id: callId, limit: limit, next: next, sort: sort, type: callType) + let request = QueryMembersRequest( + filterConditions: filters, + id: callId, + limit: limit, + next: next, + sort: sort, + type: callType + ) let response = try await coordinatorClient.queryMembers(queryMembersRequest: request) await state.mergeMembers(response.members) return response } public func queryMembers( - filters: [String : RawJSON]? = nil, + filters: [String: RawJSON]? = nil, sort: [SortParamRequest] = [SortParamRequest.descending("created_at")], limit: Int = 25 ) async throws -> QueryMembersResponse { @@ -703,19 +721,19 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { /// method, which is expected to handle the camera focus logic. If an error occurs during the process, /// it throws an exception. /// - /// - Parameter point: A `CGPoint` value representing the location within the view where the + /// - Parameter point: A `CGPoint` value representing the location within the view where the /// camera should focus. The point (0, 0) is at the top-left corner of the view, and the point (1, 1) is at /// the bottom-right corner. - /// - Throws: An error if the focus operation cannot be completed. The type of error depends on + /// - Throws: An error if the focus operation cannot be completed. The type of error depends on /// the underlying implementation in the `callController`. /// - /// - Note: Ensure that the device supports tap to focus and that it is enabled before calling this + /// - Note: Ensure that the device supports tap to focus and that it is enabled before calling this /// method. Otherwise, it might result in an error. public func focus(at point: CGPoint) throws { try callController.focus(at: point) } - //MARK: - Internal + // MARK: - Internal internal func update(reconnectionStatus: ReconnectionStatus) { executeOnMain { [weak self] in @@ -750,7 +768,8 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { } } - //MARK: - private + // MARK: - private + private func updatePermissions( for userId: String, granted: [String], @@ -843,4 +862,3 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { speaker.audioOutputStatus = callSettings.audioOutputOn ? .enabled : .disabled } } - diff --git a/Sources/StreamVideo/CallSettings/CallSettingsManager.swift b/Sources/StreamVideo/CallSettings/CallSettingsManager.swift index ff11529ef..6a96cf0d0 100644 --- a/Sources/StreamVideo/CallSettings/CallSettingsManager.swift +++ b/Sources/StreamVideo/CallSettings/CallSettingsManager.swift @@ -11,8 +11,8 @@ protocol CallSettingsManager { func updateState( newState state: Bool, current: Bool, - action: (Bool) async throws -> (), - onUpdate: @Sendable (Bool) -> () + action: (Bool) async throws -> Void, + onUpdate: @Sendable(Bool) -> Void ) async throws } @@ -20,8 +20,8 @@ extension CallSettingsManager { func updateState( newState state: Bool, current: Bool, - action: (Bool) async throws -> (), - onUpdate: @Sendable (Bool) -> () + action: (Bool) async throws -> Void, + onUpdate: @Sendable(Bool) -> Void ) async throws { let updatingState = await self.state.updatingState if state == current || updatingState == state { diff --git a/Sources/StreamVideo/CallSettings/CameraManager.swift b/Sources/StreamVideo/CallSettings/CameraManager.swift index 3754a65e3..a5b98db6e 100644 --- a/Sources/StreamVideo/CallSettings/CameraManager.swift +++ b/Sources/StreamVideo/CallSettings/CameraManager.swift @@ -18,8 +18,8 @@ public final class CameraManager: ObservableObject, CallSettingsManager, @unchec initialDirection: CameraPosition ) { self.callController = callController - self.status = initialStatus - self.direction = initialDirection + status = initialStatus + direction = initialDirection } /// Toggles the camera state. @@ -31,7 +31,7 @@ public final class CameraManager: ObservableObject, CallSettingsManager, @unchec public func flip() async throws { let next = direction.next() try await callController.changeCameraMode(position: next) - self.direction = next + direction = next } /// Enables the camera. @@ -53,7 +53,7 @@ public final class CameraManager: ObservableObject, CallSettingsManager, @unchec action: { [unowned self] state in try await callController.changeVideoState(isEnabled: state) }, - onUpdate: { value in + onUpdate: { _ in self.status = status } ) diff --git a/Sources/StreamVideo/CallSettings/MicrophoneManager.swift b/Sources/StreamVideo/CallSettings/MicrophoneManager.swift index d699152e1..a19bed737 100644 --- a/Sources/StreamVideo/CallSettings/MicrophoneManager.swift +++ b/Sources/StreamVideo/CallSettings/MicrophoneManager.swift @@ -14,7 +14,7 @@ public final class MicrophoneManager: ObservableObject, CallSettingsManager, @un init(callController: CallController, initialStatus: CallSettingsStatus) { self.callController = callController - self.status = initialStatus + status = initialStatus } /// Toggles the microphone state. @@ -41,7 +41,7 @@ public final class MicrophoneManager: ObservableObject, CallSettingsManager, @un action: { [unowned self] state in try await callController.changeAudioState(isEnabled: state) }, - onUpdate: { value in + onUpdate: { _ in self.status = status } ) diff --git a/Sources/StreamVideo/CallSettings/SpeakerManager.swift b/Sources/StreamVideo/CallSettings/SpeakerManager.swift index 5738782b4..7b964b74c 100644 --- a/Sources/StreamVideo/CallSettings/SpeakerManager.swift +++ b/Sources/StreamVideo/CallSettings/SpeakerManager.swift @@ -18,8 +18,8 @@ public final class SpeakerManager: ObservableObject, CallSettingsManager, @unche initialAudioOutputStatus: CallSettingsStatus ) { self.callController = callController - self.status = initialSpeakerStatus - self.audioOutputStatus = initialAudioOutputStatus + status = initialSpeakerStatus + audioOutputStatus = initialAudioOutputStatus } /// Toggles the speaker during a call. @@ -56,7 +56,7 @@ public final class SpeakerManager: ObservableObject, CallSettingsManager, @unche action: { [unowned self] state in try await callController.changeSpeakerState(isEnabled: state) }, - onUpdate: { value in + onUpdate: { _ in self.status = status } ) @@ -65,11 +65,11 @@ public final class SpeakerManager: ObservableObject, CallSettingsManager, @unche private func updateAudioOutputStatus(_ status: CallSettingsStatus) async throws { try await updateState( newState: status.boolValue, - current: self.audioOutputStatus.boolValue, + current: audioOutputStatus.boolValue, action: { [unowned self] state in try await callController.changeSoundState(isEnabled: state) }, - onUpdate: { value in + onUpdate: { _ in self.audioOutputStatus = status } ) diff --git a/Sources/StreamVideo/CallState.swift b/Sources/StreamVideo/CallState.swift index c8391d738..2b4790c37 100644 --- a/Sources/StreamVideo/CallState.swift +++ b/Sources/StreamVideo/CallState.swift @@ -23,7 +23,7 @@ public struct PermissionRequest: @unchecked Sendable, Identifiable { self.onReject = onReject } - public func reject() -> Void { + public func reject() { onReject(self) } } @@ -40,6 +40,7 @@ public class CallState: ObservableObject { @Published public internal(set) var participantsMap = [String: CallParticipant]() { didSet { didUpdate(Array(participantsMap.values)) } } + @Published public internal(set) var localParticipant: CallParticipant? @Published public internal(set) var dominantSpeaker: CallParticipant? @Published public internal(set) var remoteParticipants: [CallParticipant] = [] @@ -53,6 +54,7 @@ public class CallState: ObservableObject { } } } + @Published public internal(set) var recordingState: RecordingState = .noRecording @Published public internal(set) var blockedUserIds: Set = [] @Published public internal(set) var settings: CallSettingsResponse? @@ -63,6 +65,7 @@ public class CallState: ObservableObject { @Published public internal(set) var createdAt: Date = .distantPast { didSet { if !isInitialized { isInitialized = true }} } + @Published public internal(set) var updatedAt: Date = .distantPast @Published public internal(set) var startsAt: Date? @Published public internal(set) var startedAt: Date? { @@ -70,6 +73,7 @@ public class CallState: ObservableObject { setupDurationTimer() } } + @Published public internal(set) var endedAt: Date? @Published public internal(set) var endedBy: User? @Published public internal(set) var custom: [String: RawJSON] = [:] @@ -84,6 +88,7 @@ public class CallState: ObservableObject { didUpdate(session) } } + @Published public internal(set) var reconnectionStatus = ReconnectionStatus.connected @Published public internal(set) var participantCount: UInt32 = 0 @Published public internal(set) var isInitialized: Bool = false @@ -97,83 +102,83 @@ public class CallState: ObservableObject { internal func updateState(from event: VideoEvent) { switch event { - case .typeBlockedUserEvent(let event): + case let .typeBlockedUserEvent(event): blockUser(id: event.user.id) - case .typeCallAcceptedEvent(let event): + case let .typeCallAcceptedEvent(event): update(from: event.call) - case .typeCallHLSBroadcastingStartedEvent(_): - self.egress?.broadcasting = true - case .typeCallHLSBroadcastingStoppedEvent(_): - self.egress?.broadcasting = false - case .typeCallCreatedEvent(let event): + case .typeCallHLSBroadcastingStartedEvent: + egress?.broadcasting = true + case .typeCallHLSBroadcastingStoppedEvent: + egress?.broadcasting = false + case let .typeCallCreatedEvent(event): update(from: event.call) mergeMembers(event.members) - case .typeCallEndedEvent(let event): + case let .typeCallEndedEvent(event): update(from: event.call) - case .typeCallLiveStartedEvent(let event): + case let .typeCallLiveStartedEvent(event): update(from: event.call) - case .typeCallMemberAddedEvent(let event): + case let .typeCallMemberAddedEvent(event): mergeMembers(event.members) - case .typeCallMemberRemovedEvent(let event): + case let .typeCallMemberRemovedEvent(event): let updated = members.filter { !event.members.contains($0.id) } - self.members = updated - case .typeCallMemberUpdatedEvent(let event): + members = updated + case let .typeCallMemberUpdatedEvent(event): mergeMembers(event.members) - case .typeCallMemberUpdatedPermissionEvent(let event): + case let .typeCallMemberUpdatedPermissionEvent(event): capabilitiesByRole = event.capabilitiesByRole mergeMembers(event.members) update(from: event.call) - case .typeCallNotificationEvent(let event): + case let .typeCallNotificationEvent(event): mergeMembers(event.members) update(from: event.call) - case .typeCallReactionEvent(_): + case .typeCallReactionEvent: break - case .typeCallRecordingStartedEvent(_): + case .typeCallRecordingStartedEvent: if recordingState != .recording { recordingState = .recording } - case .typeCallRecordingStoppedEvent(_): + case .typeCallRecordingStoppedEvent: if recordingState != .noRecording { recordingState = .noRecording } - case .typeCallRejectedEvent(let event): + case let .typeCallRejectedEvent(event): update(from: event.call) - case .typeCallRingEvent(let event): + case let .typeCallRingEvent(event): update(from: event.call) mergeMembers(event.members) - case .typeCallSessionEndedEvent(let event): + case let .typeCallSessionEndedEvent(event): update(from: event.call) - case .typeCallSessionParticipantJoinedEvent(let event): + case let .typeCallSessionParticipantJoinedEvent(event): if session?.participants.contains(event.participant) == false { session?.participants.append(event.participant) } - case .typeCallSessionParticipantLeftEvent(let event): + case let .typeCallSessionParticipantLeftEvent(event): session?.participants.removeAll(where: { participant in participant == event.participant }) - case .typeCallSessionStartedEvent(let event): + case let .typeCallSessionStartedEvent(event): update(from: event.call) - case .typeCallUpdatedEvent(let event): + case let .typeCallUpdatedEvent(event): update(from: event.call) - case .typePermissionRequestEvent(let event): + case let .typePermissionRequestEvent(event): addPermissionRequest(user: event.user.toUser, permissions: event.permissions, requestedAt: event.createdAt) - case .typeUnblockedUserEvent(let event): + case let .typeUnblockedUserEvent(event): unblockUser(id: event.user.id) - case .typeUpdatedCallPermissionsEvent(let event): + case let .typeUpdatedCallPermissionsEvent(event): updateOwnCapabilities(event) - case .typeConnectedEvent(_): + case .typeConnectedEvent: // note: connection events are not relevant for call state sync'ing break - case .typeConnectionErrorEvent(_): + case .typeConnectionErrorEvent: // note: connection events are not relevant for call state sync'ing break - case .typeCustomVideoEvent(_): + case .typeCustomVideoEvent: // note: custom events are exposed via event subscriptions break - case .typeHealthCheckEvent(_): + case .typeHealthCheckEvent: // note: health checks are not relevant for call state sync'ing break - case .typeCallUserMuted(_): + case .typeCallUserMuted: break } } @@ -288,7 +293,7 @@ public class CallState: ObservableObject { address: response.ingress.rtmp.address, streamKey: streamVideo.token.rawValue ) - self.ingress = Ingress(rtmp: rtmp) + ingress = Ingress(rtmp: rtmp) if !localCallSettingsUpdate { callSettings = response.settings.toCallSettings @@ -310,21 +315,20 @@ public class CallState: ObservableObject { else { return } - self.ownCapabilities = event.ownCapabilities + ownCapabilities = event.ownCapabilities } private func didUpdate(_ newParticipants: [CallParticipant]) { // Combine existing and newly added participants. - let currentParticipantIds = Set(self.participants.map(\.id)) + let currentParticipantIds = Set(participants.map(\.id)) let newlyAddedParticipants = Set(newParticipants.map(\.id)) .subtracting(currentParticipantIds) .compactMap { participantsMap[$0] } // Sort the updated participants. let updatedCurrentParticipants: [CallParticipant] = ( - self - .participants - .compactMap { participantsMap[$0.id] } + newlyAddedParticipants + participants + .compactMap { participantsMap[$0.id] } + newlyAddedParticipants ) .sorted(by: defaultComparators) @@ -359,14 +363,14 @@ public class CallState: ObservableObject { } // Update the respective class properties. - self.participants = updatedCurrentParticipants + participants = updatedCurrentParticipants self.screenSharingSession = screenSharingSession self.remoteParticipants = remoteParticipants self.activeSpeakers = activeSpeakers } private func didUpdate(_ egress: EgressResponse?) { - self.broadcasting = egress?.broadcasting ?? false + broadcasting = egress?.broadcasting ?? false } private func didUpdate(_ session: CallSessionResponse?) { diff --git a/Sources/StreamVideo/Controllers/CallController.swift b/Sources/StreamVideo/Controllers/CallController.swift index 98e2897cc..843487e7e 100644 --- a/Sources/StreamVideo/Controllers/CallController.swift +++ b/Sources/StreamVideo/Controllers/CallController.swift @@ -45,7 +45,7 @@ class CallController { self.callType = callType self.apiKey = apiKey self.videoConfig = videoConfig - self.sfuReconnectionTime = environment.sfuReconnectionTime + sfuReconnectionTime = environment.sfuReconnectionTime self.environment = environment self.defaultAPI = defaultAPI self.cachedLocation = cachedLocation @@ -84,7 +84,7 @@ class CallController { notify: notify ) - self.currentSFU = response.credentials.server.edgeName + currentSFU = response.credentials.server.edgeName let settings = callSettings ?? response.call.settings.toCallSettings try await connectToEdge( @@ -188,24 +188,24 @@ class CallController { /// Initiates a focus operation at a specific point on the camera's view. /// - /// This method attempts to focus the camera at the given point by calling the `focus(at:)` + /// This method attempts to focus the camera at the given point by calling the `focus(at:)` /// method on the current WebRTC client. The focus point is specified as a `CGPoint` within the /// coordinate space of the view. /// - /// - Parameter point: A `CGPoint` value representing the location within the view where the + /// - Parameter point: A `CGPoint` value representing the location within the view where the /// camera should attempt to focus. The coordinate space of the point is typically normalized to the /// range [0, 1], where (0, 0) represents the top-left corner of the view, and (1, 1) represents the /// bottom-right corner. - /// - Throws: An error if the focus operation cannot be completed. This might occur if there is no + /// - Throws: An error if the focus operation cannot be completed. This might occur if there is no /// current WebRTC client available, if the camera does not support tap to focus, or if an internal error /// occurs within the WebRTC client. /// - /// - Note: Before calling this method, ensure that the device's camera supports tap to focus + /// - Note: Before calling this method, ensure that the device's camera supports tap to focus /// functionality and that the current WebRTC client is properly configured and connected. Otherwise, /// the method may throw an error. - func focus(at point: CGPoint) throws { - try currentWebRTCClient().focus(at: point) - } + func focus(at point: CGPoint) throws { + try currentWebRTCClient().focus(at: point) + } /// Cleans up the call controller. func cleanUp() { @@ -309,7 +309,7 @@ class CallController { private func handleSignalChannelConnectionStateChange(_ state: WebSocketConnectionState) { switch state { - case .disconnected(let source): + case let .disconnected(source): log.debug("Signal channel disconnected") executeOnMain { [weak self] in self?.handleSignalChannelDisconnect(source: source) @@ -339,7 +339,7 @@ class CallController { return } guard (call.state.reconnectionStatus != .reconnecting || isRetry), - source != .userInitiated else { + source != .userInitiated else { return } if reconnectionDate == nil { @@ -382,8 +382,8 @@ class CallController { private func handleReconnectionError() { log.error("Error while reconnecting to the call") - self.call?.update(reconnectionStatus: .disconnected) - self.cleanUp() + call?.update(reconnectionStatus: .disconnected) + cleanUp() } private func handleSessionMigrationEvent() { diff --git a/Sources/StreamVideo/Controllers/CallsController.swift b/Sources/StreamVideo/Controllers/CallsController.swift index ec523ece0..a00398f68 100644 --- a/Sources/StreamVideo/Controllers/CallsController.swift +++ b/Sources/StreamVideo/Controllers/CallsController.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Combine +import Foundation /// Controller used for querying and watching calls. public class CallsController: ObservableObject { @@ -39,8 +39,8 @@ public class CallsController: ObservableObject { init(streamVideo: StreamVideo, callsQuery: CallsQuery) { self.callsQuery = callsQuery self.streamVideo = streamVideo - self.subscribeToWatchEvents() - self.subscribeToConnectionUpdates() + subscribeToWatchEvents() + subscribeToConnectionUpdates() } /// Loads the next page of calls. @@ -62,7 +62,7 @@ public class CallsController: ObservableObject { private func subscribeToConnectionUpdates() { streamVideo.state.$connection.sink { [weak self] status in guard let self = self else { return } - if case .disconnected(_) = status { + if case .disconnected = status { self.socketDisconnected = true } else if status == .disconnecting { self.socketDisconnected = true diff --git a/Sources/StreamVideo/Errors/Errors.swift b/Sources/StreamVideo/Errors/Errors.swift index 226d740c0..775dc072c 100644 --- a/Sources/StreamVideo/Errors/Errors.swift +++ b/Sources/StreamVideo/Errors/Errors.swift @@ -85,7 +85,7 @@ extension ClientError { public class MissingPermissions: ClientError {} /// Invalid url error. - public class InvalidURL: ClientError {} + public class InvalidURL: ClientError {} } // This should probably live only in the test target since it's not "true" equatable @@ -124,7 +124,7 @@ extension Error { var hasClientErrors: Bool { if let apiError = self as? APIError, - ClosedRange.clientErrorCodes ~= apiError.statusCode { + ClosedRange.clientErrorCodes ~= apiError.statusCode { return false } return true diff --git a/Sources/StreamVideo/HTTPClient/HTTPClient.swift b/Sources/StreamVideo/HTTPClient/HTTPClient.swift index 58eb19516..f48154a56 100644 --- a/Sources/StreamVideo/HTTPClient/HTTPClient.swift +++ b/Sources/StreamVideo/HTTPClient/HTTPClient.swift @@ -94,14 +94,20 @@ final class URLSessionClient: HTTPClient, @unchecked Sendable { continuation.resume(throwing: ClientError.InvalidToken()) } else { let requestURLString = request.url?.absoluteString ?? "" - log.debug("Error executing request \(requestURLString) \(String(describing: errorResponse))", subsystems: .httpRequests) + log.debug( + "Error executing request \(requestURLString) \(String(describing: errorResponse))", + subsystems: .httpRequests + ) continuation.resume(throwing: ClientError.NetworkError(response.description)) } return } else if response.statusCode >= 400 { let requestURLString = request.url?.absoluteString ?? "" let errorResponse = Self.errorResponse(from: data, response: response) as? [String: Any] - log.error("Error executing request \(requestURLString) \(String(describing: errorResponse))", subsystems: .httpRequests) + log.error( + "Error executing request \(requestURLString) \(String(describing: errorResponse))", + subsystems: .httpRequests + ) continuation.resume(throwing: ClientError.NetworkError(response.description)) return } diff --git a/Sources/StreamVideo/Models/CallSettings.swift b/Sources/StreamVideo/Models/CallSettings.swift index eb65a5370..5a6557c40 100644 --- a/Sources/StreamVideo/Models/CallSettings.swift +++ b/Sources/StreamVideo/Models/CallSettings.swift @@ -61,7 +61,6 @@ extension CallSettingsResponse { cameraPosition: video.cameraFacing == .back ? .back : .front ) } - } public extension CallSettings { diff --git a/Sources/StreamVideo/Models/CallsQuery.swift b/Sources/StreamVideo/Models/CallsQuery.swift index 0423e6f21..5af8ef717 100644 --- a/Sources/StreamVideo/Models/CallsQuery.swift +++ b/Sources/StreamVideo/Models/CallsQuery.swift @@ -25,7 +25,7 @@ public struct CallsQuery { public init( pageSize: Int = 25, sortParams: [CallSortParam], - filters: [String : RawJSON]? = nil, + filters: [String: RawJSON]? = nil, watch: Bool ) { self.pageSize = pageSize diff --git a/Sources/StreamVideo/Models/CoordinatorModels.swift b/Sources/StreamVideo/Models/CoordinatorModels.swift index f31dbdf8c..a84a31266 100644 --- a/Sources/StreamVideo/Models/CoordinatorModels.swift +++ b/Sources/StreamVideo/Models/CoordinatorModels.swift @@ -48,7 +48,7 @@ public struct CreateCallOptions: Sendable { public init( memberIds: [String]? = nil, members: [MemberRequest]? = nil, - custom: [String : RawJSON]? = nil, + custom: [String: RawJSON]? = nil, settings: CallSettingsRequest? = nil, startsAt: Date? = nil, team: String? = nil diff --git a/Sources/StreamVideo/Models/Member.swift b/Sources/StreamVideo/Models/Member.swift index 51e66db0b..96f7cc62c 100644 --- a/Sources/StreamVideo/Models/Member.swift +++ b/Sources/StreamVideo/Models/Member.swift @@ -10,6 +10,7 @@ public struct Member: Identifiable, Equatable, Sendable, Codable { public var id: String { user.id } + /// The underlying user. public let user: User /// The role of the member in the call. @@ -18,7 +19,7 @@ public struct Member: Identifiable, Equatable, Sendable, Codable { public let customData: [String: RawJSON] public let updatedAt: Date - public init(user: User, role: String? = nil, customData: [String : RawJSON] = [:], updatedAt: Date) { + public init(user: User, role: String? = nil, customData: [String: RawJSON] = [:], updatedAt: Date) { self.user = user self.role = role ?? user.role self.customData = customData diff --git a/Sources/StreamVideo/Models/Token.swift b/Sources/StreamVideo/Models/Token.swift index 7aa2a4ee6..8ebfe63bd 100644 --- a/Sources/StreamVideo/Models/Token.swift +++ b/Sources/StreamVideo/Models/Token.swift @@ -36,5 +36,4 @@ extension ClientError { public extension UserToken { static let empty = UserToken(rawValue: "") - } diff --git a/Sources/StreamVideo/Models/VideoOptions.swift b/Sources/StreamVideo/Models/VideoOptions.swift index 42172ed7e..b14f3757c 100644 --- a/Sources/StreamVideo/Models/VideoOptions.swift +++ b/Sources/StreamVideo/Models/VideoOptions.swift @@ -25,23 +25,23 @@ struct VideoOptions: Sendable { self.preferredFormat = preferredFormat self.preferredFps = preferredFps if let targetResolution { - self.preferredDimensions = CMVideoDimensions( + preferredDimensions = CMVideoDimensions( width: Int32(targetResolution.width), height: Int32(targetResolution.height) ) do { - self.supportedCodecs = try VideoCapturingUtils.codecs( + supportedCodecs = try VideoCapturingUtils.codecs( preferredFormat: preferredFormat, preferredDimensions: preferredDimensions, preferredFps: preferredFps, preferredBitrate: targetResolution.bitrate ) } catch { - self.supportedCodecs = VideoCodec.defaultCodecs + supportedCodecs = VideoCodec.defaultCodecs } } else { - self.preferredDimensions = .full - self.supportedCodecs = VideoCodec.defaultCodecs + preferredDimensions = .full + supportedCodecs = VideoCodec.defaultCodecs } } } diff --git a/Sources/StreamVideo/StreamVideo.swift b/Sources/StreamVideo/StreamVideo.swift index 98ac0f474..006f1f024 100644 --- a/Sources/StreamVideo/StreamVideo.swift +++ b/Sources/StreamVideo/StreamVideo.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftProtobuf import StreamWebRTC +import SwiftProtobuf public typealias UserTokenProvider = (@escaping (Result) -> Void) -> Void public typealias UserTokenUpdater = (UserToken) -> Void @@ -26,6 +26,7 @@ public class StreamVideo: ObservableObject { } } } + @Published public internal(set) var ringingCall: Call? init(user: User) { @@ -105,7 +106,7 @@ public class StreamVideo: ObservableObject { pushNotificationsConfig: pushNotificationsConfig, environment: Environment() ) - } + } init( apiKey: String, @@ -117,22 +118,22 @@ public class StreamVideo: ObservableObject { environment: Environment ) { self.apiKey = APIKey(apiKey) - self.state = State(user: user) + state = State(user: user) self.token = token self.tokenProvider = tokenProvider self.videoConfig = videoConfig self.environment = environment self.pushNotificationsConfig = pushNotificationsConfig - self.apiTransport = environment.apiTransportBuilder(tokenProvider) + apiTransport = environment.apiTransportBuilder(tokenProvider) let defaultParams = DefaultParams(apiKey: apiKey) - self.coordinatorClient = DefaultAPI( + coordinatorClient = DefaultAPI( basePath: Self.endpointConfig.baseVideoURL, transport: apiTransport, middlewares: [defaultParams] ) StreamVideoProviderKey.currentValue = self - (self.apiTransport as? URLSessionTransport)?.setTokenUpdater { [weak self] userToken in + (apiTransport as? URLSessionTransport)?.setTokenUpdater { [weak self] userToken in self?.token = userToken } if user.type != .anonymous { @@ -146,7 +147,7 @@ public class StreamVideo: ObservableObject { let anonymousAuth = AnonymousAuth(token: token.rawValue) coordinatorClient.middlewares.append(anonymousAuth) } - self.prefetchLocation() + prefetchLocation() connectTask = Task { if user.type == .guest { do { @@ -270,7 +271,7 @@ public class StreamVideo: ObservableObject { /// Subscribes to a particular WS event. /// - Returns: `AsyncStream` of the requested WS event. public func subscribe(for event: WSEvent.Type) -> AsyncStream { - return AsyncStream(event) { [weak self] continuation in + AsyncStream(event) { [weak self] continuation in let eventHandler: EventHandling = { event in guard let coordinatorEvent = event.unwrap() else { return @@ -287,7 +288,7 @@ public class StreamVideo: ObservableObject { next: String? = nil, watch: Bool = false ) async throws -> (calls: [Call], next: String?) { - try await self.queryCalls(filters:nil, sort:nil, next: next, watch: watch) + try await queryCalls(filters: nil, sort: nil, next: next, watch: watch) } public func queryCalls( @@ -296,7 +297,7 @@ public class StreamVideo: ObservableObject { limit: Int? = 25, watch: Bool = false ) async throws -> (calls: [Call], next: String?) { - try await self.queryCalls(filters:filters, sort:sort, limit:limit, next: nil, watch: watch) + try await queryCalls(filters: filters, sort: sort, limit: limit, next: nil, watch: watch) } internal func queryCalls( @@ -307,12 +308,12 @@ public class StreamVideo: ObservableObject { watch: Bool = false ) async throws -> (calls: [Call], next: String?) { let response = try await queryCalls(request: QueryCallsRequest(filterConditions: filters, limit: limit, sort: sort)) - return (response.calls.map({ + return (response.calls.map { let callController = makeCallController(callType: $0.call.type, callId: $0.call.id) - let call = Call(from: $0, coordinatorClient: self.coordinatorClient, callController: callController) + let call = Call(from: $0, coordinatorClient: self.coordinatorClient, callController: callController) eventsMiddleware.add(subscriber: call) return call - }), response.next) + }, response.next) } /// Queries calls with the provided request. @@ -403,7 +404,7 @@ public class StreamVideo: ObservableObject { } guard webSocketClient?.connectionState == .connecting - || webSocketClient?.connectionState == .authenticating else { + || webSocketClient?.connectionState == .authenticating else { return "" } @@ -527,7 +528,7 @@ public class StreamVideo: ObservableObject { apiKey: String, environment: Environment, result: @escaping (Result) -> Void - ) { + ) { Task { do { let response = try await createGuestUser( @@ -549,7 +550,6 @@ public class StreamVideo: ObservableObject { self.cachedLocation = try await LocationFetcher.getLocation() } } - } extension StreamVideo: ConnectionStateDelegate { diff --git a/Sources/StreamVideo/Utils/EndpointConfig.swift b/Sources/StreamVideo/Utils/EndpointConfig.swift index a28dd7697..efdaea21a 100644 --- a/Sources/StreamVideo/Utils/EndpointConfig.swift +++ b/Sources/StreamVideo/Utils/EndpointConfig.swift @@ -6,7 +6,7 @@ import Foundation struct EndpointConfig { let hostname: String - let wsEndpoint: String + let wsEndpoint: String var baseVideoURL: String { "\(hostname)video" } diff --git a/Sources/StreamVideo/Utils/IntExtensions.swift b/Sources/StreamVideo/Utils/IntExtensions.swift index 82478e60c..4730ad1bb 100644 --- a/Sources/StreamVideo/Utils/IntExtensions.swift +++ b/Sources/StreamVideo/Utils/IntExtensions.swift @@ -4,7 +4,7 @@ import Foundation -extension FixedWidthInteger { +extension FixedWidthInteger { func roundUp(toMultipleOf powerOfTwo: Self) -> Self { precondition(powerOfTwo > 0 && powerOfTwo & (powerOfTwo &- 1) == 0) return (self + (powerOfTwo &- 1)) & (0 &- powerOfTwo) diff --git a/Sources/StreamVideo/Utils/LocationFetcher.swift b/Sources/StreamVideo/Utils/LocationFetcher.swift index 966193386..48ab1979f 100644 --- a/Sources/StreamVideo/Utils/LocationFetcher.swift +++ b/Sources/StreamVideo/Utils/LocationFetcher.swift @@ -21,5 +21,4 @@ class LocationFetcher { } throw FetchingLocationError() } - } diff --git a/Sources/StreamVideo/Utils/Sorting.swift b/Sources/StreamVideo/Utils/Sorting.swift index b67a5d76d..590bc8fa7 100644 --- a/Sources/StreamVideo/Utils/Sorting.swift +++ b/Sources/StreamVideo/Utils/Sorting.swift @@ -135,13 +135,14 @@ func comparison( } // MARK: - Comparators + // MARK: Aggregators /// Combines multiple comparators into a single comparator. /// It uses the first comparator to compare two elements. If they are deemed "equal" (i.e., orderedSame), /// it moves to the next comparator, and so on, until a decision can be made or all comparators have been exhausted. public func combineComparators(_ comparators: [StreamSortComparator]) -> StreamSortComparator { - return { a, b in + { a, b in for comparator in comparators { let result = comparator(a, b) if result != .orderedSame { @@ -154,9 +155,10 @@ public func combineComparators(_ comparators: [StreamSortComparator]) -> S /// Returns a new comparator that only applies the given comparator if the predicate returns true. /// If the predicate returns false, it deems the two elements "equal" (i.e., orderedSame). -public func conditional(_ predicate: @escaping (T, T) -> Bool) -> (@escaping StreamSortComparator) -> StreamSortComparator { - return { comparator in - return { a, b in +public func conditional(_ predicate: @escaping (T, T) -> Bool) + -> (@escaping StreamSortComparator) -> StreamSortComparator { + { comparator in + { a, b in if !predicate(a, b) { return .orderedSame } @@ -184,10 +186,10 @@ public var pinned: StreamSortComparator = { a, b in switch (a.pin, b.pin) { case (nil, _?): return .orderedDescending case (_?, nil): return .orderedAscending - case (let aPin?, let bPin?) where aPin.isLocal && !bPin.isLocal: return .orderedAscending - case (let aPin?, let bPin?) where !aPin.isLocal && bPin.isLocal: return .orderedDescending - case (let aPin?, let bPin?) where aPin.pinnedAt > bPin.pinnedAt: return .orderedAscending - case (let aPin?, let bPin?) where aPin.pinnedAt < bPin.pinnedAt: return .orderedDescending + case let (aPin?, bPin?) where aPin.isLocal && !bPin.isLocal: return .orderedAscending + case let (aPin?, bPin?) where !aPin.isLocal && bPin.isLocal: return .orderedDescending + case let (aPin?, bPin?) where aPin.pinnedAt > bPin.pinnedAt: return .orderedAscending + case let (aPin?, bPin?) where aPin.pinnedAt < bPin.pinnedAt: return .orderedDescending default: return .orderedSame } } @@ -206,7 +208,7 @@ public var name: StreamSortComparator = { comparison($0, $1, ke /// A comparator creator which will set up a comparator which prioritizes participants who have a specific role. public func roles(_ priorityRoles: [String] = ["admin", "host", "speaker"]) -> StreamSortComparator { - return { (p1, p2) in + { (p1, p2) in if p1.roles == p2.roles { return .orderedSame } for role in priorityRoles { if p1.roles.contains(role) && !p2.roles.contains(role) { diff --git a/Sources/StreamVideo/Utils/StringExtensions.swift b/Sources/StreamVideo/Utils/StringExtensions.swift index 8eb0f1ffc..2f1563921 100644 --- a/Sources/StreamVideo/Utils/StringExtensions.swift +++ b/Sources/StreamVideo/Utils/StringExtensions.swift @@ -7,7 +7,7 @@ import Foundation extension String { var preferredRedCodec: String { - let parts = self.components(separatedBy: "\r\n") + let parts = components(separatedBy: "\r\n") var redId: String = "" var opusId: String = "" for part in parts { @@ -25,7 +25,7 @@ extension String { if !redId.isEmpty && !opusId.isEmpty { let redOpusPair = "\(redId) \(opusId)" let opusRedPair = "\(opusId) \(redId)" - let updatedResult = self.replacingOccurrences( + let updatedResult = replacingOccurrences( of: opusRedPair, with: redOpusPair ) @@ -44,5 +44,4 @@ extension String { guard components.count > 1 else { return "" } return components[1] } - } diff --git a/Sources/StreamVideo/Utils/SystemEnvironment+XStreamClient.swift b/Sources/StreamVideo/Utils/SystemEnvironment+XStreamClient.swift index 5ec4f4cba..c43c94d74 100644 --- a/Sources/StreamVideo/Utils/SystemEnvironment+XStreamClient.swift +++ b/Sources/StreamVideo/Utils/SystemEnvironment+XStreamClient.swift @@ -93,7 +93,7 @@ extension SystemEnvironment { } extension Array { - private subscript(safe index: Int) -> Element? { - return indices ~= index ? self[index] : nil - } + private subscript(safe index: Int) -> Element? { + indices ~= index ? self[index] : nil + } } diff --git a/Sources/StreamVideo/Utils/ThermalStateObserver.swift b/Sources/StreamVideo/Utils/ThermalStateObserver.swift index e8899d726..8b0c78de4 100644 --- a/Sources/StreamVideo/Utils/ThermalStateObserver.swift +++ b/Sources/StreamVideo/Utils/ThermalStateObserver.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Combine +import Foundation extension LogSubsystem { public static let thermalState = Self(rawValue: 1 << 6) @@ -72,10 +72,9 @@ final class ThermalStateObserver: ObservableObject, ThermalStateObserving { self.init { ProcessInfo.processInfo.thermalState } } - init(thermalStateProvider: @escaping () -> ProcessInfo.ThermalState) { // Initialize the thermal state with the current process's thermal state - self.state = thermalStateProvider() + state = thermalStateProvider() self.thermalStateProvider = thermalStateProvider // Set up a publisher to monitor thermal state changes diff --git a/Sources/StreamVideo/Utils/Utils.swift b/Sources/StreamVideo/Utils/Utils.swift index 6f251efbc..8a8c3a9b8 100644 --- a/Sources/StreamVideo/Utils/Utils.swift +++ b/Sources/StreamVideo/Utils/Utils.swift @@ -22,14 +22,14 @@ public enum CallNotification { public static let participantLeft = "StreamVideo.Call.ParticipantLeft" } -typealias EventHandling = ((WrappedEvent) -> ())? +typealias EventHandling = ((WrappedEvent) -> Void)? -func executeOnMain(_ task: @escaping @MainActor () -> ()) { +func executeOnMain(_ task: @escaping @MainActor() -> Void) { Task { await task() } } func infoPlistValue(for key: String) -> String? { - return Bundle.main.infoDictionary?[key] as? String + Bundle.main.infoDictionary?[key] as? String } diff --git a/Sources/StreamVideo/WebRTC/AudioSession.swift b/Sources/StreamVideo/WebRTC/AudioSession.swift index e01a663a5..e0081961e 100644 --- a/Sources/StreamVideo/WebRTC/AudioSession.swift +++ b/Sources/StreamVideo/WebRTC/AudioSession.swift @@ -41,7 +41,6 @@ actor AudioSession { defer { audioSession.unlockForConfiguration() } audioSession.isAudioEnabled = enabled } - } extension RTCAudioSessionConfiguration { diff --git a/Sources/StreamVideo/WebRTC/PeerConnection.swift b/Sources/StreamVideo/WebRTC/PeerConnection.swift index 1ea03950d..492ccaa4d 100644 --- a/Sources/StreamVideo/WebRTC/PeerConnection.swift +++ b/Sources/StreamVideo/WebRTC/PeerConnection.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftProtobuf import StreamWebRTC +import SwiftProtobuf enum PeerConnectionType: String { case subscriber @@ -183,7 +183,10 @@ class PeerConnection: NSObject, RTCPeerConnectionDelegate, @unchecked Sendable { func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {} func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) { - log.debug("New stream added with id = \(stream.streamId) for \(type.rawValue), sfu = \(signalService.hostname)", subsystems: .webRTC) + log.debug( + "New stream added with id = \(stream.streamId) for \(type.rawValue), sfu = \(signalService.hostname)", + subsystems: .webRTC + ) if stream.streamId.contains(WebRTCClient.Constants.screenshareTrackType) { screensharingStreams.append(stream) } @@ -265,11 +268,11 @@ class PeerConnection: NSObject, RTCPeerConnectionDelegate, @unchecked Sendable { func update(configuration: RTCConfiguration?) { guard let configuration else { return } - self.pc.setConfiguration(configuration) + pc.setConfiguration(configuration) } func statsReport() async throws -> RTCStatisticsReport { - return try await withCheckedThrowingContinuation { [weak self] continuation in + try await withCheckedThrowingContinuation { [weak self] continuation in guard let self else { return continuation.resume(throwing: ClientError.Unexpected()) } diff --git a/Sources/StreamVideo/WebRTC/Retries.swift b/Sources/StreamVideo/WebRTC/Retries.swift index 6b4ca8b5d..86a77e2a0 100644 --- a/Sources/StreamVideo/WebRTC/Retries.swift +++ b/Sources/StreamVideo/WebRTC/Retries.swift @@ -43,7 +43,6 @@ func executeTask( throw error } } - } struct RetryPolicy { @@ -76,6 +75,6 @@ extension RetryPolicy { } static func delay(retries: Int) -> TimeInterval { - return TimeInterval.random(in: 0.5...2.5) + TimeInterval.random(in: 0.5...2.5) } } diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferConnection.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferConnection.swift index ec4c1a8d8..8641c5ac0 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferConnection.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferConnection.swift @@ -57,7 +57,7 @@ class BroadcastBufferConnection: NSObject { scheduleStreams() } - //MARK: - private + // MARK: - private private func scheduleStreams() { shouldKeepRunning = true diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReader.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReader.swift index d2e54f794..2197e7209 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReader.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReader.swift @@ -2,9 +2,9 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation -import CoreVideo import CoreImage +import CoreVideo +import Foundation import StreamWebRTC private class Message { @@ -38,7 +38,7 @@ private class Message { framedMessage, BroadcastConstants.contentLength as CFString )?.takeRetainedValue(), - let body = CFHTTPMessageCopyBody(framedMessage)?.takeRetainedValue() + let body = CFHTTPMessageCopyBody(framedMessage)?.takeRetainedValue() else { return -1 } @@ -49,7 +49,7 @@ private class Message { let missingBytesCount = contentLength - bodyLength if missingBytesCount == 0 { let success = unwrapMessage(framedMessage) - self.onComplete?(success, self) + onComplete?(success, self) self.framedMessage = nil } @@ -57,7 +57,7 @@ private class Message { } private func imageContext() -> CIContext? { - return Message.imageContextVar + Message.imageContextVar } private func unwrapMessage(_ framedMessage: CFHTTPMessage) -> Bool { @@ -81,7 +81,7 @@ private class Message { let width = Int(CFStringGetIntValue(widthStr)) let height = Int(CFStringGetIntValue(heightStr)) - self.imageOrientation = CGImagePropertyOrientation( + imageOrientation = CGImagePropertyOrientation( rawValue: UInt32(CFStringGetIntValue(imageOrientationStr)) ) ?? .up @@ -208,7 +208,6 @@ final class BroadcastBufferReader: NSObject { onCapture?(pixelBuffer, rotation) } - } extension BroadcastBufferReader: StreamDelegate { diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReaderConnection.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReaderConnection.swift index 64c8015ab..bffb70610 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReaderConnection.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferReaderConnection.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Darwin +import Foundation final class BroadcastBufferReaderConnection: BroadcastBufferConnection { private let streamDelegate: StreamDelegate @@ -61,7 +61,7 @@ final class BroadcastBufferReaderConnection: BroadcastBufferConnection { Darwin.close(socketHandle) } - //MARK: - private + // MARK: - private private func setupAddress() -> Bool { var addr = sockaddr_un() diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploadConnection.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploadConnection.swift index 8eec2239a..d38346eeb 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploadConnection.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploadConnection.swift @@ -25,8 +25,8 @@ class BroadcastBufferUploadConnection: BroadcastBufferConnection { func open() -> Bool { guard FileManager.default.fileExists(atPath: filePath), - setupAddress(), - connectSocket() else { + setupAddress(), + connectSocket() else { return false } @@ -40,7 +40,7 @@ class BroadcastBufferUploadConnection: BroadcastBufferConnection { return true } - //MARK: - private + // MARK: - private private func setupAddress() -> Bool { var addr = sockaddr_un() diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploader.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploader.swift index 13f3bbe5a..0806e8706 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploader.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastBufferUploader.swift @@ -3,8 +3,8 @@ // import Foundation -import StreamWebRTC import ReplayKit +import StreamWebRTC actor BroadcastBufferUploader { diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastObserver.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastObserver.swift index 1acb6eddc..f77f9eb42 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastObserver.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastObserver.swift @@ -16,11 +16,11 @@ public class BroadcastObserver: ObservableObject { public init() {} - lazy var broadcastStarted: CFNotificationCallback = { center, observer, name, object, userInfo in + lazy var broadcastStarted: CFNotificationCallback = { _, _, _, _, _ in postNotification(with: BroadcastConstants.broadcastStartedNotification) } - lazy var broadcastStopped: CFNotificationCallback = { center, observer, name, object, userInfo in + lazy var broadcastStopped: CFNotificationCallback = { _, _, _, _, _ in postNotification(with: BroadcastConstants.broadcastStoppedNotification) } @@ -61,10 +61,10 @@ public class BroadcastObserver: ObservableObject { } @objc func handleBroadcastStarted() { - self.broadcastState = .started + broadcastState = .started } @objc func handleBroadcastStopped() { - self.broadcastState = .finished + broadcastState = .finished } } diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastSampleHandler.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastSampleHandler.swift index 91c4a360b..470347403 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastSampleHandler.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastSampleHandler.swift @@ -9,7 +9,7 @@ open class BroadcastSampleHandler: RPBroadcastSampleHandler { private var clientConnection: BroadcastBufferUploadConnection? private var uploader: BroadcastBufferUploader? - private let notificationCenter: CFNotificationCenter + private let notificationCenter: CFNotificationCenter private var socketFilePath: String { guard let appGroupIdentifier = infoPlistValue(for: BroadcastConstants.broadcastAppGroupIdentifier), let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) @@ -23,20 +23,20 @@ open class BroadcastSampleHandler: RPBroadcastSampleHandler { .path } - public override init() { + override public init() { notificationCenter = CFNotificationCenterGetDarwinNotifyCenter() super.init() - if let connection = BroadcastBufferUploadConnection(filePath: self.socketFilePath) { - self.clientConnection = connection - self.setupConnection() - self.uploader = BroadcastBufferUploader(connection: connection) + if let connection = BroadcastBufferUploadConnection(filePath: socketFilePath) { + clientConnection = connection + setupConnection() + uploader = BroadcastBufferUploader(connection: connection) } } override public func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) { postNotification(BroadcastConstants.broadcastStartedNotification) - self.openConnection() + openConnection() } override public func broadcastPaused() {} @@ -59,7 +59,7 @@ open class BroadcastSampleHandler: RPBroadcastSampleHandler { } } - //MARK: - private + // MARK: - private private func setupConnection() { clientConnection?.onClose = { [weak self] error in diff --git a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastScreenCapturer.swift b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastScreenCapturer.swift index bcbfae975..41e45da94 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/BroadcastScreenCapturer.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/BroadcastScreenCapturer.swift @@ -3,8 +3,8 @@ // import Foundation -import StreamWebRTC import ReplayKit +import StreamWebRTC class BroadcastScreenCapturer: VideoCapturing { @@ -23,13 +23,13 @@ class BroadcastScreenCapturer: VideoCapturing { ) { self.videoOptions = videoOptions self.videoSource = videoSource -#if targetEnvironment(simulator) + #if targetEnvironment(simulator) videoCapturer = RTCFileVideoCapturer(delegate: videoSource) -#else + #else let handler = StreamVideoCaptureHandler(source: videoSource, filters: videoFilters, handleRotation: false) videoCaptureHandler = handler videoCapturer = RTCCameraVideoCapturer(delegate: handler) -#endif + #endif } func startCapture(device: AVCaptureDevice?) async throws { @@ -38,7 +38,7 @@ class BroadcastScreenCapturer: VideoCapturing { } guard let identifier = infoPlistValue(for: BroadcastConstants.broadcastAppGroupIdentifier), - let filePath = self.filePathForIdentifier(identifier) + let filePath = filePathForIdentifier(identifier) else { return } @@ -93,14 +93,14 @@ class BroadcastScreenCapturer: VideoCapturing { } func stopCapture() async throws { - guard self.bufferReader != nil else { + guard bufferReader != nil else { // already stopped return } - self.bufferReader?.stopCapturing() - self.bufferReader = nil - await (videoCapturer as? RTCCameraVideoCapturer)?.stopCapture() + bufferReader?.stopCapturing() + bufferReader = nil + await(videoCapturer as? RTCCameraVideoCapturer)?.stopCapture() } private func filePathForIdentifier(_ identifier: String) -> String? { diff --git a/Sources/StreamVideo/WebRTC/Screensharing/ScreenshareCapturer.swift b/Sources/StreamVideo/WebRTC/Screensharing/ScreenshareCapturer.swift index acdc1de7a..413cfa5c2 100644 --- a/Sources/StreamVideo/WebRTC/Screensharing/ScreenshareCapturer.swift +++ b/Sources/StreamVideo/WebRTC/Screensharing/ScreenshareCapturer.swift @@ -3,8 +3,8 @@ // import Foundation -import StreamWebRTC import ReplayKit +import StreamWebRTC class ScreenshareCapturer: VideoCapturing { private var videoCapturer: RTCVideoCapturer @@ -42,7 +42,7 @@ class ScreenshareCapturer: VideoCapturing { RPScreenRecorder.shared().isMicrophoneEnabled = false return try await withCheckedThrowingContinuation { continuation in - RPScreenRecorder.shared().startCapture(handler: { [weak self] sampleBuffer, type, error in + RPScreenRecorder.shared().startCapture(handler: { [weak self] sampleBuffer, type, _ in guard let self else { return } self.handle(sampleBuffer: sampleBuffer, type: type, for: device) }) { error in @@ -57,7 +57,7 @@ class ScreenshareCapturer: VideoCapturing { func stopCapture() async throws { try await stopScreensharing() - await (videoCapturer as? RTCCameraVideoCapturer)?.stopCapture() + await(videoCapturer as? RTCCameraVideoCapturer)?.stopCapture() } func handle(sampleBuffer: CMSampleBuffer, type: RPSampleBufferType, for device: AVCaptureDevice) { @@ -89,9 +89,9 @@ class ScreenshareCapturer: VideoCapturing { timeStampNs: timeStampNs ) - self.videoCaptureHandler?.capturer(self.videoCapturer, didCapture: rtcFrame) + videoCaptureHandler?.capturer(videoCapturer, didCapture: rtcFrame) if let dimensions = outputFormat.dimensions { - self.videoSource.adaptOutputFormat( + videoSource.adaptOutputFormat( toWidth: dimensions.width, height: dimensions.height, fps: Int32(outputFormat.fps) @@ -101,7 +101,7 @@ class ScreenshareCapturer: VideoCapturing { } private func stopScreensharing() async throws { - return try await withCheckedThrowingContinuation { continuation in + try await withCheckedThrowingContinuation { continuation in guard RPScreenRecorder.shared().isRecording else { continuation.resume(returning: ()) return @@ -115,5 +115,4 @@ class ScreenshareCapturer: VideoCapturing { } } } - } diff --git a/Sources/StreamVideo/WebRTC/SfuMiddleware.swift b/Sources/StreamVideo/WebRTC/SfuMiddleware.swift index a3537afc1..248809466 100644 --- a/Sources/StreamVideo/WebRTC/SfuMiddleware.swift +++ b/Sources/StreamVideo/WebRTC/SfuMiddleware.swift @@ -15,9 +15,9 @@ class SfuMiddleware: EventMiddleware { private var subscriber: PeerConnection? private var publisher: PeerConnection? var onSocketConnected: ((Bool) -> Void)? - var onParticipantCountUpdated: ((UInt32) -> ())? + var onParticipantCountUpdated: ((UInt32) -> Void)? var onSessionMigrationEvent: (() -> Void)? - var onPinsChanged: (([Stream_Video_Sfu_Models_Pin]) -> ())? + var onPinsChanged: (([Stream_Video_Sfu_Models_Pin]) -> Void)? init( sessionID: String, @@ -52,43 +52,43 @@ class SfuMiddleware: EventMiddleware { return } switch event { - case .subscriberOffer(let event): + case let .subscriberOffer(event): await handleSubscriberEvent(event) - case .publisherAnswer(_): + case .publisherAnswer: log.warning("Publisher answer event shouldn't be sent") - case .connectionQualityChanged(let event): + case let .connectionQualityChanged(event): await handleConnectionQualityChangedEvent(event) - case .audioLevelChanged(let event): + case let .audioLevelChanged(event): await handleAudioLevelsChanged(event) - case .iceTrickle(let event): + case let .iceTrickle(event): try await handleICETrickle(event) - case .changePublishQuality(let event): + case let .changePublishQuality(event): handleChangePublishQualityEvent(event) - case .participantJoined(let event): + case let .participantJoined(event): await handleParticipantJoined(event) - case .participantLeft(let event): + case let .participantLeft(event): await handleParticipantLeft(event) - case .dominantSpeakerChanged(let event): + case let .dominantSpeakerChanged(event): await handleDominantSpeakerChanged(event) - case .joinResponse(let event): + case let .joinResponse(event): onSocketConnected?(event.reconnected) await loadParticipants(from: event) - case .healthCheckResponse(let event): + case let .healthCheckResponse(event): onParticipantCountUpdated?(event.participantCount.total) - case .trackPublished(let event): + case let .trackPublished(event): await handleTrackPublishedEvent(event) - case .trackUnpublished(let event): + case let .trackUnpublished(event): await handleTrackUnpublishedEvent(event) - case .error(let event): + case let .error(event): log.error(event.error.message, error: event.error) - case .callGrantsUpdated(_): + case .callGrantsUpdated: log.warning("TODO: callGrantsUpdated") - case .goAway(let event): + case let .goAway(event): log.info("Received go away event with reason: \(event.reason.rawValue)") onSessionMigrationEvent?() - case .iceRestart(_): + case .iceRestart: log.info("Received ice restart message") - case .pinsUpdated(let event): + case let .pinsUpdated(event): log.debug("Pins changed \(event.pins.map(\.sessionID))") onPinsChanged?(event.pins) } diff --git a/Sources/StreamVideo/WebRTC/SimulatorScreenCapturer.swift b/Sources/StreamVideo/WebRTC/SimulatorScreenCapturer.swift index bcafb824b..8570b3f3d 100644 --- a/Sources/StreamVideo/WebRTC/SimulatorScreenCapturer.swift +++ b/Sources/StreamVideo/WebRTC/SimulatorScreenCapturer.swift @@ -45,7 +45,8 @@ final class SimulatorScreenCapturer: RTCVideoCapturer { track: track, outputSettings: [ kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32BGRA) - ]) + ] + ) do { assetReader = try AVAssetReader(asset: asset) assetReader?.add(trackOutput) @@ -65,15 +66,19 @@ final class SimulatorScreenCapturer: RTCVideoCapturer { let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! let frameTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) let videoFrame = RTCCVPixelBuffer(pixelBuffer: pixelBuffer) - let rtcVideoFrame = RTCVideoFrame(buffer: videoFrame, rotation: ._0, timeStampNs: Int64(CMTimeGetSeconds(frameTime) * 1e9)) + let rtcVideoFrame = RTCVideoFrame( + buffer: videoFrame, + rotation: ._0, + timeStampNs: Int64(CMTimeGetSeconds(frameTime) * 1e9) + ) DispatchQueue.main.async { self.delegate?.capturer(self, didCapture: rtcVideoFrame) } } else { // Reached the end of the video file, restart from the beginning - self.assetReader?.cancelReading() - self.setupAssetReader() + assetReader?.cancelReading() + setupAssetReader() } } } diff --git a/Sources/StreamVideo/WebRTC/StatsReporter.swift b/Sources/StreamVideo/WebRTC/StatsReporter.swift index 4274c94d9..63e096acb 100644 --- a/Sources/StreamVideo/WebRTC/StatsReporter.swift +++ b/Sources/StreamVideo/WebRTC/StatsReporter.swift @@ -41,7 +41,7 @@ enum StatsReporter { for (_, value) in values { let stats = value.values if let trackIdentifier = stats[StatsConstants.trackIdentifier] as? String, - stats[StatsConstants.kind] as? String == "video" { + stats[StatsConstants.kind] as? String == "video" { if var existing = current[trackIdentifier] { for (key, value) in stats { existing[key] = value @@ -52,7 +52,7 @@ enum StatsReporter { } } if let codecId = stats[StatsConstants.codecId] as? String, - stats[StatsConstants.kind] as? String == "video" { + stats[StatsConstants.kind] as? String == "video" { fallbackCodecId = codecId } } diff --git a/Sources/StreamVideo/WebRTC/StreamVideoCaptureHandler.swift b/Sources/StreamVideo/WebRTC/StreamVideoCaptureHandler.swift index 207381426..94263d2d8 100644 --- a/Sources/StreamVideo/WebRTC/StreamVideoCaptureHandler.swift +++ b/Sources/StreamVideo/WebRTC/StreamVideoCaptureHandler.swift @@ -49,8 +49,8 @@ final class StreamVideoCaptureHandler: NSObject, RTCVideoCapturerDelegate { } let updatedFrame = handleRotation - ? adjustRotation(capturer, for: _buffer, frame: frame) - : frame + ? adjustRotation(capturer, for: _buffer, frame: frame) + : frame self.source.capturer(capturer, didCapture: updatedFrame) } @@ -100,11 +100,11 @@ final class StreamVideoCaptureHandler: NSObject, RTCVideoCapturerDelegate { } deinit { - NotificationCenter.default.removeObserver( + NotificationCenter.default.removeObserver( self, name: UIDevice.orientationDidChangeNotification, object: nil - ) + ) } } diff --git a/Sources/StreamVideo/WebRTC/VideoCapturer.swift b/Sources/StreamVideo/WebRTC/VideoCapturer.swift index 5078b745b..91eb8543f 100644 --- a/Sources/StreamVideo/WebRTC/VideoCapturer.swift +++ b/Sources/StreamVideo/WebRTC/VideoCapturer.swift @@ -53,7 +53,7 @@ class VideoCapturer: CameraVideoCapturing { } func startCapture(device: AVCaptureDevice?) async throws { - return try await withCheckedThrowingContinuation { continuation in + try await withCheckedThrowingContinuation { continuation in guard let videoCapturer = videoCapturer as? RTCCameraVideoCapturer, let device else { continuation.resume(throwing: ClientError.Unexpected()) return @@ -90,11 +90,11 @@ class VideoCapturer: CameraVideoCapturing { continuation.resume(returning: ()) } } - } + } as Void } func stopCapture() async throws { - return try await withCheckedThrowingContinuation { continuation in + try await withCheckedThrowingContinuation { continuation in if let capturer = videoCapturer as? RTCCameraVideoCapturer { capturer.stopCapture { continuation.resume(returning: ()) @@ -107,18 +107,18 @@ class VideoCapturer: CameraVideoCapturing { /// Initiates a focus and exposure operation at the specified point on the camera's view. /// - /// This method attempts to focus the camera and set the exposure at a specific point by interacting + /// This method attempts to focus the camera and set the exposure at a specific point by interacting /// with the device's capture session. - /// It requires the `videoCapturer` property to be cast to `RTCCameraVideoCapturer`, and for + /// It requires the `videoCapturer` property to be cast to `RTCCameraVideoCapturer`, and for /// a valid `AVCaptureDeviceInput` to be accessible. /// If these conditions are not met, it throws a `ClientError.Unexpected` error. /// - /// - Parameter point: A `CGPoint` representing the location within the view where the camera + /// - Parameter point: A `CGPoint` representing the location within the view where the camera /// should adjust focus and exposure. - /// - Throws: A `ClientError.Unexpected` error if the necessary video capture components are + /// - Throws: A `ClientError.Unexpected` error if the necessary video capture components are /// not available or properly configured. /// - /// - Note: Ensure that the `point` is normalized to the camera's coordinate space, ranging + /// - Note: Ensure that the `point` is normalized to the camera's coordinate space, ranging /// from (0,0) at the top-left to (1,1) at the bottom-right. func focus(at point: CGPoint) throws { guard @@ -159,7 +159,7 @@ class VideoCapturer: CameraVideoCapturing { } } - //MARK: - private + // MARK: - private private func checkForBackgroundCameraAccess() { if #available(iOS 16, *) { @@ -196,8 +196,8 @@ extension AVCaptureDevice.Format { videoSupportedFrameRateRanges .map { $0.toRange() } .reduce(into: 0...0) { result, current in - result = merge(range: result, with: current) - } + result = merge(range: result, with: current) + } } } diff --git a/Sources/StreamVideo/WebRTC/VideoCapturing.swift b/Sources/StreamVideo/WebRTC/VideoCapturing.swift index 4d12c99e4..0a88f8564 100644 --- a/Sources/StreamVideo/WebRTC/VideoCapturing.swift +++ b/Sources/StreamVideo/WebRTC/VideoCapturing.swift @@ -10,7 +10,7 @@ protocol VideoCapturing { func stopCapture() async throws } -protocol CameraVideoCapturing: VideoCapturing { +protocol CameraVideoCapturing: VideoCapturing { func setCameraPosition(_ cameraPosition: AVCaptureDevice.Position) async throws func setVideoFilter(_ videoFilter: VideoFilter?) func capturingDevice(for cameraPosition: AVCaptureDevice.Position) -> AVCaptureDevice? diff --git a/Sources/StreamVideo/WebRTC/VideoCapturingUtils.swift b/Sources/StreamVideo/WebRTC/VideoCapturingUtils.swift index fb6f0062b..356c00f16 100644 --- a/Sources/StreamVideo/WebRTC/VideoCapturingUtils.swift +++ b/Sources/StreamVideo/WebRTC/VideoCapturingUtils.swift @@ -72,7 +72,7 @@ enum VideoCapturingUtils { selectedFormat = foundFormat } else { selectedFormat = sortedFormats.first(where: { $0.dimensions.area >= preferredDimensions.area - && $0.format.fpsRange().contains(preferredFps) + && $0.format.fpsRange().contains(preferredFps) }) if selectedFormat == nil { @@ -87,10 +87,16 @@ enum VideoCapturingUtils { } guard let selectedFormat = selectedFormat else { - log.warning("Unable to resolve format with preferredDimensions:\(preferredDimensions.width)x\(preferredDimensions.height) preferredFormat:\(String(describing: preferredFormat)) preferredFPS:\(preferredFps)") + log + .warning( + "Unable to resolve format with preferredDimensions:\(preferredDimensions.width)x\(preferredDimensions.height) preferredFormat:\(String(describing: preferredFormat)) preferredFPS:\(preferredFps)" + ) return (format: nil, dimensions: nil, fps: 0) } - log.debug("SelectedFormat dimensions:\(selectedFormat.dimensions.width)x\(selectedFormat.dimensions.height) format:\(selectedFormat.format) diff:\(selectedFormat.diff)") + log + .debug( + "SelectedFormat dimensions:\(selectedFormat.dimensions.width)x\(selectedFormat.dimensions.height) format:\(selectedFormat.format) diff:\(selectedFormat.diff)" + ) var selectedFps = preferredFps let fpsRange = selectedFormat.format.fpsRange() diff --git a/Sources/StreamVideo/WebRTC/WebRTCClient.swift b/Sources/StreamVideo/WebRTC/WebRTCClient.swift index d3073d3d3..74c2a126a 100644 --- a/Sources/StreamVideo/WebRTC/WebRTCClient.swift +++ b/Sources/StreamVideo/WebRTC/WebRTCClient.swift @@ -38,7 +38,6 @@ class WebRTCClient: NSObject, @unchecked Sendable { scheduledUpdate = false } } - } } var tracks = [String: RTCVideoTrack]() @@ -181,14 +180,15 @@ class WebRTCClient: NSObject, @unchecked Sendable { @Injected(\.thermalStateObserver) private var thermalStateObserver var onParticipantsUpdated: (([String: CallParticipant]) -> Void)? - var onSignalConnectionStateChange: ((WebSocketConnectionState) -> ())? - var onParticipantCountUpdated: ((UInt32) -> ())? - var onSessionMigrationEvent: (() -> ())? { + var onSignalConnectionStateChange: ((WebSocketConnectionState) -> Void)? + var onParticipantCountUpdated: ((UInt32) -> Void)? + var onSessionMigrationEvent: (() -> Void)? { didSet { sfuMiddleware.onSessionMigrationEvent = onSessionMigrationEvent } } - var onSessionMigrationCompleted: (() -> ())? + + var onSessionMigrationCompleted: (() -> Void)? /// The notification center used to send and receive notifications about incoming events. private(set) lazy var eventNotificationCenter: EventNotificationCenter = { @@ -308,7 +308,7 @@ class WebRTCClient: NSObject, @unchecked Sendable { hostname: url, token: token ) - self.migratingSignalService = signalServer + migratingSignalService = signalServer if let url = URL(string: webSocketURL) { migratingWSClient = makeWebSocketClient( url: url, @@ -402,7 +402,7 @@ class WebRTCClient: NSObject, @unchecked Sendable { let connectURL = signalChannel?.connectURL try await executeTask(retryPolicy: .neverGonnaGiveYouUp { [weak self] in self?.sfuChanged(connectURL) == false - && self?.callSettings.audioOn == !isEnabled + && self?.callSettings.audioOn == !isEnabled }) { _ = try await signalService.updateMuteStates(updateMuteStatesRequest: request) callSettings = callSettings.withUpdatedAudioState(isEnabled) @@ -444,7 +444,7 @@ class WebRTCClient: NSObject, @unchecked Sendable { let connectURL = signalChannel?.connectURL try await executeTask(retryPolicy: .neverGonnaGiveYouUp { [weak self] in self?.sfuChanged(connectURL) == false - && self?.callSettings.videoOn == !isEnabled + && self?.callSettings.videoOn == !isEnabled }) { _ = try await signalService.updateMuteStates(updateMuteStatesRequest: request) callSettings = callSettings.withUpdatedVideoState(isEnabled) @@ -522,7 +522,7 @@ class WebRTCClient: NSObject, @unchecked Sendable { } else { throw ClientError.MissingPermissions() } - self.currentScreenhsareType = type + currentScreenhsareType = type try await screenshareCapturer?.startCapture(device: nil) } @@ -899,7 +899,6 @@ class WebRTCClient: NSObject, @unchecked Sendable { return joinRequest } - private func makeWebSocketClient( url: URL, apiKey: APIKey, @@ -1024,12 +1023,13 @@ class WebRTCClient: NSObject, @unchecked Sendable { var tracks = [Stream_Video_Sfu_Signal_TrackSubscriptionDetails]() let callParticipants = await state.callParticipants - - for (_, value) in callParticipants { if value.id != sessionID { if value.hasVideo { - log.debug("updating video subscription for user \(value.name) with size \(value.trackSize)", subsystems: .webRTC) + log.debug( + "updating video subscription for user \(value.name) with size \(value.trackSize)", + subsystems: .webRTC + ) var dimension = Stream_Video_Sfu_Models_VideoDimension() dimension.height = UInt32(value.trackSize.height) dimension.width = UInt32(value.trackSize.width) @@ -1158,7 +1158,6 @@ class WebRTCClient: NSObject, @unchecked Sendable { } await state.update(pausedTrackIds: pausedTrackIds) } - } @objc private func unpauseTracks() { @@ -1189,7 +1188,7 @@ class WebRTCClient: NSObject, @unchecked Sendable { ) updated = participant.withUpdated(pin: pin) } else if !sessionIds.contains(sessionId) - && (participant.pin != nil && participant.pin?.isLocal == false) { + && (participant.pin != nil && participant.pin?.isLocal == false) { updated = participant.withUpdated(pin: nil) } updatedParticipants[sessionId] = updated @@ -1233,7 +1232,8 @@ class WebRTCClient: NSObject, @unchecked Sendable { } @objc private func handleConnectionStateChange(_ notification: NSNotification) { - guard let status = notification.userInfo?[Notification.internetConnectionStatusUserInfoKey] as? InternetConnection.Status else { + guard let status = notification.userInfo?[Notification.internetConnectionStatusUserInfoKey] as? InternetConnection.Status + else { return } @@ -1279,8 +1279,10 @@ class WebRTCClient: NSObject, @unchecked Sendable { private func checkFastReconnectionStatus(retries: Int = 0) { DispatchQueue.main.asyncAfter(deadline: .now() + Constants.fastReconnectTimeout) { [weak self] in guard let self else { return } - if (self.isPeerConnectionConnecting(self.publisher, otherNotDisconnected: self.subscriber) - || self.isPeerConnectionConnecting(self.subscriber, otherNotDisconnected: self.publisher)) + if ( + self.isPeerConnectionConnecting(self.publisher, otherNotDisconnected: self.subscriber) + || self.isPeerConnectionConnecting(self.subscriber, otherNotDisconnected: self.publisher) + ) && retries == 0 { log.debug("Still connecting, check again after the interval") self.checkFastReconnectionStatus(retries: 1) diff --git a/Sources/StreamVideo/WebSockets/Client/WebSocketClient.swift b/Sources/StreamVideo/WebSockets/Client/WebSocketClient.swift index 55b97193e..5040da139 100644 --- a/Sources/StreamVideo/WebSockets/Client/WebSocketClient.swift +++ b/Sources/StreamVideo/WebSockets/Client/WebSocketClient.swift @@ -100,7 +100,7 @@ class WebSocketClient { /// Calling this method has no effect is the web socket is already connected, or is in the connecting phase. func connect() { switch connectionState { - // Calling connect in the following states has no effect + // Calling connect in the following states has no effect case .connecting, .authenticating, .connected(healthCheckInfo: _): return default: break @@ -202,11 +202,11 @@ extension WebSocketClient: WebSocketEngineDelegate { } switch event { - case .coordinatorEvent(let event): + case let .coordinatorEvent(event): log.info("received WS \(event.type) event from coordinator \(connectURL)", subsystems: .webSocket) - case .internalEvent(_): + case .internalEvent: break - case .sfuEvent(_): + case .sfuEvent: break } @@ -236,7 +236,11 @@ extension WebSocketClient: WebSocketEngineDelegate { connectionState = .disconnected(source: source) case .initialized, .disconnected: - log.error("Web socket can not be disconnected when in \(connectionState) state", subsystems: .webSocket, error: engineError) + log.error( + "Web socket can not be disconnected when in \(connectionState) state", + subsystems: .webSocket, + error: engineError + ) } } diff --git a/Sources/StreamVideo/WebSockets/Events/Event.swift b/Sources/StreamVideo/WebSockets/Events/Event.swift index 38c1a6e2f..c5f6a4e88 100644 --- a/Sources/StreamVideo/WebSockets/Events/Event.swift +++ b/Sources/StreamVideo/WebSockets/Events/Event.swift @@ -27,7 +27,7 @@ extension Event { } func forCall(cid: String) -> Bool { - guard let videoEvent = self.unwrap() else { + guard let videoEvent = unwrap() else { return false } guard let wsCallEvent = videoEvent.rawValue as? WSCallEvent else { @@ -50,17 +50,19 @@ internal enum WrappedEvent: Event { return HealthCheckInfo(coordinatorHealthCheck: healthCheckEvent) } if case let .typeConnectedEvent(connectedEvent) = event { - return HealthCheckInfo(coordinatorHealthCheck: .init( - connectionId: connectedEvent.connectionId, - createdAt: connectedEvent.createdAt, - type: connectedEvent.type) + return HealthCheckInfo( + coordinatorHealthCheck: .init( + connectionId: connectedEvent.connectionId, + createdAt: connectedEvent.createdAt, + type: connectedEvent.type + ) ) } case let .sfuEvent(event): if case let .healthCheckResponse(healthCheckEvent) = event { return HealthCheckInfo(sfuHealthCheck: healthCheckEvent) } - case .internalEvent(_): + case .internalEvent: break } return nil @@ -77,7 +79,7 @@ internal enum WrappedEvent: Event { return errorEvent.error } return nil - case .internalEvent(_): + case .internalEvent: break } return nil diff --git a/Sources/StreamVideo/WebSockets/Events/WSEventsMiddleware.swift b/Sources/StreamVideo/WebSockets/Events/WSEventsMiddleware.swift index a9331b49c..d5dba4a8a 100644 --- a/Sources/StreamVideo/WebSockets/Events/WSEventsMiddleware.swift +++ b/Sources/StreamVideo/WebSockets/Events/WSEventsMiddleware.swift @@ -38,5 +38,4 @@ final class WSEventsMiddleware: EventMiddleware { protocol WSEventsSubscriber: AnyObject { func onEvent(_ event: WrappedEvent) - } diff --git a/Sources/StreamVideoSwiftUI/CallContainer.swift b/Sources/StreamVideoSwiftUI/CallContainer.swift index 9d3c6a2a5..1f3ab760a 100644 --- a/Sources/StreamVideoSwiftUI/CallContainer.swift +++ b/Sources/StreamVideoSwiftUI/CallContainer.swift @@ -88,7 +88,7 @@ public struct CallContainer: View { private var shouldShowCallView: Bool { switch viewModel.callingState { - case .outgoing, .incoming(_), .inCall, .joining, .lobby(_): + case .outgoing, .incoming(_), .inCall, .joining, .lobby: return true default: return false diff --git a/Sources/StreamVideoSwiftUI/CallView/CallControlsView.swift b/Sources/StreamVideoSwiftUI/CallView/CallControlsView.swift index 5f6dc9601..02a681275 100644 --- a/Sources/StreamVideoSwiftUI/CallView/CallControlsView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/CallControlsView.swift @@ -204,5 +204,4 @@ public struct SpeakerIconView: View { } ) } - } diff --git a/Sources/StreamVideoSwiftUI/CallView/CallDurationView.swift b/Sources/StreamVideoSwiftUI/CallView/CallDurationView.swift index c5620c82f..fbacdff47 100644 --- a/Sources/StreamVideoSwiftUI/CallView/CallDurationView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/CallDurationView.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI /// A view that presents the call's duration and recording state. public struct CallDurationView: View { @@ -20,7 +20,7 @@ public struct CallDurationView: View { @MainActor public init(_ viewModel: CallViewModel) { self.viewModel = viewModel - self.duration = viewModel.call?.state.duration ?? 0 + duration = viewModel.call?.state.duration ?? 0 } public var body: some View { @@ -28,14 +28,14 @@ public struct CallDurationView: View { if duration > 0, let formattedDuration = formatter.format(duration) { HStack(spacing: 4) { iconView - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 12) - .foregroundColor( - viewModel.recordingState == .recording - ? colors.inactiveCallControl - : colors.onlineIndicatorColor - ) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 12) + .foregroundColor( + viewModel.recordingState == .recording + ? colors.inactiveCallControl + : colors.onlineIndicatorColor + ) TimeView(formattedDuration) .layoutPriority(2) @@ -52,7 +52,7 @@ public struct CallDurationView: View { .accessibility(identifier: accessibilityLabel) } - // MARK - Private Helpers + // MARK: - Private Helpers private var iconView: Image { viewModel.recordingState == .recording @@ -70,7 +70,7 @@ public struct CallDurationView: View { } } -fileprivate struct TimeView: View { +private struct TimeView: View { @Injected(\.fonts) private var fonts: Fonts @Injected(\.colors) private var colors: Colors @@ -80,8 +80,16 @@ fileprivate struct TimeView: View { fileprivate init(_ value: String) { let attributed = NSMutableAttributedString(string: value) self.value = attributed - self.value.addAttribute(.foregroundColor, value: colors.callDurationColor.withAlphaComponent(0.6), range: .init(location: 0, length: attributed.length - 3)) - self.value.addAttribute(.foregroundColor, value: colors.callDurationColor, range: .init(location: attributed.length - 3, length: 3)) + self.value.addAttribute( + .foregroundColor, + value: colors.callDurationColor.withAlphaComponent(0.6), + range: .init(location: 0, length: attributed.length - 3) + ) + self.value.addAttribute( + .foregroundColor, + value: colors.callDurationColor, + range: .init(location: attributed.length - 3, length: 3) + ) } fileprivate var body: some View { diff --git a/Sources/StreamVideoSwiftUI/CallView/CallTopView.swift b/Sources/StreamVideoSwiftUI/CallView/CallTopView.swift index cd1b06579..4c665cb1a 100644 --- a/Sources/StreamVideoSwiftUI/CallView/CallTopView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/CallTopView.swift @@ -21,9 +21,9 @@ public struct CallTopView: View { Group { HStack(spacing: 0) { HStack { - if + if #available(iOS 14.0, *), - viewModel.callParticipants.count > 1 + viewModel.callParticipants.count > 1 { LayoutMenuView(viewModel: viewModel) .opacity(hideLayoutMenu ? 0 : 1) @@ -58,12 +58,12 @@ public struct CallTopView: View { } .overlay( viewModel.call?.state.isCurrentUserScreensharing == true ? - SharingIndicator( - viewModel: viewModel, - sharingPopupDismissed: $sharingPopupDismissed - ) - .opacity(sharingPopupDismissed ? 0 : 1) - : nil + SharingIndicator( + viewModel: viewModel, + sharingPopupDismissed: $sharingPopupDismissed + ) + .opacity(sharingPopupDismissed ? 0 : 1) + : nil ) } @@ -103,10 +103,8 @@ public struct SharingIndicator: View { .frame(height: 14) } .padding(.leading, 4) - } .padding(.all, 8) .modifier(ShadowViewModifier()) } - } diff --git a/Sources/StreamVideoSwiftUI/CallView/CallView.swift b/Sources/StreamVideoSwiftUI/CallView/CallView.swift index ef1d8ba7b..92e94fb54 100644 --- a/Sources/StreamVideoSwiftUI/CallView/CallView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/CallView.swift @@ -3,8 +3,8 @@ // import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI public struct CallView: View { @@ -46,9 +46,9 @@ public struct CallView: View { .modifier(ShadowViewModifier()) .padding() .accessibility(identifier: "participantEventLabel") -#if STREAM_E2E_TESTS + #if STREAM_E2E_TESTS .offset(y: 300) -#endif + #endif } Spacer() @@ -73,8 +73,7 @@ public struct CallView: View { .accessibility(identifier: "localVideoView") } else if let screenSharingSession = viewModel.call?.state.screenSharingSession, - viewModel.call?.state.isCurrentUserScreensharing == false - { + viewModel.call?.state.isCurrentUserScreensharing == false { viewFactory.makeScreenSharingView( viewModel: viewModel, screensharingSession: screenSharingSession, @@ -109,8 +108,8 @@ public struct CallView: View { private var shouldShowDraggableView: Bool { (viewModel.call?.state.screenSharingSession == nil || viewModel.call?.state.isCurrentUserScreensharing == true) - && viewModel.participantsLayout == .grid - && viewModel.participants.count <= 3 + && viewModel.participantsLayout == .grid + && viewModel.participants.count <= 3 } @ViewBuilder @@ -140,7 +139,8 @@ public struct CallView: View { availableFrame: bounds, ratio: bounds.width / bounds.height, showAllInfo: true - )) + ) + ) .accessibility(identifier: "minimizedParticipantView") } else { EmptyView() diff --git a/Sources/StreamVideoSwiftUI/CallView/CornerDraggableView.swift b/Sources/StreamVideoSwiftUI/CallView/CornerDraggableView.swift index 795d6a059..f59a76e17 100644 --- a/Sources/StreamVideoSwiftUI/CallView/CornerDraggableView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/CornerDraggableView.swift @@ -33,7 +33,7 @@ public struct CornerDraggableView: View { self.onTap = onTap let proxyFrame = proxy.frame(in: .local) - self.availableFrame = .init( + availableFrame = .init( origin: .zero, size: .init( width: proxyFrame.width * scaleFactorX, diff --git a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/ControlBadgeView.swift b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/ControlBadgeView.swift index a8e6d382b..6dd5f21be 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/ControlBadgeView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/ControlBadgeView.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI public struct ControlBadgeView: View { @Injected(\.colors) private var colors diff --git a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ModalButton.swift b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ModalButton.swift index 58b614ebc..91947cafa 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ModalButton.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ModalButton.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI public struct ModalButton: View { diff --git a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ParticipantsListButton.swift b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ParticipantsListButton.swift index 9da49eb5e..a7a6e3b5c 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ParticipantsListButton.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/Controls/ParticipantsListButton.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI /// A button that can be used to present toggle the Participants List's presentation. Additionally, it will /// display a badge with the number of the total participants in the call. diff --git a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/HorizontalParticipantsListView.swift b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/HorizontalParticipantsListView.swift index e6ccb4281..da72fed29 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/HorizontalParticipantsListView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LayoutComponents/HorizontalParticipantsListView.swift @@ -59,8 +59,8 @@ public struct HorizontalParticipantsListView: View { ) self.barFrame = barFrame - let aspectRatioWidth = min(barFrame.width, barFrame.height * 16/9) - self.itemFrame = .init( + let aspectRatioWidth = min(barFrame.width, barFrame.height * 16 / 9) + itemFrame = .init( origin: .zero, size: .init( width: aspectRatioWidth - innerItemSpace / 2, diff --git a/Sources/StreamVideoSwiftUI/CallView/LayoutMenuView.swift b/Sources/StreamVideoSwiftUI/CallView/LayoutMenuView.swift index 53c0570d3..62317405f 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LayoutMenuView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LayoutMenuView.swift @@ -51,13 +51,12 @@ public struct LayoutMenuView: View { } } - struct LayoutMenuItem: View { var title: String var layout: ParticipantsLayout var selectedLayout: ParticipantsLayout - var selectLayout: (ParticipantsLayout) -> () + var selectLayout: (ParticipantsLayout) -> Void var body: some View { Button { @@ -74,5 +73,4 @@ struct LayoutMenuItem: View { } } } - } diff --git a/Sources/StreamVideoSwiftUI/CallView/LocalParticipantViewModifier.swift b/Sources/StreamVideoSwiftUI/CallView/LocalParticipantViewModifier.swift index e4b19d933..b0ab80006 100644 --- a/Sources/StreamVideoSwiftUI/CallView/LocalParticipantViewModifier.swift +++ b/Sources/StreamVideoSwiftUI/CallView/LocalParticipantViewModifier.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI @available(iOS 14.0, *) public struct LocalParticipantViewModifier: ViewModifier { @@ -26,7 +26,7 @@ public struct LocalParticipantViewModifier: ViewModifier { self.localParticipant = localParticipant self.call = call _microphoneChecker = .init(wrappedValue: .init()) - self._callSettings = callSettings + _callSettings = callSettings self.showAllInfo = showAllInfo self.decorations = .init(decorations) } @@ -74,7 +74,6 @@ public struct LocalParticipantViewModifier: ViewModifier { } } - @available(iOS, introduced: 13, obsoleted: 14) public struct LocalParticipantViewModifier_iOS13: ViewModifier { @@ -95,7 +94,7 @@ public struct LocalParticipantViewModifier_iOS13: ViewModifier { self.localParticipant = localParticipant self.call = call _microphoneChecker = .init(wrappedValue: .init()) - self._callSettings = callSettings + _callSettings = callSettings self.showAllInfo = showAllInfo self.decorations = .init(decorations) } diff --git a/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoView.swift b/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoView.swift index e5154b6e7..c25591482 100644 --- a/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoView.swift @@ -99,7 +99,7 @@ struct CallParticipantsViewContainer: View { ) } } - .padding(.horizontal) + .padding(.horizontal) } HStack(spacing: 16) { @@ -128,7 +128,7 @@ struct CallParticipantsViewContainer: View { EmptyView() } } - .toolbar{ + .toolbar { ToolbarItem(placement: .navigationBarTrailing) { ModalButton(image: images.xmark, action: closeTapped) .accessibility(identifier: "Close") @@ -188,7 +188,7 @@ struct ParticipantsButton: View { struct BlockedUsersView: View { var blockedUsers: [User] - var unblockActions: @MainActor (User) -> [CallParticipantMenuAction] + var unblockActions: @MainActor(User) -> [CallParticipantMenuAction] var body: some View { HStack { @@ -238,21 +238,21 @@ struct CallParticipantView: View { UserAvatar(imageURL: imageURL, size: imageSize) { CircledTitleView( title: participant.name.isEmpty - ? participant.id - : String(participant.name.uppercased().first!), + ? participant.id + : String(participant.name.uppercased().first!), size: imageSize ) } } else { CircledTitleView( title: participant.name.isEmpty - ? participant.id - : String(participant.name.uppercased().first!), + ? participant.id + : String(participant.name.uppercased().first!), size: imageSize ) } } - .overlay(TopRightView { OnlineIndicatorView(indicatorSize: imageSize * 0.3) } ) + .overlay(TopRightView { OnlineIndicatorView(indicatorSize: imageSize * 0.3) }) Text(participant.name) .font(fonts.bodyBold) diff --git a/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoViewModel.swift b/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoViewModel.swift index 5902af109..5e19c8a09 100644 --- a/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoViewModel.swift +++ b/Sources/StreamVideoSwiftUI/CallView/Participants/CallParticipantsInfoViewModel.swift @@ -108,10 +108,10 @@ class CallParticipantsInfoViewModel: ObservableObject { } } - private func executeMute(userId: String,audio: Bool = true,video: Bool = true){ + private func executeMute(userId: String, audio: Bool = true, video: Bool = true) { guard let call else { return } Task { - try await call.mute(userId:userId, audio:audio, video:video) + try await call.mute(userId: userId, audio: audio, video: video) } } } diff --git a/Sources/StreamVideoSwiftUI/CallView/ParticipantsFullScreenLayout.swift b/Sources/StreamVideoSwiftUI/CallView/ParticipantsFullScreenLayout.swift index 5de632b63..ca7341507 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ParticipantsFullScreenLayout.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ParticipantsFullScreenLayout.swift @@ -3,8 +3,8 @@ // import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI public struct ParticipantsFullScreenLayout: View { @@ -19,7 +19,7 @@ public struct ParticipantsFullScreenLayout: View { participant: CallParticipant, call: Call?, frame: CGRect, - onChangeTrackVisibility: @escaping @MainActor (CallParticipant, Bool) -> Void + onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void ) { self.viewFactory = viewFactory self.participant = participant @@ -50,9 +50,11 @@ public struct ParticipantsFullScreenLayout: View { log.debug("Participant \(participant.name) is visible") onChangeTrackVisibility(participant, true) } - .modifier(ParticipantChangeModifier( - participant: participant, - onChangeTrackVisibility: onChangeTrackVisibility) + .modifier( + ParticipantChangeModifier( + participant: participant, + onChangeTrackVisibility: onChangeTrackVisibility + ) ) } @@ -77,5 +79,4 @@ struct ParticipantChangeModifier: ViewModifier { content } } - } diff --git a/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridLayout.swift b/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridLayout.swift index cd0acaa02..cd0085b9e 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridLayout.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridLayout.swift @@ -3,8 +3,8 @@ // import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI public struct ParticipantsGridLayout: View { @@ -310,7 +310,6 @@ struct VerticalParticipantsView: View { } } - struct HorizontalParticipantsView: View { var viewFactory: Factory diff --git a/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridView.swift b/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridView.swift index 744bcbe0e..a9d691010 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ParticipantsGridView.swift @@ -3,8 +3,8 @@ // import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI @MainActor struct ParticipantsGridView: View { diff --git a/Sources/StreamVideoSwiftUI/CallView/ParticipantsSpotlightLayout.swift b/Sources/StreamVideoSwiftUI/CallView/ParticipantsSpotlightLayout.swift index ab8f45e71..470d202bc 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ParticipantsSpotlightLayout.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ParticipantsSpotlightLayout.swift @@ -21,7 +21,7 @@ public struct ParticipantsSpotlightLayout: View { participants: [CallParticipant], frame: CGRect, innerItemSpace: CGFloat = 8, - onChangeTrackVisibility: @escaping @MainActor (CallParticipant, Bool) -> Void + onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void ) { self.viewFactory = viewFactory self.participant = participant @@ -55,7 +55,7 @@ public struct ParticipantsSpotlightLayout: View { } private var topParticipantFrame: CGRect { - /// Top + /// Top .init( origin: frame.origin, size: CGSize(width: frame.size.width, height: frame.height - participantsStripFrame.height - innerItemSpace) diff --git a/Sources/StreamVideoSwiftUI/CallView/ReconnectionView.swift b/Sources/StreamVideoSwiftUI/CallView/ReconnectionView.swift index b42c28500..6845565f5 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ReconnectionView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ReconnectionView.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import SwiftUI import StreamVideo +import SwiftUI public struct ReconnectionView: View { diff --git a/Sources/StreamVideoSwiftUI/CallView/RecordingView.swift b/Sources/StreamVideoSwiftUI/CallView/RecordingView.swift index e6a0de57f..60de95f0b 100644 --- a/Sources/StreamVideoSwiftUI/CallView/RecordingView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/RecordingView.swift @@ -6,7 +6,7 @@ import SwiftUI public struct RecordingView: View { - public init() { /* Public init. */} + public init() { /* Public init. */ } public var body: some View { HStack { diff --git a/Sources/StreamVideoSwiftUI/CallView/SampleBufferVideoCallView.swift b/Sources/StreamVideoSwiftUI/CallView/SampleBufferVideoCallView.swift index 48a3a76e9..0d64efd30 100644 --- a/Sources/StreamVideoSwiftUI/CallView/SampleBufferVideoCallView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/SampleBufferVideoCallView.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import UIKit import AVKit +import UIKit final class SampleBufferVideoCallView: UIView { override class var layerClass: AnyClass { @@ -11,6 +11,6 @@ final class SampleBufferVideoCallView: UIView { } var sampleBufferDisplayLayer: AVSampleBufferDisplayLayer { - return layer as! AVSampleBufferDisplayLayer + layer as! AVSampleBufferDisplayLayer } } diff --git a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/BroadcastPickerView.swift b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/BroadcastPickerView.swift index 639b506eb..d22d16fe2 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/BroadcastPickerView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/BroadcastPickerView.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import SwiftUI import ReplayKit +import SwiftUI public struct BroadcastPickerView: UIViewRepresentable { @@ -23,5 +23,4 @@ public struct BroadcastPickerView: UIViewRepresentable { } public func updateUIView(_ uiView: UIViewType, context: Context) {} - } diff --git a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreenSharingView.swift b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreenSharingView.swift index f4f1ef6cd..f0bcc09fe 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreenSharingView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreenSharingView.swift @@ -30,7 +30,7 @@ public struct ScreenSharingView: View { ) { self.viewModel = viewModel self.screenSharing = screenSharing - self.frame = availableFrame + frame = availableFrame self.innerItemSpace = innerItemSpace self.viewFactory = viewFactory self.isZoomEnabled = isZoomEnabled @@ -61,7 +61,7 @@ public struct ScreenSharingView: View { ) } } - .onRotate { newOrientation in + .onRotate { _ in orientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .unknown } } @@ -73,7 +73,10 @@ public struct ScreenSharingView: View { contentMode: .scaleAspectFit ) { view in if let track = screenSharing.participant.screenshareTrack { - log.info("Found \(track.kind) track:\(track.trackId) for \(screenSharing.participant.name) and will add on \(type(of: self)):\(identifier))", subsystems: .webRTC) + log.info( + "Found \(track.kind) track:\(track.trackId) for \(screenSharing.participant.name) and will add on \(type(of: self)):\(identifier))", + subsystems: .webRTC + ) view.add(track: track) } } @@ -120,5 +123,4 @@ struct HorizontalContainer: View { HStack(content: content) } } - } diff --git a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreensharingControls.swift b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreensharingControls.swift index a9dae3023..8455fc4c8 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreensharingControls.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ScreenSharing/ScreensharingControls.swift @@ -53,7 +53,7 @@ public struct BroadcastIconView: View { self.viewModel = viewModel self.preferredExtension = preferredExtension self.size = size - self.offset = { + offset = { if #available(iOS 16.0, *) { return .init(x: -5, y: -4) } else { diff --git a/Sources/StreamVideoSwiftUI/CallView/VideoParticipantsView.swift b/Sources/StreamVideoSwiftUI/CallView/VideoParticipantsView.swift index 3e6fb6635..70b05a8e2 100644 --- a/Sources/StreamVideoSwiftUI/CallView/VideoParticipantsView.swift +++ b/Sources/StreamVideoSwiftUI/CallView/VideoParticipantsView.swift @@ -3,8 +3,8 @@ // import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI public struct VideoParticipantsView: View { @@ -71,7 +71,6 @@ public struct VideoParticipantsView: View { orientation = .landscapeRight default: orientation = .unknown - break } } } @@ -243,7 +242,8 @@ public struct VideoCallParticipantOptionsModifier: ViewModifier { .actionSheet(isPresented: $presentActionSheet) { ActionSheet( title: Text("\(participant.name)"), - buttons: elements.map { ActionSheet.Button.default(Text($0.title), action: $0.action) } + [ActionSheet.Button.cancel()] + buttons: elements + .map { ActionSheet.Button.default(Text($0.title), action: $0.action) } + [ActionSheet.Button.cancel()] ) } } @@ -454,16 +454,15 @@ public struct SoundIndicator: View { .accessibility(identifier: "participantMic") .streamAccessibility(value: participant.hasAudio ? "1" : "0") } - } public struct PopoverButton: View { var title: String @Binding var popoverShown: Bool - var action: () -> () + var action: () -> Void - public init(title: String, popoverShown: Binding, action: @escaping () -> ()) { + public init(title: String, popoverShown: Binding, action: @escaping () -> Void) { self.title = title _popoverShown = popoverShown self.action = action diff --git a/Sources/StreamVideoSwiftUI/CallView/VideoRenderer.swift b/Sources/StreamVideoSwiftUI/CallView/VideoRenderer.swift index c1c06c6ed..03769a974 100644 --- a/Sources/StreamVideoSwiftUI/CallView/VideoRenderer.swift +++ b/Sources/StreamVideoSwiftUI/CallView/VideoRenderer.swift @@ -2,11 +2,11 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // +import Combine +import MetalKit import StreamVideo -import SwiftUI import StreamWebRTC -import MetalKit -import Combine +import SwiftUI public struct LocalVideoView: View { @@ -54,7 +54,6 @@ public struct LocalVideoView: View { private var shouldRotate: Bool { callSettings.cameraPosition == .front && callSettings.videoOn } - } public struct VideoRendererView: UIViewRepresentable { @@ -93,8 +92,8 @@ public struct VideoRendererView: UIViewRepresentable { public func makeUIView(context: Context) -> VideoRenderer { let view = showVideo - ? utils.videoRendererFactory.view(for: id, size: size) - : VideoRenderer() + ? utils.videoRendererFactory.view(for: id, size: size) + : VideoRenderer() view.videoContentMode = contentMode view.backgroundColor = colors.participantBackground if showVideo { @@ -127,14 +126,15 @@ public class VideoRenderer: RTCMTLVideoView { log.debug("🔄 preferredFramesPerSecond was updated to \(preferredFramesPerSecond).") } } + private lazy var metalView: MTKView? = { subviews.compactMap { $0 as? MTKView }.first }() - var trackId: String? { self.track?.trackId } + var trackId: String? { track?.trackId } private var viewSize: CGSize? @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - public override init(frame: CGRect) { + override public init(frame: CGRect) { super.init(frame: frame) cancellable = thermalStateObserver .statePublisher @@ -158,7 +158,7 @@ public class VideoRenderer: RTCMTLVideoView { track?.remove(self) } - public override var hash: Int { identifier.hashValue } + override public var hash: Int { identifier.hashValue } public func add(track: RTCVideoTrack) { queue.sync { @@ -170,9 +170,9 @@ public class VideoRenderer: RTCMTLVideoView { } } - public override func layoutSubviews() { + override public func layoutSubviews() { super.layoutSubviews() - self.viewSize = bounds.size + viewSize = bounds.size } } @@ -180,16 +180,22 @@ extension VideoRenderer { public func handleViewRendering( for participant: CallParticipant, - onTrackSizeUpdate: @escaping (CGSize, CallParticipant) -> () + onTrackSizeUpdate: @escaping (CGSize, CallParticipant) -> Void ) { if let track = participant.track { - log.info("Found \(track.kind) track:\(track.trackId) for \(participant.name) and will add on \(type(of: self)):\(identifier))", subsystems: .webRTC) - self.add(track: track) + log.info( + "Found \(track.kind) track:\(track.trackId) for \(participant.name) and will add on \(type(of: self)):\(identifier))", + subsystems: .webRTC + ) + add(track: track) DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.01) { [weak self] in guard let self else { return } let prev = participant.trackSize if let viewSize, prev != viewSize { - log.debug("Update trackSize of \(track.kind) track for \(participant.name) on \(type(of: self)):\(identifier)), \(prev) → \(viewSize)", subsystems: .webRTC) + log.debug( + "Update trackSize of \(track.kind) track for \(participant.name) on \(type(of: self)):\(identifier)), \(prev) → \(viewSize)", + subsystems: .webRTC + ) onTrackSizeUpdate(viewSize, participant) } } diff --git a/Sources/StreamVideoSwiftUI/CallView/ViewModifiers/ParticipantsListViewModifier.swift b/Sources/StreamVideoSwiftUI/CallView/ViewModifiers/ParticipantsListViewModifier.swift index 490202da5..bfd5d898d 100644 --- a/Sources/StreamVideoSwiftUI/CallView/ViewModifiers/ParticipantsListViewModifier.swift +++ b/Sources/StreamVideoSwiftUI/CallView/ViewModifiers/ParticipantsListViewModifier.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI extension View { @@ -15,10 +15,10 @@ extension View { @ObservedObject viewModel: CallViewModel, viewFactory: Factory ) -> some View { - self.halfSheet(isPresented: $viewModel.participantsShown) { + halfSheet(isPresented: $viewModel.participantsShown) { viewFactory.makeParticipantsListView(viewModel: viewModel) - .opacity(viewModel.hideUIElements ? 0 : 1) - .accessibility(identifier: "trailingTopView") + .opacity(viewModel.hideUIElements ? 0 : 1) + .accessibility(identifier: "trailingTopView") } } } diff --git a/Sources/StreamVideoSwiftUI/CallView/VisibilityThresholdModifier.swift b/Sources/StreamVideoSwiftUI/CallView/VisibilityThresholdModifier.swift index f9906b529..b0094f5ba 100644 --- a/Sources/StreamVideoSwiftUI/CallView/VisibilityThresholdModifier.swift +++ b/Sources/StreamVideoSwiftUI/CallView/VisibilityThresholdModifier.swift @@ -30,9 +30,10 @@ struct VisibilityThresholdModifier: ViewModifier { /// Closure to handle visibility changes. var changeHandler: (Bool) -> Void - init(in bounds: CGRect, - threshold: CGFloat, - changeHandler: @escaping (Bool) -> Void + init( + in bounds: CGRect, + threshold: CGFloat, + changeHandler: @escaping (Bool) -> Void ) { self.bounds = bounds self.threshold = threshold @@ -72,10 +73,10 @@ struct VisibilityThresholdModifier: ViewModifier { /// Check if the content view is vertically within the parent's bounds. let verticalVisible = (minY + requiredHeight <= bounds.maxY && minY >= bounds.minY) || - (maxY - requiredHeight >= bounds.minY && maxY <= bounds.maxY) + (maxY - requiredHeight >= bounds.minY && maxY <= bounds.maxY) /// Check if the content view is horizontally within the parent's bounds. let horizontalVisible = (minX + requiredWidth <= bounds.maxX && minX >= bounds.minX) || - (maxX - requiredWidth >= bounds.minX && maxX <= bounds.maxX) + (maxX - requiredWidth >= bounds.minX && maxX <= bounds.maxX) return (verticalVisible, horizontalVisible) } diff --git a/Sources/StreamVideoSwiftUI/CallViewModel.swift b/Sources/StreamVideoSwiftUI/CallViewModel.swift index ab8a5da8c..4fc478ad8 100644 --- a/Sources/StreamVideoSwiftUI/CallViewModel.swift +++ b/Sources/StreamVideoSwiftUI/CallViewModel.swift @@ -4,8 +4,8 @@ import Combine import StreamVideo -import SwiftUI import StreamWebRTC +import SwiftUI // View model that provides methods for views that present a call. @MainActor @@ -27,12 +27,12 @@ open class CallViewModel: ObservableObject { .receive(on: RunLoop.main) .sink(receiveValue: { [weak self] blockedUserIds in self?.blockedUsers = blockedUserIds.map { User(id: $0) } - }) + }) recordingUpdates = call?.state.$recordingState .receive(on: RunLoop.main) .sink(receiveValue: { [weak self] newState in self?.recordingState = newState - }) + }) reconnectionUpdates = call?.state.$reconnectionStatus .receive(on: RunLoop.main) .sink(receiveValue: { [weak self] reconnectionStatus in @@ -194,9 +194,9 @@ open class CallViewModel: ObservableObject { ) { self.participantsLayout = participantsLayout self.callSettings = callSettings ?? CallSettings() - self.localCallSettingsChange = callSettings != nil + localCallSettingsChange = callSettings != nil - self.subscribeToCallEvents() + subscribeToCallEvents() pictureInPictureAdapter.onSizeUpdate = { [weak self] in self?.updateTrackSize($0, for: $1) } @@ -205,7 +205,7 @@ open class CallViewModel: ObservableObject { /// Toggles the state of the camera (visible vs non-visible). public func toggleCameraEnabled() { guard let call = call else { - self.callSettings = callSettings.withUpdatedVideoState(!callSettings.videoOn) + callSettings = callSettings.withUpdatedVideoState(!callSettings.videoOn) return } Task { @@ -221,7 +221,7 @@ open class CallViewModel: ObservableObject { /// Toggles the state of the microphone (muted vs unmuted). public func toggleMicrophoneEnabled() { guard let call = call else { - self.callSettings = callSettings.withUpdatedAudioState(!callSettings.audioOn) + callSettings = callSettings.withUpdatedAudioState(!callSettings.audioOn) return } Task { @@ -253,7 +253,7 @@ open class CallViewModel: ObservableObject { /// Enables or disables the audio output. public func toggleAudioOutput() { guard let call = call else { - self.callSettings = callSettings.withUpdatedAudioOutputState(!callSettings.audioOutputOn) + callSettings = callSettings.withUpdatedAudioOutputState(!callSettings.audioOutputOn) return } Task { @@ -273,7 +273,7 @@ open class CallViewModel: ObservableObject { /// Enables or disables the speaker. public func toggleSpeaker() { guard let call = call else { - self.callSettings = callSettings.withUpdatedSpeakerState(!callSettings.speakerOn) + callSettings = callSettings.withUpdatedSpeakerState(!callSettings.speakerOn) return } Task { @@ -335,7 +335,7 @@ open class CallViewModel: ObservableObject { callingState = .lobby(lobbyInfo) if !localCallSettingsChange { Task { - let call = streamVideo.call(callType: callType, callId: callId) + let call = streamVideo.call(callType: callType, callId: callId) let info = try await call.get() self.callSettings = info.settings.toCallSettings } @@ -426,16 +426,16 @@ open class CallViewModel: ObservableObject { /// Updates the participants layout. /// - Parameter participantsLayout: the new participants layout. public func update(participantsLayout: ParticipantsLayout) { - self.automaticLayoutHandling = false + automaticLayoutHandling = false self.participantsLayout = participantsLayout } public func setActiveCall(_ call: Call?) { if let call { - self.callingState = .inCall + callingState = .inCall self.call = call } else { - self.callingState = .idle + callingState = .idle self.call = nil } } @@ -551,7 +551,8 @@ open class CallViewModel: ObservableObject { } else if case let .accepted(callEventInfo) = callEvent { if callingState == .outgoing { enterCall(call: call, callType: callEventInfo.type, callId: callEventInfo.callId, members: []) - } else if case .incoming(_) = callingState, callEventInfo.user?.id == streamVideo.user.id && enteringCallTask == nil { + } else if case .incoming = callingState, + callEventInfo.user?.id == streamVideo.user.id && enteringCallTask == nil { // Accepted on another device. callingState = .idle } @@ -585,12 +586,12 @@ open class CallViewModel: ObservableObject { } private func handleRejectedEvent(_ callEvent: CallEvent) { - if case .rejected(_) = callEvent { - let outgoingMembersCount = outgoingCallMembers.filter({ $0.userId != streamVideo.user.id }).count + if case .rejected = callEvent { + let outgoingMembersCount = outgoingCallMembers.filter { $0.userId != streamVideo.user.id }.count let rejections = call?.state.session?.rejectedBy.count ?? 0 let accepted = call?.state.session?.acceptedBy.count ?? 0 - if accepted == 0, rejections >= outgoingMembersCount { + if accepted == 0, rejections >= outgoingMembersCount { Task { _ = try? await call?.reject() leaveCall() @@ -601,7 +602,7 @@ open class CallViewModel: ObservableObject { private func updateCallStateIfNeeded() { if callingState == .outgoing { - if callParticipants.count > 0 { + if !callParticipants.isEmpty { callingState = .inCall } return diff --git a/Sources/StreamVideoSwiftUI/CallingViews/CallBackgrounds.swift b/Sources/StreamVideoSwiftUI/CallingViews/CallBackgrounds.swift index ea6e0f5ac..828a07a6b 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/CallBackgrounds.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/CallBackgrounds.swift @@ -34,8 +34,8 @@ struct DefaultBackgroundGradient: View { var body: some View { LinearGradient( colors: [ - Color(red: 60/255, green: 64/255, blue: 72/255), - Color(red: 30/255, green: 33/255, blue: 36/255) + Color(red: 60 / 255, green: 64 / 255, blue: 72 / 255), + Color(red: 30 / 255, green: 33 / 255, blue: 36 / 255) ], startPoint: .top, endPoint: .bottom diff --git a/Sources/StreamVideoSwiftUI/CallingViews/CallConnectingView.swift b/Sources/StreamVideoSwiftUI/CallingViews/CallConnectingView.swift index a5ecaf538..c2a38ce2d 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/CallConnectingView.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/CallConnectingView.swift @@ -30,7 +30,7 @@ struct CallConnectingView: View { participants: outgoingCallMembers ) .accessibility(identifier: "callConnectingGroupView") - } else if outgoingCallMembers.count > 0 { + } else if !outgoingCallMembers.isEmpty { AnimatingParticipantView( participant: outgoingCallMembers.first ) diff --git a/Sources/StreamVideoSwiftUI/CallingViews/IncomingCallViewModel.swift b/Sources/StreamVideoSwiftUI/CallingViews/IncomingCallViewModel.swift index 7ac61660d..51802880b 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/IncomingCallViewModel.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/IncomingCallViewModel.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Combine +import Foundation import StreamVideo @MainActor @@ -46,5 +46,4 @@ public class IncomingViewModel: ObservableObject { ringingTimer?.invalidate() ringingTimer = nil } - } diff --git a/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneCheckView.swift b/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneCheckView.swift index 2ba70361f..0780ef0aa 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneCheckView.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneCheckView.swift @@ -128,6 +128,7 @@ struct AudioLevel: Identifiable { var id: String { "\(index)-\(value)" } + let value: Float let index: Int } diff --git a/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneChecker.swift b/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneChecker.swift index d2da13304..62ae0578a 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneChecker.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/MicrophoneChecker.swift @@ -3,9 +3,9 @@ // import AVFoundation +import Combine import Foundation import StreamVideo -import Combine /// Checks the audio capabilities of the device. public class MicrophoneChecker: ObservableObject { @@ -49,7 +49,7 @@ public class MicrophoneChecker: ObservableObject { self.valueLimit = valueLimit self.audioSession = audioSession self.notificationCenter = notificationCenter - self.audioLevels = [Float](repeating: 0.0, count: valueLimit) + audioLevels = [Float](repeating: 0.0, count: valueLimit) setUpAudioCapture() subscribeToCallEnded() @@ -76,7 +76,7 @@ public class MicrophoneChecker: ObservableObject { stopAudioRecorder() } - //MARK: - private + // MARK: - private private func subscribeToCallEnded() { callEndedCancellable = notificationCenter @@ -120,7 +120,7 @@ public class MicrophoneChecker: ObservableObject { newAudioRecorder.record() newAudioRecorder.isMeteringEnabled = true - self.audioRecorder = newAudioRecorder + audioRecorder = newAudioRecorder timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in guard let self = self else { return } newAudioRecorder.updateMeters() diff --git a/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningView.swift b/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningView.swift index 952e2c341..1fb0050ad 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningView.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningView.swift @@ -14,16 +14,16 @@ public struct LobbyView: View { var callId: String var callType: String @Binding var callSettings: CallSettings - var onJoinCallTap: () -> () - var onCloseLobby: () -> () + var onJoinCallTap: () -> Void + var onCloseLobby: () -> Void public init( viewModel: LobbyViewModel? = nil, callId: String, callType: String, callSettings: Binding, - onJoinCallTap: @escaping () -> (), - onCloseLobby: @escaping () -> () + onJoinCallTap: @escaping () -> Void, + onCloseLobby: @escaping () -> Void ) { self.callId = callId self.callType = callType @@ -63,8 +63,8 @@ struct LobbyContentView: View { var callId: String var callType: String @Binding var callSettings: CallSettings - var onJoinCallTap: () -> () - var onCloseLobby: () -> () + var onJoinCallTap: () -> Void + var onCloseLobby: () -> Void var body: some View { VStack { @@ -189,7 +189,7 @@ struct JoinCallView: View { var callId: String var callType: String var callParticipants: [User] - var onJoinCallTap: () -> () + var onJoinCallTap: () -> Void var body: some View { VStack(alignment: .leading, spacing: 16) { @@ -201,7 +201,7 @@ struct JoinCallView: View { .streamAccessibility(value: "\(otherParticipantsCount)") if #available(iOS 14, *) { - if callParticipants.count > 0 { + if !callParticipants.isEmpty { ParticipantsInCallView( callParticipants: callParticipants ) @@ -227,7 +227,7 @@ struct JoinCallView: View { } private var waitingRoomDescription: String { - return "\(L10n.WaitingRoom.description) \(L10n.WaitingRoom.numberOfParticipants(callParticipants.count))" + "\(L10n.WaitingRoom.description) \(L10n.WaitingRoom.numberOfParticipants(callParticipants.count))" } private var otherParticipantsCount: Int { @@ -316,13 +316,15 @@ struct ParticipantsInCallView: View { if let imageURL = participant.user.imageURL { UserAvatar(imageURL: imageURL, size: 40) { CircledTitleView( - title: participant.user.name.isEmpty ? participant.user.id : String(participant.user.name.uppercased().first!), + title: participant.user.name.isEmpty ? participant.user + .id : String(participant.user.name.uppercased().first!), size: 40 ) } } else { CircledTitleView( - title: participant.user.name.isEmpty ? participant.user.id : String(participant.user.name.uppercased().first!), + title: participant.user.name.isEmpty ? participant.user + .id : String(participant.user.name.uppercased().first!), size: 40 ) } diff --git a/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningViewModel.swift b/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningViewModel.swift index d83ec245f..e6587c422 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningViewModel.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/PreJoiningViewModel.swift @@ -19,7 +19,7 @@ public class LobbyViewModel: ObservableObject, @unchecked Sendable { private let call: Call public init(callType: String, callId: String) { - self.call = InjectedValues[\.streamVideo].call( + call = InjectedValues[\.streamVideo].call( callType: callType, callId: callId ) @@ -53,7 +53,7 @@ public class LobbyViewModel: ObservableObject, @unchecked Sendable { public func startCamera(front: Bool) { if #available(iOS 14, *) { Task { - await (camera as? Camera)?.start() + await(camera as? Camera)?.start() if front { (camera as? Camera)?.switchCaptureDevice() } @@ -82,7 +82,7 @@ public class LobbyViewModel: ObservableObject, @unchecked Sendable { Task { let response = try await call.get() withAnimation { - participants = response.session?.participants.map { $0.user.toUser } ?? [] + participants = response.session?.participants.map(\.user.toUser) ?? [] } } } diff --git a/Sources/StreamVideoSwiftUI/CallingViews/iOS13/PreJoiningView_iOS13.swift b/Sources/StreamVideoSwiftUI/CallingViews/iOS13/PreJoiningView_iOS13.swift index 50853ba50..96be89ce4 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/iOS13/PreJoiningView_iOS13.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/iOS13/PreJoiningView_iOS13.swift @@ -15,16 +15,16 @@ struct LobbyView_iOS13: View { var callId: String var callType: String @Binding var callSettings: CallSettings - var onJoinCallTap: () -> () - var onCloseLobby: () -> () + var onJoinCallTap: () -> Void + var onCloseLobby: () -> Void public init( callViewModel: CallViewModel, callId: String, callType: String, callSettings: Binding, - onJoinCallTap: @escaping () -> (), - onCloseLobby: @escaping () -> () + onJoinCallTap: @escaping () -> Void, + onCloseLobby: @escaping () -> Void ) { _callViewModel = ObservedObject(wrappedValue: callViewModel) _viewModel = BackportStateObject( diff --git a/Sources/StreamVideoSwiftUI/CallingViews/iOS13/VideoView_iOS13.swift b/Sources/StreamVideoSwiftUI/CallingViews/iOS13/VideoView_iOS13.swift index 54c5e72d9..56e731102 100644 --- a/Sources/StreamVideoSwiftUI/CallingViews/iOS13/VideoView_iOS13.swift +++ b/Sources/StreamVideoSwiftUI/CallingViews/iOS13/VideoView_iOS13.swift @@ -43,7 +43,7 @@ public struct CallContainer_iOS13: View { } } .alert(isPresented: $viewModel.errorAlertShown, content: { - return Alert.defaultErrorAlert + Alert.defaultErrorAlert }) .overlay(overlayView) .onReceive(viewModel.$callingState) { _ in diff --git a/Sources/StreamVideoSwiftUI/Colors.swift b/Sources/StreamVideoSwiftUI/Colors.swift index 85c3270c1..898312046 100644 --- a/Sources/StreamVideoSwiftUI/Colors.swift +++ b/Sources/StreamVideoSwiftUI/Colors.swift @@ -61,7 +61,7 @@ private extension UIColor { static let streamBlueAlice = mode(0xe9f2ff, 0x00193d) static let streamAccentBlue = mode(0x005fff, 0x005fff) static let streamAccentRed = mode(0xff3742, 0xff3742) - static let streamAccentGreen = mode(0x00E2A1, 0x00E2A1) + static let streamAccentGreen = mode(0x00e2a1, 0x00e2a1) static let streamGrayDisabledText = mode(0x72767e, 0x72767e) static let streamInnerBorder = mode(0xdbdde1, 0x272a30) static let streamHighlight = mode(0xfbf4dd, 0x333024) @@ -71,7 +71,7 @@ private extension UIColor { static let streamCallControlsBackground = mode(0xffffff, 0x1c1e22) static let streamWaitingRoomBackground = mode(0xffffff, 0x2c2c2e) static let streamWaitingRoomSecondaryBackground = mode(0xf2f2f2, 0x1c1c1e) - static let streamParticipantBackground = mode(0x19232D, 0x19232D) + static let streamParticipantBackground = mode(0x19232d, 0x19232d) // Currently we are not using the correct shadow color from figma's color palette. This is to avoid // an issue with snapshots inconsistency between Intel vs M1. We can't use shadows with transparency. diff --git a/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayer.swift b/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayer.swift index 7ef09b00a..2e45de962 100644 --- a/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayer.swift +++ b/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayer.swift @@ -10,7 +10,7 @@ public struct LivestreamPlayer: View { @Injected(\.colors) var colors - var onFullScreenStateChange: ((Bool) -> ())? + var onFullScreenStateChange: ((Bool) -> Void)? @StateObject var state: CallState @StateObject var viewModel: LivestreamPlayerViewModel @@ -20,7 +20,7 @@ public struct LivestreamPlayer: View { id: String, muted: Bool = false, showParticipantCount: Bool = true, - onFullScreenStateChange: ((Bool) -> ())? = nil + onFullScreenStateChange: ((Bool) -> Void)? = nil ) { let viewModel = LivestreamPlayerViewModel( type: type, @@ -131,7 +131,6 @@ struct LiveIndicator: View { .background(colors.primaryButtonBackground) .cornerRadius(8) } - } struct LivestreamPlayPauseButton: View { @@ -139,7 +138,7 @@ struct LivestreamPlayPauseButton: View { @Injected(\.colors) var colors @ObservedObject var viewModel: LivestreamPlayerViewModel - var trackUpdate: () -> () + var trackUpdate: () -> Void var body: some View { Button { @@ -152,7 +151,6 @@ struct LivestreamPlayPauseButton: View { .frame(width: 60) .foregroundColor(colors.livestreamCallControlsColor) } - } } @@ -199,7 +197,7 @@ struct LivestreamButton: View { private let buttonSize: CGFloat = 32 var imageName: String - var action: () -> () + var action: () -> Void var body: some View { Button { @@ -213,6 +211,6 @@ struct LivestreamButton: View { .background(colors.participantInfoBackgroundColor) .cornerRadius(8) } - .padding(.horizontal, 2) + .padding(.horizontal, 2) } } diff --git a/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayerViewModel.swift b/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayerViewModel.swift index 0588f0ac4..72167ac3f 100644 --- a/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayerViewModel.swift +++ b/Sources/StreamVideoSwiftUI/Livestreaming/LivestreamPlayerViewModel.swift @@ -11,15 +11,16 @@ final class LivestreamPlayerViewModel: ObservableObject { @Published private(set) var controlsShown = false { didSet { if controlsShown { - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in guard let self else { return } if !self.streamPaused { self.controlsShown = false } - }) + } } } } + @Published private(set) var streamPaused = false @Published private(set) var loading = false @Published private(set) var muted: Bool @@ -58,7 +59,7 @@ final class LivestreamPlayerViewModel: ObservableObject { } func duration(from state: CallState) -> String? { - guard state.duration > 0 else { return nil } + guard state.duration > 0 else { return nil } return formatter.string(from: state.duration) } diff --git a/Sources/StreamVideoSwiftUI/Models/CallEventsHandler.swift b/Sources/StreamVideoSwiftUI/Models/CallEventsHandler.swift index d569557c0..9aa32c995 100644 --- a/Sources/StreamVideoSwiftUI/Models/CallEventsHandler.swift +++ b/Sources/StreamVideoSwiftUI/Models/CallEventsHandler.swift @@ -11,35 +11,35 @@ public class CallEventsHandler { public func checkForCallEvents(from event: VideoEvent) -> CallEvent? { switch event { - case .typeBlockedUserEvent(let blockedUserEvent): + case let .typeBlockedUserEvent(blockedUserEvent): let callEventInfo = CallEventInfo( callCid: blockedUserEvent.callCid, user: blockedUserEvent.user.toUser, action: .block ) return .userBlocked(callEventInfo) - case .typeCallAcceptedEvent(let callAcceptedEvent): + case let .typeCallAcceptedEvent(callAcceptedEvent): let callEventInfo = CallEventInfo( callCid: callAcceptedEvent.callCid, user: callAcceptedEvent.user.toUser, action: .accept ) return .accepted(callEventInfo) - case .typeCallEndedEvent(let callEndedEvent): + case let .typeCallEndedEvent(callEndedEvent): let callEventInfo = CallEventInfo( callCid: callEndedEvent.callCid, user: callEndedEvent.user?.toUser, action: .end ) return .ended(callEventInfo) - case .typeCallRejectedEvent(let callRejectedEvent): + case let .typeCallRejectedEvent(callRejectedEvent): let callEventInfo = CallEventInfo( callCid: callRejectedEvent.callCid, user: callRejectedEvent.user.toUser, action: .reject ) return .rejected(callEventInfo) - case .typeCallRingEvent(let ringEvent): + case let .typeCallRingEvent(ringEvent): log.debug("Received ring event \(ringEvent)") let cId = ringEvent.callCid let id = cId.components(separatedBy: ":").last ?? cId @@ -53,13 +53,13 @@ public class CallEventsHandler { timeout: TimeInterval(ringEvent.call.settings.ring.autoCancelTimeoutMs / 1000) ) return .incoming(incomingCall) - case .typeCallSessionStartedEvent(let callSessionStartedEvent): + case let .typeCallSessionStartedEvent(callSessionStartedEvent): if let session = callSessionStartedEvent.call.session { return .sessionStarted(session) } else { return nil } - case .typeUnblockedUserEvent(let unblockedUserEvent): + case let .typeUnblockedUserEvent(unblockedUserEvent): let callEventInfo = CallEventInfo( callCid: unblockedUserEvent.callCid, user: unblockedUserEvent.user.toUser, @@ -73,14 +73,14 @@ public class CallEventsHandler { public func checkForParticipantEvents(from event: VideoEvent) -> ParticipantEvent? { switch event { - case .typeCallSessionParticipantJoinedEvent(let event): + case let .typeCallSessionParticipantJoinedEvent(event): return ParticipantEvent( id: event.participant.user.id, action: .join, user: event.participant.user.name ?? event.participant.user.id, imageURL: URL(string: event.participant.user.image ?? "") ) - case .typeCallSessionParticipantLeftEvent(let event): + case let .typeCallSessionParticipantLeftEvent(event): return ParticipantEvent( id: event.participant.user.id, action: .leave, diff --git a/Sources/StreamVideoSwiftUI/Utils/Camera.swift b/Sources/StreamVideoSwiftUI/Utils/Camera.swift index 269744f58..2aa13cb29 100644 --- a/Sources/StreamVideoSwiftUI/Utils/Camera.swift +++ b/Sources/StreamVideoSwiftUI/Utils/Camera.swift @@ -253,7 +253,7 @@ class Camera: NSObject, @unchecked Sendable { } } - func stop() { + func stop() { if captureSession.isRunning { sessionQueue.async { self.captureSession.stopRunning() diff --git a/Sources/StreamVideoSwiftUI/Utils/Formatters/MediaDurationFormatter.swift b/Sources/StreamVideoSwiftUI/Utils/Formatters/MediaDurationFormatter.swift index b72373b2f..aa053d7bc 100644 --- a/Sources/StreamVideoSwiftUI/Utils/Formatters/MediaDurationFormatter.swift +++ b/Sources/StreamVideoSwiftUI/Utils/Formatters/MediaDurationFormatter.swift @@ -31,9 +31,8 @@ open class StreamMediaDurationFormatter: MediaDurationFormatter { open func format(_ time: TimeInterval) -> String? { let dateComponentsFormatter = time < 3600 - ? withoutHoursDateComponentsFormatter - : withHoursDateComponentsFormatter + ? withoutHoursDateComponentsFormatter + : withHoursDateComponentsFormatter return dateComponentsFormatter.string(from: time) } } - diff --git a/Sources/StreamVideoSwiftUI/Utils/HalfSheetView.swift b/Sources/StreamVideoSwiftUI/Utils/HalfSheetView.swift index 06526ee49..08381de4b 100644 --- a/Sources/StreamVideoSwiftUI/Utils/HalfSheetView.swift +++ b/Sources/StreamVideoSwiftUI/Utils/HalfSheetView.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI struct HalfSheetView: View { @Injected(\.colors) var colors @@ -42,7 +42,10 @@ struct DraggableSheetView: View { GeometryReader { proxy in VStack(spacing: 0) { DragHandleView() - .background(Color(colors.callBackground)) // Give the view "volume" so the DragGesture is effective from it's whole width. + .background(Color( + colors + .callBackground + )) // Give the view "volume" so the DragGesture is effective from it's whole width. .padding(.vertical, 5) .padding(.horizontal, 24) // Avoid collision with rounded corners. .gesture( @@ -99,8 +102,8 @@ extension View { isPresented: Binding, onDismiss: (() -> Void)? = nil, @ViewBuilder content: @escaping () -> Content - ) -> some View where Content : View { - if #available (iOS 16.0, *) { + ) -> some View where Content: View { + if #available(iOS 16.0, *) { sheet(isPresented: isPresented, onDismiss: onDismiss) { content() .padding(.vertical) diff --git a/Sources/StreamVideoSwiftUI/Utils/LazyImageExtensions.swift b/Sources/StreamVideoSwiftUI/Utils/LazyImageExtensions.swift index 7b8cc69e4..161c29ee3 100644 --- a/Sources/StreamVideoSwiftUI/Utils/LazyImageExtensions.swift +++ b/Sources/StreamVideoSwiftUI/Utils/LazyImageExtensions.swift @@ -44,20 +44,20 @@ public struct StreamLazyImage: View { if failedToLoadContent, let failback { failback() } else { -#if STREAM_SNAPSHOT_TESTS + #if STREAM_SNAPSHOT_TESTS if let imageURL = imageURL, imageURL.isFileURL, - let image = UIImage(contentsOfFile: imageURL.path) { + let image = UIImage(contentsOfFile: imageURL.path) { NukeImage(image) .aspectRatio(contentMode: .fill) } else { LazyImage(imageURL: imageURL) .onFailure { _ in self.failedToLoadContent = true } } -#else + #else LazyImage(imageURL: imageURL) .onFailure { _ in self.failedToLoadContent = true } -#endif + #endif } } } diff --git a/Sources/StreamVideoSwiftUI/Utils/ModifiedContent.swift b/Sources/StreamVideoSwiftUI/Utils/ModifiedContent.swift index dee5d4954..e2472be72 100644 --- a/Sources/StreamVideoSwiftUI/Utils/ModifiedContent.swift +++ b/Sources/StreamVideoSwiftUI/Utils/ModifiedContent.swift @@ -8,7 +8,7 @@ extension View { public func streamAccessibility(value: String) -> some View { #if DEBUG - return self.accessibility(value: Text(value)) + return accessibility(value: Text(value)) #else return self #endif diff --git a/Sources/StreamVideoSwiftUI/Utils/Modifiers.swift b/Sources/StreamVideoSwiftUI/Utils/Modifiers.swift index 439933ba7..4c4e6e53c 100644 --- a/Sources/StreamVideoSwiftUI/Utils/Modifiers.swift +++ b/Sources/StreamVideoSwiftUI/Utils/Modifiers.swift @@ -101,6 +101,6 @@ struct DeviceRotationViewModifier: ViewModifier { extension View { public func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View { - self.modifier(DeviceRotationViewModifier(action: action)) + modifier(DeviceRotationViewModifier(action: action)) } } diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamAVPictureInPictureVideoCallViewController.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamAVPictureInPictureVideoCallViewController.swift index dfa543909..25d8fb20d 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamAVPictureInPictureVideoCallViewController.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamAVPictureInPictureVideoCallViewController.swift @@ -26,7 +26,8 @@ protocol StreamAVPictureInPictureViewControlling { } @available(iOS 15.0, *) -final class StreamAVPictureInPictureVideoCallViewController: AVPictureInPictureVideoCallViewController, StreamAVPictureInPictureViewControlling { +final class StreamAVPictureInPictureVideoCallViewController: AVPictureInPictureVideoCallViewController, + StreamAVPictureInPictureViewControlling { private let contentView: StreamPictureInPictureVideoRenderer = .init() diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamBufferTransformer.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamBufferTransformer.swift index 4331c549e..11571c29c 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamBufferTransformer.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamBufferTransformer.swift @@ -43,7 +43,7 @@ struct StreamBufferTransformer { ) -> CMSampleBuffer? { var sampleBuffer: CMSampleBuffer? - var timimgInfo = CMSampleTimingInfo() + var timimgInfo = CMSampleTimingInfo() var formatDescription: CMFormatDescription? CMVideoFormatDescriptionCreateForImageBuffer( allocator: kCFAllocatorDefault, @@ -182,7 +182,7 @@ struct StreamBufferTransformer { let vValue = Int(vPlane[vOffset]) - 128 let index = (y * bgraBytesPerRow) + (x * 4) - var pixel: [UInt8] = [0, 0, 0, 255] // BGRA format, fully opaque + var pixel: [UInt8] = [0, 0, 0, 255] // BGRA format, fully opaque // Perform YUV to RGB conversion with chroma upsampling let c = yValue - 16 @@ -209,6 +209,6 @@ struct StreamBufferTransformer { } private func clamp(_ value: Int) -> UInt8 { - return UInt8(max(0, min(255, value))) + UInt8(max(0, min(255, value))) } } diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPIctureView.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPIctureView.swift index e44eff398..890a3e58a 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPIctureView.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPIctureView.swift @@ -3,8 +3,8 @@ // import Foundation -import SwiftUI import StreamVideo +import SwiftUI /// A view that can be used as the sourceView for picture-in-picture. This is quite useful as PiP can become /// very weird if the sourceView isn't in the ViewHierarchy or doesn't have an appropriate size. @@ -64,6 +64,6 @@ extension View { /// - Note:The View itself won't be used as sourceView. @ViewBuilder public func enablePictureInPicture(_ isActive: Bool) -> some View { - self.modifier(PictureInPictureModifier(isActive: isActive)) + modifier(PictureInPictureModifier(isActive: isActive)) } } diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureAdapter.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureAdapter.swift index 1657818a2..f603971fc 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureAdapter.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureAdapter.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import Combine +import Foundation import StreamVideo import UIKit @@ -72,7 +72,8 @@ final class StreamPictureInPictureAdapter { let sessionId = call?.state.sessionId let otherParticipants = participants.filter { $0.sessionId != sessionId } - if let session = call?.state.screenSharingSession, call?.state.isCurrentUserScreensharing == false, let track = session.track { + if let session = call?.state.screenSharingSession, call?.state.isCurrentUserScreensharing == false, + let track = session.track { pictureInPictureController?.track = track activeParticipant = nil } else if let participant = otherParticipants.first(where: { $0.track != nil }), let track = participant.track { diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureController.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureController.swift index 8138be4b5..2ae66ab1d 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureController.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureController.swift @@ -2,11 +2,11 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation import AVKit -import StreamWebRTC -import StreamVideo import Combine +import Foundation +import StreamVideo +import StreamWebRTC /// A controller class for picture-in-picture whenever that is possible. final class StreamPictureInPictureController: NSObject, AVPictureInPictureControllerDelegate { @@ -48,20 +48,19 @@ final class StreamPictureInPictureController: NSObject, AVPictureInPictureContro /// A set of `AnyCancellable` objects used to manage subscriptions. private var cancellableBag: Set = [] - /// A `AnyCancellable` object used to ensure that the active track is enabled while in picture-in-picture + /// A `AnyCancellable` object used to ensure that the active track is enabled while in picture-in-picture /// mode. private var ensureActiveTrackIsEnabledCancellable: AnyCancellable? - /// A `StreamPictureInPictureTrackStateAdapter` object that manages the state of the + /// A `StreamPictureInPictureTrackStateAdapter` object that manages the state of the /// active track. private let trackStateAdapter: StreamPictureInPictureTrackStateAdapter = .init() - // MARK: - Lifecycle /// Initializes the controller and creates the content view /// - /// - Parameter canStartPictureInPictureAutomaticallyFromInline A boolean value + /// - Parameter canStartPictureInPictureAutomaticallyFromInline A boolean value /// indicating whether the picture-in-picture session should start automatically when the app enters /// background. /// @@ -146,11 +145,12 @@ final class StreamPictureInPictureController: NSObject, AVPictureInPictureContro pictureInPictureController? .publisher(for: \.isPictureInPictureActive) .removeDuplicates() - .sink { [weak self] in self?.didUpdatePictureInPictureActiveState($0) } + .sink { [weak self] in self?.didUpdatePictureInPictureActiveState($0) } .store(in: &cancellableBag) } else { // If picture-in-picture is active, simply update the sourceView. - if #available(iOS 15.0, *), let contentViewController = contentViewController as? StreamAVPictureInPictureVideoCallViewController { + if #available(iOS 15.0, *), + let contentViewController = contentViewController as? StreamAVPictureInPictureVideoCallViewController { pictureInPictureController?.contentSource = .init( activeVideoCallSourceView: sourceView, contentViewController: contentViewController @@ -165,16 +165,19 @@ final class StreamPictureInPictureController: NSObject, AVPictureInPictureContro } private func makePictureInPictureController(with sourceView: UIView) { - if #available(iOS 15.0, *), let contentViewController = contentViewController as? StreamAVPictureInPictureVideoCallViewController { + if #available(iOS 15.0, *), + let contentViewController = contentViewController as? StreamAVPictureInPictureVideoCallViewController { pictureInPictureController = .init( contentSource: .init( activeVideoCallSourceView: sourceView, contentViewController: contentViewController - )) + ) + ) } if #available(iOS 14.2, *) { - pictureInPictureController?.canStartPictureInPictureAutomaticallyFromInline = canStartPictureInPictureAutomaticallyFromInline + pictureInPictureController? + .canStartPictureInPictureAutomaticallyFromInline = canStartPictureInPictureAutomaticallyFromInline } pictureInPictureController?.delegate = self diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureTrackStateAdapter.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureTrackStateAdapter.swift index 6cdb405bd..f9a86d8d3 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureTrackStateAdapter.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureTrackStateAdapter.swift @@ -2,10 +2,10 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import Foundation -import StreamWebRTC import Combine +import Foundation import StreamVideo +import StreamWebRTC /// StreamPictureInPictureTrackStateAdapter serves as an adapter for managing the state of a video track /// used for picture-in-picture functionality. It can enable or disable observers based on its isEnabled property @@ -16,7 +16,7 @@ final class StreamPictureInPictureTrackStateAdapter { var isEnabled: Bool = false { didSet { /// When the 'isEnabled' property changes, this didSet observer is called. - /// It checks if the new value is different from the old value, and if so, + /// It checks if the new value is different from the old value, and if so, /// it calls the 'enableObserver' function. guard isEnabled != oldValue else { return } enableObserver(isEnabled) diff --git a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureVideoRenderer.swift b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureVideoRenderer.swift index 1f1cf406f..33d99e483 100644 --- a/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureVideoRenderer.swift +++ b/Sources/StreamVideoSwiftUI/Utils/PictureInPicture/StreamPictureInPictureVideoRenderer.swift @@ -2,10 +2,10 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import StreamWebRTC -import Foundation import Combine +import Foundation import StreamVideo +import StreamWebRTC /// A view that can be used to render an instance of `RTCVideoTrack` final class StreamPictureInPictureVideoRenderer: UIView, RTCVideoRenderer { @@ -52,7 +52,7 @@ final class StreamPictureInPictureVideoRenderer: UIView, RTCVideoRenderer { /// The track's size. private var trackSize: CGSize = .zero { didSet { - guard trackSize != oldValue else { return } + guard trackSize != oldValue else { return } didUpdateTrackSize() } } @@ -129,14 +129,12 @@ final class StreamPictureInPictureVideoRenderer: UIView, RTCVideoRenderer { if let pixelBuffer = frame.buffer as? RTCCVPixelBuffer, - let sampleBuffer = bufferTransformer.transform(pixelBuffer.pixelBuffer) - { + let sampleBuffer = bufferTransformer.transform(pixelBuffer.pixelBuffer) { bufferPublisher.send(sampleBuffer) } else if let i420buffer = frame.buffer as? RTCI420Buffer, let transformedBuffer = bufferTransformer.transform(i420buffer, targetSize: contentSize), - let sampleBuffer = bufferTransformer.transform(transformedBuffer) - { + let sampleBuffer = bufferTransformer.transform(transformedBuffer) { bufferPublisher.send(sampleBuffer) } else { log.warning("Failed to convert \(type(of: frame.buffer)) CMSampleBuffer.") @@ -179,7 +177,6 @@ final class StreamPictureInPictureVideoRenderer: UIView, RTCVideoRenderer { contentView.sampleBufferDisplayLayer.enqueue(buffer) } } - } /// A method used to start consuming frames from the track. @@ -214,7 +211,10 @@ final class StreamPictureInPictureVideoRenderer: UIView, RTCVideoRenderer { requiresResize = widthDiffRatio >= sizeRatioThreshold || heightDiffRatio >= sizeRatioThreshold framesToSkip = requiresResize ? max(Int(min(Int(widthDiffRatio), Int(heightDiffRatio)) / 2), 1) : 1 framesSkipped = 0 - log.debug("contentSize:\(contentSize), trackId:\(track?.trackId ?? "n/a") trackSize:\(trackSize) requiresResize:\(requiresResize) framesToSkip:\(framesToSkip) framesSkipped:\(framesSkipped)") + log + .debug( + "contentSize:\(contentSize), trackId:\(track?.trackId ?? "n/a") trackSize:\(trackSize) requiresResize:\(requiresResize) framesToSkip:\(framesToSkip) framesSkipped:\(framesSkipped)" + ) } /// A method used to handle the frameSkipping(step) during frame consumption. diff --git a/Sources/StreamVideoSwiftUI/Utils/ToastView.swift b/Sources/StreamVideoSwiftUI/Utils/ToastView.swift index 13e9f275a..39893f066 100644 --- a/Sources/StreamVideoSwiftUI/Utils/ToastView.swift +++ b/Sources/StreamVideoSwiftUI/Utils/ToastView.swift @@ -65,7 +65,7 @@ public struct ToastModifier: ViewModifier { } .animation(.spring(), value: toast) ) - .onChange(of: toast) { value in + .onChange(of: toast) { _ in showToast() } } @@ -120,6 +120,6 @@ public struct ToastModifier: ViewModifier { @available(iOS 14.0, *) extension View { public func toastView(toast: Binding) -> some View { - self.modifier(ToastModifier(toast: toast)) + modifier(ToastModifier(toast: toast)) } } diff --git a/Sources/StreamVideoSwiftUI/Utils/ViewExtensions.swift b/Sources/StreamVideoSwiftUI/Utils/ViewExtensions.swift index d55e63375..ddc7d6d68 100644 --- a/Sources/StreamVideoSwiftUI/Utils/ViewExtensions.swift +++ b/Sources/StreamVideoSwiftUI/Utils/ViewExtensions.swift @@ -2,8 +2,8 @@ // Copyright © 2024 Stream.io Inc. All rights reserved. // -import SwiftUI import Combine +import SwiftUI extension Alert { public static var defaultErrorAlert: Alert { @@ -21,9 +21,9 @@ extension View { func onReceive

( _ publisher: P?, perform action: @escaping (P.Output) -> Void - ) -> some View where P : Publisher, P.Failure == Never { + ) -> some View where P: Publisher, P.Failure == Never { if let publisher = publisher { - self.onReceive(publisher, perform: action) + onReceive(publisher, perform: action) } else { self } diff --git a/Sources/StreamVideoSwiftUI/ViewFactory.swift b/Sources/StreamVideoSwiftUI/ViewFactory.swift index 0c7298f48..b9bb6c839 100644 --- a/Sources/StreamVideoSwiftUI/ViewFactory.swift +++ b/Sources/StreamVideoSwiftUI/ViewFactory.swift @@ -274,7 +274,7 @@ extension ViewFactory { callSettings: Binding ) -> some View { let handleJoinCall = { - if case .lobby(_) = viewModel.callingState { + if case .lobby = viewModel.callingState { viewModel.startCall( callType: lobbyInfo.callType, callId: lobbyInfo.callId, diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f93d12615..4b852c5f6 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -22,6 +22,7 @@ reversed_gci = gci.split('.').reverse.join('.') is_localhost = !is_ci @force_check = false develop_branch = 'main' +swiftformat_excluded_paths = ["**/Generated", "**/generated", "**/protobuf", "**/OpenApi", "**/Nuke*"] before_all do |lane| if is_ci @@ -581,11 +582,11 @@ lane :rubocop do sh('bundle exec rubocop') end -desc 'Run source code linting' -lane :run_linter do +desc 'Run source code formatting/linting' +lane :run_swift_format do |options| Dir.chdir('..') do - UI.error('SwiftFormat lint was skipped. Check out https://github.com/GetStream/ios-issues-tracking/issues/465 for more info') - # sh('mint run swiftformat --lint --config .swiftformat Sources --exclude **/Generated, **/generated, **/protobuf, **/OpenApi, Sources/StreamChatSwiftUI/StreamNuke') + action = options[:lint] ? "--lint" : "" + sh("mint run swiftformat #{action} --config .swiftformat --exclude #{swiftformat_excluded_paths.join(',')} Sources") end end diff --git a/hooks/pre-commit.sh b/hooks/pre-commit.sh index 7a693aac0..01292ab72 100755 --- a/hooks/pre-commit.sh +++ b/hooks/pre-commit.sh @@ -1,3 +1,3 @@ #!/bin/bash - +mint run swiftformat --lint --config .swiftformat --exclude "**/Generated","**/generated","**/protobuf","**/OpenApi","**/Nuke*" Sources \ No newline at end of file