diff --git a/.github/workflows/sdk-size-metrics.yml b/.github/workflows/sdk-size-metrics.yml index a05407b1c..e71245a9a 100644 --- a/.github/workflows/sdk-size-metrics.yml +++ b/.github/workflows/sdk-size-metrics.yml @@ -18,6 +18,7 @@ jobs: runs-on: macos-15 env: GITHUB_TOKEN: '${{ secrets.CI_BOT_GITHUB_TOKEN }}' + GITHUB_PR_NUM: ${{ github.event.pull_request.number }} steps: - name: Connect Bot uses: webfactory/ssh-agent@v0.7.0 @@ -28,10 +29,13 @@ jobs: - uses: ./.github/actions/bootstrap - - name: Run SDK Size Metrics + - name: Run General SDK Size Metrics run: bundle exec fastlane show_frameworks_sizes timeout-minutes: 30 env: - GITHUB_PR_NUM: ${{ github.event.pull_request.number }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} + + - name: Run Detailed SDK Size Metrics + run: bundle exec fastlane size_analyze + timeout-minutes: 30 diff --git a/.gitignore b/.gitignore index 1759eff5c..8bd3c2a36 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ App Thinning Size Report.txt app-thinning.plist *.dmg yeetd-normal.pkg +*LinkMap.txt # Stream Video Buddy video-buddy-server.log diff --git a/.spi.yml b/.spi.yml index e1ffb7f96..ee9870e66 100644 --- a/.spi.yml +++ b/.spi.yml @@ -3,5 +3,6 @@ builder: configs: - platform: ios documentation_targets: [StreamVideoSwiftUI, StreamVideoUIKit, StreamVideo] - scheme: StreamChatSwiftUI + scheme: StreamVideoSwiftUI + swift_version: '6.1' diff --git a/CHANGELOG.md b/CHANGELOG.md index ebed385ae..ad27ad0b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). # Upcoming +### 🔄 Changed +- Update WebRTC to 137.0.42 which brings performance improvements on video rendering. [#983](https://github.com/GetStream/stream-video-swift/pull/983) + +# [1.34.2](https://github.com/GetStream/stream-video-swift/releases/tag/1.34.2) +_October 24, 2025_ + ### 🔄 Changed # [1.34.1](https://github.com/GetStream/stream-video-swift/releases/tag/1.34.1) diff --git a/DemoApp/Sources/Views/StatsView/DemoStatsAdapter.swift b/DemoApp/Sources/Views/StatsView/DemoStatsAdapter.swift index 7a0e16900..bcac9ac82 100644 --- a/DemoApp/Sources/Views/StatsView/DemoStatsAdapter.swift +++ b/DemoApp/Sources/Views/StatsView/DemoStatsAdapter.swift @@ -29,8 +29,15 @@ final class DemoStatsAdapter { callStatsReportCancellable = call? .state .$statsReport + .receive(on: DispatchQueue.main) .sink { [weak self] in - if let report = $0 { self?.reports.append(report) } + guard let self, let report = $0 else { return } + + reports.append(report) + + if reports.endIndex > 20 { + _ = reports.dropFirst() + } } } } diff --git a/Gemfile.lock b/Gemfile.lock index 060b7df36..0f12880b9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -211,9 +211,11 @@ GEM bundler fastlane pry - fastlane-plugin-stream_actions (0.3.90) + fastlane-plugin-stream_actions (0.3.101) xctest_list (= 1.2.1) fastlane-plugin-versioning (0.7.1) + fastlane-plugin-xcsize (1.1.0) + xcsize (= 1.1.0) fastlane-sirp (1.0.0) sysrandom (~> 1.0) ffi (1.17.2) @@ -416,6 +418,8 @@ GEM rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) + xcsize (1.1.0) + commander (>= 4.6, < 6.0) xctest_list (1.2.1) PLATFORMS @@ -429,8 +433,9 @@ DEPENDENCIES fastlane fastlane-plugin-create_xcframework fastlane-plugin-lizard - fastlane-plugin-stream_actions (= 0.3.90) + fastlane-plugin-stream_actions (= 0.3.101) fastlane-plugin-versioning + fastlane-plugin-xcsize (= 1.1.0) json lefthook plist diff --git a/Package.swift b/Package.swift index 5abe14b34..60b210cb4 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-protobuf.git", exact: "1.30.0"), - .package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "137.0.41") + .package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "137.0.42") ], targets: [ .target( diff --git a/README.md b/README.md index 980274df9..d827f3a96 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- StreamVideo + StreamVideo StreamVideoSwiftUI StreamVideoUIKit StreamWebRTC @@ -247,10 +247,27 @@ Video roadmap and changelog is available [here](https://github.com/GetStream/pro - [x] Better audio support for livestreams ### 1.5.0 milestone - June -- [] Livestreaming support on 1080p @ 60fps -- [] Distinguish between RTMP and regular tracks -- [] Waiting rooms -- [] Improve visibility on join call issues +- [x] Livestreaming support on 1080p @ 60fps +- [x] Improve visibility on join call issues +- [x] Stats V2 +- [x] Configure policies based on the the device's proximity information + +### 1.6.0 milestone - September + +- [x] Pausing video on low bandwidths +- [x] Distinguish between RTMP and regular tracks +- [x] WHIP +- [x] SRT ingress +- [x] Python RTC SDK (A/V AI integrations) +- [x] TURN/STUN on TCP/UDP 443 and static IP ranges + +### 1.7.0 milestone - December + +- [] Hifi Audio +- [] Screensharing Audio +- [] Battery usage improvements +- [] Swift 6.2 support +- [] RTMP streaming from device ### After 1.5.0 diff --git a/Sources/StreamVideo/Generated/SystemEnvironment+Version.swift b/Sources/StreamVideo/Generated/SystemEnvironment+Version.swift index 16f69e86f..b952f1086 100644 --- a/Sources/StreamVideo/Generated/SystemEnvironment+Version.swift +++ b/Sources/StreamVideo/Generated/SystemEnvironment+Version.swift @@ -7,7 +7,7 @@ import Foundation extension SystemEnvironment { /// A Stream Video version. - public static let version: String = "1.34.1" + public static let version: String = "1.34.2" /// The WebRTC version. - public static let webRTCVersion: String = "137.0.41" + public static let webRTCVersion: String = "137.0.42" } diff --git a/Sources/StreamVideo/Info.plist b/Sources/StreamVideo/Info.plist index 2a735cd3a..4c36b4383 100644 --- a/Sources/StreamVideo/Info.plist +++ b/Sources/StreamVideo/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.34.1 + 1.34.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright diff --git a/Sources/StreamVideo/StreamVideo.swift b/Sources/StreamVideo/StreamVideo.swift index c6f072cd2..924da535c 100644 --- a/Sources/StreamVideo/StreamVideo.swift +++ b/Sources/StreamVideo/StreamVideo.swift @@ -17,6 +17,7 @@ public class StreamVideo: ObservableObject, @unchecked Sendable { @Injected(\.callCache) private var callCache @Injected(\.screenProperties) private var screenProperties @Injected(\.audioStore) private var audioStore + @Injected(\.battery) private var battery private enum DisposableKey: String { case ringEventReceived } @@ -203,6 +204,7 @@ public class StreamVideo: ObservableObject, @unchecked Sendable { // Warm up _ = eventNotificationCenter _ = idleTimerAdapter + _ = battery if user.type != .anonymous { let userAuth = UserAuth { [weak self] in diff --git a/Sources/StreamVideo/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore+DefaultReducer.swift b/Sources/StreamVideo/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore+DefaultReducer.swift index 68d36c23f..df7dc798a 100644 --- a/Sources/StreamVideo/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore+DefaultReducer.swift +++ b/Sources/StreamVideo/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore+DefaultReducer.swift @@ -17,7 +17,7 @@ extension AudioProcessingStore.Namespace { file: StaticString, function: StaticString, line: UInt - ) throws -> AudioProcessingStore.Namespace.StoreState { + ) async throws -> AudioProcessingStore.Namespace.StoreState { var updatedState = state switch action { diff --git a/Sources/StreamVideo/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder+DefaultReducer.swift b/Sources/StreamVideo/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder+DefaultReducer.swift index 7900fd0b2..ddeb667e4 100644 --- a/Sources/StreamVideo/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder+DefaultReducer.swift +++ b/Sources/StreamVideo/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder+DefaultReducer.swift @@ -47,7 +47,7 @@ extension StreamCallAudioRecorder.Namespace { file: StaticString, function: StaticString, line: UInt - ) throws -> State { + ) async throws -> State { var updatedState = state switch action { diff --git a/Sources/StreamVideo/Utils/Battery/BatteryStore.swift b/Sources/StreamVideo/Utils/Battery/BatteryStore.swift new file mode 100644 index 000000000..dc59aac65 --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/BatteryStore.swift @@ -0,0 +1,123 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Combine +import Foundation +#if canImport(UIKit) +import UIKit +#endif + +/// Monitors the device battery state and exposes the latest readings through +/// the shared store pipeline. +final class BatteryStore: CustomStringConvertible, @unchecked Sendable { + + var state: Namespace.State { store.state } + + var description: String { + var result = "Battery {" + result += " isMonitoring:\(store.state.isMonitoringEnabled)" + result += " state:\(store.state.state)" + result += " level:\(store.state.level)" + result += " }" + return result + } + + private let store: Store + private let disposableBag = DisposableBag() + + init( + store: Store = Namespace.store( + initialState: .init( + isMonitoringEnabled: false, + state: .unknown, + level: 0 + ) + ) + ) { + self.store = store + + self.store.dispatch(.setMonitoringEnabled(true)) + + #if canImport(UIKit) + self.store + .publisher(\.isMonitoringEnabled) + .filter { $0 } + .removeDuplicates() + .receive(on: DispatchQueue.main) + .map { _ in + MainActor.assumeIsolated { + ( + UIDevice.current.batteryState, + UIDevice.current.batteryLevel + ) + } + } + .sink { [weak self] stateAndLevel in + let (state, level) = stateAndLevel + self?.store.dispatch([ + .setState(.init(state)), + .setLevel(level) + ]) + } + .store(in: disposableBag) + #endif + } + + func publisher( + _ keyPath: KeyPath + ) -> AnyPublisher { + store.publisher(keyPath) + } + + func dispatch( + _ actions: [Namespace.Action], + file: StaticString = #file, + function: StaticString = #function, + line: UInt = #line + ) -> StoreTask { + store.dispatch( + actions, + file: file, + function: function, + line: line + ) + } +} + +extension BatteryStore: Encodable { + private enum CodingKeys: String, CodingKey { + case isMonitoringEnabled + case state + case level + } + + /// Encodes a snapshot of the store's observable state. + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + let state = store.state + try container.encode( + state.isMonitoringEnabled, + forKey: .isMonitoringEnabled + ) + try container.encode(state.state, forKey: .state) + try container.encode(state.level, forKey: .level) + } +} + +extension BatteryStore: InjectionKey { + /// The default recorder instance used when no custom recorder is + /// provided. + nonisolated(unsafe) static var currentValue: BatteryStore = .init() +} + +extension InjectedValues { + var battery: BatteryStore { + get { + Self[BatteryStore.self] + } + set { + Self[BatteryStore.self] = newValue + } + } +} diff --git a/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Action.swift b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Action.swift new file mode 100644 index 000000000..b4086707b --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Action.swift @@ -0,0 +1,17 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation + +extension BatteryStore.Namespace { + /// Actions that drive battery monitoring state transitions. + /// + /// These actions mirror updates from `UIDevice` and user controlled + /// monitoring preferences. + enum StoreAction: Sendable, Equatable, StoreActionBoxProtocol { + case setMonitoringEnabled(Bool) + case setLevel(Float) + case setState(StoreState.BatteryState) + } +} diff --git a/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Namespace.swift b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Namespace.swift new file mode 100644 index 000000000..c69a72542 --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+Namespace.swift @@ -0,0 +1,35 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation + +extension BatteryStore { + + /// Namespace configuration for the battery monitoring store. + enum Namespace: StoreNamespace { + + /// The state type for this store namespace. + typealias State = StoreState + + /// The action type for this store namespace. + typealias Action = StoreAction + + /// Unique identifier for this store instance. + /// + /// Used for logging and debugging purposes. + static let identifier: String = "battery.store" + + static func reducers() -> [Reducer] { + [ + DefaultReducer() + ] + } + + static func middleware() -> [Middleware] { + [ + ObservationMiddleware() + ] + } + } +} diff --git a/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+State.swift b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+State.swift new file mode 100644 index 000000000..65efdd740 --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/Namespace/BatteryStore+State.swift @@ -0,0 +1,78 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation +#if canImport(UIKit) +import UIKit +#endif + +extension BatteryStore.Namespace { + + /// Snapshot of the battery monitoring configuration and readings. + struct StoreState: Equatable { + var isMonitoringEnabled: Bool + var state: BatteryState + var level: Int + } +} + +extension BatteryStore.Namespace.StoreState { + + /// Represents the high-level battery state emitted by `UIDevice`. + enum BatteryState: CustomStringConvertible { + case unknown + case unplugged + case charging + case full + + var description: String { + switch self { + case .unknown: + return ".unknown" + case .unplugged: + return ".unplugged" + case .charging: + return ".charging" + case .full: + return ".full" + } + } + + #if canImport(UIKit) + /// Creates a battery state from the UIKit battery representation. + init(_ rawValue: UIDevice.BatteryState) { + switch rawValue { + case .unknown: + self = .unknown + case .unplugged: + self = .unplugged + case .charging: + self = .charging + case .full: + self = .full + @unknown default: + self = .unknown + } + } + #endif + } +} + +extension BatteryStore.Namespace.StoreState: Encodable {} + +extension BatteryStore.Namespace.StoreState.BatteryState: Encodable { + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .unknown: + try container.encode("unknown") + case .unplugged: + try container.encode("unplugged") + case .charging: + try container.encode("charging") + case .full: + try container.encode("full") + } + } +} diff --git a/Sources/StreamVideo/Utils/Battery/Namespace/Middleware/BatteryStore+ObservationMiddleware.swift b/Sources/StreamVideo/Utils/Battery/Namespace/Middleware/BatteryStore+ObservationMiddleware.swift new file mode 100644 index 000000000..37f43c9a2 --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/Namespace/Middleware/BatteryStore+ObservationMiddleware.swift @@ -0,0 +1,41 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Combine +import Foundation +#if canImport(UIKit) +import UIKit +#endif + +extension BatteryStore.Namespace { + + /// Observes UIKit battery notifications and forwards updates to the store. + final class ObservationMiddleware: Middleware, @unchecked Sendable { + + private let disposableBag = DisposableBag() + + /// Initializes the middleware and sets up interruption monitoring. + override init() { + super.init() + + #if canImport(UIKit) + NotificationCenter + .default + .publisher(for: UIDevice.batteryStateDidChangeNotification) + .receive(on: DispatchQueue.main) + .map { _ in MainActor.assumeIsolated { UIDevice.current.batteryState } } + .sink { [weak self] in self?.dispatcher?.dispatch(.setState(.init($0))) } + .store(in: disposableBag) + + NotificationCenter + .default + .publisher(for: UIDevice.batteryLevelDidChangeNotification) + .receive(on: DispatchQueue.main) + .map { _ in MainActor.assumeIsolated { UIDevice.current.batteryLevel } } + .sink { [weak self] in self?.dispatcher?.dispatch(.setLevel($0)) } + .store(in: disposableBag) + #endif + } + } +} diff --git a/Sources/StreamVideo/Utils/Battery/Namespace/Reducers/BatteryStore+DefaultReducer.swift b/Sources/StreamVideo/Utils/Battery/Namespace/Reducers/BatteryStore+DefaultReducer.swift new file mode 100644 index 000000000..d7d9f0d81 --- /dev/null +++ b/Sources/StreamVideo/Utils/Battery/Namespace/Reducers/BatteryStore+DefaultReducer.swift @@ -0,0 +1,63 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation +#if canImport(UIKit) +import UIKit +#endif + +extension BatteryStore.Namespace { + + final class DefaultReducer: Reducer, @unchecked Sendable { + /// Processes an action to produce a new state. + /// + /// This method creates a copy of the current state, applies the + /// action's changes, and returns the updated state. + /// + /// - Parameters: + /// - state: The current state before the action. + /// - action: The action to process. + /// - file: Source file where the action was dispatched. + /// - function: Function name where the action was dispatched. + /// - line: Line number where the action was dispatched. + /// + /// - Returns: A new state reflecting the action's changes. + /// + /// - Throws: This implementation doesn't throw, but the protocol + /// allows for error handling in complex reducers. + override func reduce( + state: State, + action: Action, + file: StaticString, + function: StaticString, + line: UInt + ) async throws -> State { + var updatedState = state + + switch action { + case let .setMonitoringEnabled(value): + #if canImport(UIKit) + await MainActor.run { UIDevice.current.isBatteryMonitoringEnabled = value } + updatedState.isMonitoringEnabled = value + #else + break + #endif + + case let .setLevel(value): + if value >= 0 { + let percentage = Double(value) * 100 + let rounded = percentage.rounded() + updatedState.level = Int(max(0, min(rounded, 100))) + } else { + updatedState.level = 0 + } + + case let .setState(value): + updatedState.state = value + } + + return updatedState + } + } +} diff --git a/Sources/StreamVideo/Utils/Extensions/Foundation/Sequence+AsyncReduce.swift b/Sources/StreamVideo/Utils/Extensions/Foundation/Sequence+AsyncReduce.swift new file mode 100644 index 000000000..40f7d777e --- /dev/null +++ b/Sources/StreamVideo/Utils/Extensions/Foundation/Sequence+AsyncReduce.swift @@ -0,0 +1,19 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation + +extension Sequence { + + func asyncReduce( + _ initialResult: Result, + _ nextPartialResult: ((Result, Element) async throws -> Result) + ) async rethrows -> Result { + var result = initialResult + for element in self { + result = try await nextPartialResult(result, element) + } + return result + } +} diff --git a/Sources/StreamVideo/Utils/PermissionsStore/Namespace/Reducers/PermissionStore+DefaultReducer.swift b/Sources/StreamVideo/Utils/PermissionsStore/Namespace/Reducers/PermissionStore+DefaultReducer.swift index 12801a100..9e8739f44 100644 --- a/Sources/StreamVideo/Utils/PermissionsStore/Namespace/Reducers/PermissionStore+DefaultReducer.swift +++ b/Sources/StreamVideo/Utils/PermissionsStore/Namespace/Reducers/PermissionStore+DefaultReducer.swift @@ -16,7 +16,7 @@ extension PermissionStore { file: StaticString, function: StaticString, line: UInt - ) throws -> PermissionStore.StoreState { + ) async throws -> PermissionStore.StoreState { var updatedState = state switch action { diff --git a/Sources/StreamVideo/Utils/Store/Reducer.swift b/Sources/StreamVideo/Utils/Store/Reducer.swift index ef2d381ae..a1ce504ea 100644 --- a/Sources/StreamVideo/Utils/Store/Reducer.swift +++ b/Sources/StreamVideo/Utils/Store/Reducer.swift @@ -76,7 +76,7 @@ class Reducer: @unchecked Sendable { file: StaticString, function: StaticString, line: UInt - ) throws -> Namespace.State { + ) async throws -> Namespace.State { state } } diff --git a/Sources/StreamVideo/Utils/Store/StoreExecutor.swift b/Sources/StreamVideo/Utils/Store/StoreExecutor.swift index c18ca0029..cf63583f5 100644 --- a/Sources/StreamVideo/Utils/Store/StoreExecutor.swift +++ b/Sources/StreamVideo/Utils/Store/StoreExecutor.swift @@ -63,7 +63,7 @@ class StoreExecutor: @unchecked Sendable { file: StaticString, function: StaticString, line: UInt - ) async throws { + ) async throws -> Namespace.State { // Apply optional delay before processing action await action.applyDelayBeforeIfRequired() @@ -80,9 +80,9 @@ class StoreExecutor: @unchecked Sendable { do { // Process action through all reducers sequentially - let updatedState = try reducers - .reduce(state) { - try $1.reduce( + let updatedState = try await reducers + .asyncReduce(state) { + try await $1.reduce( state: $0, action: action.wrappedValue, file: file, @@ -106,6 +106,8 @@ class StoreExecutor: @unchecked Sendable { // Apply optional delay after successful processing await action.applyDelayAfterIfRequired() + + return updatedState } catch { // Log failure and rethrow logger.didFail( diff --git a/Sources/StreamVideo/Utils/Store/StoreTask.swift b/Sources/StreamVideo/Utils/Store/StoreTask.swift index bfa8c6dd4..5ae03cade 100644 --- a/Sources/StreamVideo/Utils/Store/StoreTask.swift +++ b/Sources/StreamVideo/Utils/Store/StoreTask.swift @@ -64,10 +64,11 @@ final class StoreTask: Sendable { ) async { resultSubject.send(.running) do { + var workingState = state for action in actions { - try await executor.run( + workingState = try await executor.run( identifier: identifier, - state: state, + state: workingState, action: action, reducers: reducers, middleware: middleware, diff --git a/Sources/StreamVideo/WebRTC/v2/Stats/Models/WebRTCTrace.swift b/Sources/StreamVideo/WebRTC/v2/Stats/Models/WebRTCTrace.swift index 1d9377764..4d12aa5d1 100644 --- a/Sources/StreamVideo/WebRTC/v2/Stats/Models/WebRTCTrace.swift +++ b/Sources/StreamVideo/WebRTC/v2/Stats/Models/WebRTCTrace.swift @@ -217,3 +217,15 @@ extension WebRTCTrace { ) } } + +extension WebRTCTrace { + init( + _ battery: BatteryStore + ) { + self.init( + id: nil, + tag: "device.battery.\(battery.state.state)", + data: .init(battery) + ) + } +} diff --git a/Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift b/Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift index 7360c57a2..c7e3eff9e 100644 --- a/Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift +++ b/Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift @@ -132,6 +132,7 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate, W Task { [weak self] in _ = await self?.permissionsAdapter + await self?.configureBatteryObservation() } } @@ -682,6 +683,21 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate, W ) } + private func configureBatteryObservation() { + // Observe battery to attach traces when needed + let battery = BatteryStore.currentValue + Publishers + .CombineLatest( + battery.publisher(\.level).removeDuplicates().eraseToAnyPublisher(), + battery.publisher(\.state).removeDuplicates().eraseToAnyPublisher() + ) + .compactMap { [weak battery] _ in battery } + .sinkTask(on: self, storeIn: disposableBag, handler: { actor, battery in + await actor.trace(.init(battery)) + }) + .store(in: disposableBag) + } + // MARK: - AudioSessionDelegate nonisolated func audioSessionAdapterDidUpdateSpeakerOn(_ speakerOn: Bool) { diff --git a/Sources/StreamVideoSwiftUI/Info.plist b/Sources/StreamVideoSwiftUI/Info.plist index 2a735cd3a..4c36b4383 100644 --- a/Sources/StreamVideoSwiftUI/Info.plist +++ b/Sources/StreamVideoSwiftUI/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.34.1 + 1.34.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright diff --git a/Sources/StreamVideoUIKit/Info.plist b/Sources/StreamVideoUIKit/Info.plist index 2a735cd3a..4c36b4383 100644 --- a/Sources/StreamVideoUIKit/Info.plist +++ b/Sources/StreamVideoUIKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.34.1 + 1.34.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright diff --git a/StreamVideo-XCFramework.podspec b/StreamVideo-XCFramework.podspec index adf9ac570..d816b3a25 100644 --- a/StreamVideo-XCFramework.podspec +++ b/StreamVideo-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideo-XCFramework' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo iOS Video Client' spec.description = 'StreamVideo is the official Swift client for Stream Video, a service for building video applications.' @@ -24,7 +24,7 @@ Pod::Spec.new do |spec| spec.prepare_command = <<-CMD mkdir -p Frameworks/ - curl -sL "https://github.com/GetStream/stream-video-swift-webrtc/releases/download/137.0.41/StreamWebRTC.xcframework.zip" -o Frameworks/StreamWebRTC.zip + curl -sL "https://github.com/GetStream/stream-video-swift-webrtc/releases/download/137.0.42/StreamWebRTC.xcframework.zip" -o Frameworks/StreamWebRTC.zip unzip -o Frameworks/StreamWebRTC.zip -d Frameworks/ rm Frameworks/StreamWebRTC.zip CMD diff --git a/StreamVideo.podspec b/StreamVideo.podspec index 3dd692ae2..0a115ef43 100644 --- a/StreamVideo.podspec +++ b/StreamVideo.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideo' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo iOS Video Client' spec.description = 'StreamVideo is the official Swift client for Stream Video, a service for building video applications.' @@ -25,7 +25,7 @@ Pod::Spec.new do |spec| spec.prepare_command = <<-CMD mkdir -p Frameworks/ - curl -sL "https://github.com/GetStream/stream-video-swift-webrtc/releases/download/137.0.41/StreamWebRTC.xcframework.zip" -o Frameworks/StreamWebRTC.zip + curl -sL "https://github.com/GetStream/stream-video-swift-webrtc/releases/download/137.0.42/StreamWebRTC.xcframework.zip" -o Frameworks/StreamWebRTC.zip unzip -o Frameworks/StreamWebRTC.zip -d Frameworks/ rm Frameworks/StreamWebRTC.zip CMD diff --git a/StreamVideo.xcodeproj/project.pbxproj b/StreamVideo.xcodeproj/project.pbxproj index 2c6db18b8..e2e4a2fd6 100644 --- a/StreamVideo.xcodeproj/project.pbxproj +++ b/StreamVideo.xcodeproj/project.pbxproj @@ -146,6 +146,7 @@ 40245F652BE27B2000FCF075 /* StatelessAudioOutputIconView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40245F642BE27B2000FCF075 /* StatelessAudioOutputIconView_Tests.swift */; }; 40245F672BE27B8400FCF075 /* StatelessSpeakerIconView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40245F662BE27B8400FCF075 /* StatelessSpeakerIconView_Tests.swift */; }; 40245F692BE27CCB00FCF075 /* StatelessParticipantsListButton_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40245F682BE27CCB00FCF075 /* StatelessParticipantsListButton_Tests.swift */; }; + 4026BEEA2EA79FD400360AD0 /* CallFlow_PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4026BEE92EA79FD400360AD0 /* CallFlow_PerformanceTests.swift */; }; 402778832BD13C62002F4399 /* NoiseCancellationFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402778822BD13C62002F4399 /* NoiseCancellationFilter.swift */; }; 4028FE982DC4F638001F9DC3 /* ConsumableBucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4028FE972DC4F638001F9DC3 /* ConsumableBucket.swift */; }; 4028FE9A2DC4FC8E001F9DC3 /* ConsumableBucketItemTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4028FE992DC4FC8E001F9DC3 /* ConsumableBucketItemTransformer.swift */; }; @@ -183,6 +184,9 @@ 402D0E882D0C94CD00E9B83F /* RTCAudioTrack+Clone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D0E872D0C94CD00E9B83F /* RTCAudioTrack+Clone.swift */; }; 402D0E8A2D0C94E600E9B83F /* RTCVideoTrack+Clone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D0E892D0C94E600E9B83F /* RTCVideoTrack+Clone.swift */; }; 402D0E8C2D0C94F900E9B83F /* CallSettings+Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D0E8B2D0C94F900E9B83F /* CallSettings+Audio.swift */; }; + 402E69A22EA65FF90082F7FA /* BatteryStoreObservationMiddleware_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402E69A02EA65FF90082F7FA /* BatteryStoreObservationMiddleware_Tests.swift */; }; + 402E69A32EA65FF90082F7FA /* BatteryStore_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402E699E2EA65FF90082F7FA /* BatteryStore_Tests.swift */; }; + 402E69A42EA65FF90082F7FA /* BatteryStoreDefaultReducer_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402E699F2EA65FF90082F7FA /* BatteryStoreDefaultReducer_Tests.swift */; }; 402EE1302AA8861B00312632 /* DemoChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EE12F2AA8861B00312632 /* DemoChatViewModel.swift */; }; 402F04A92B70ED8600CA1986 /* StreamCallStatisticsReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402F04A62B70ED8600CA1986 /* StreamCallStatisticsReporter.swift */; }; 402F04AA2B70ED8600CA1986 /* Statistics+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402F04A72B70ED8600CA1986 /* Statistics+Convenience.swift */; }; @@ -504,6 +508,13 @@ 40A0E9602B88ABC80089E8D3 /* DemoBackgroundEffectSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0E95F2B88ABC80089E8D3 /* DemoBackgroundEffectSelector.swift */; }; 40A0E9622B88D3DC0089E8D3 /* UIInterfaceOrientation+CGOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0E9612B88D3DC0089E8D3 /* UIInterfaceOrientation+CGOrientation.swift */; }; 40A0E9682B88E04D0089E8D3 /* CIImage_Resize_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0E9672B88E04D0089E8D3 /* CIImage_Resize_Tests.swift */; }; + 40A0FFB12EA63CB900F39D8F /* BatteryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFB02EA63CB900F39D8F /* BatteryStore.swift */; }; + 40A0FFB42EA63D3C00F39D8F /* BatteryStore+Namespace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFB32EA63D3C00F39D8F /* BatteryStore+Namespace.swift */; }; + 40A0FFB62EA63D8F00F39D8F /* BatteryStore+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFB52EA63D8F00F39D8F /* BatteryStore+State.swift */; }; + 40A0FFB82EA63D9700F39D8F /* BatteryStore+Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFB72EA63D9700F39D8F /* BatteryStore+Action.swift */; }; + 40A0FFBB2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBA2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift */; }; + 40A0FFBE2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBD2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift */; }; + 40A0FFC02EA6418000F39D8F /* Sequence+AsyncReduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBF2EA6418000F39D8F /* Sequence+AsyncReduce.swift */; }; 40A7C5B52E099B4600EEDF9C /* ParticipantEventResetAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A7C5B22E099B1000EEDF9C /* ParticipantEventResetAdapter.swift */; }; 40A7C5B82E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A7C5B72E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift */; }; 40A9416E2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A9416D2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift */; }; @@ -664,6 +675,7 @@ 40B575D42DCCECE800F489B8 /* MockAVPictureInPictureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B575D22DCCECDA00F489B8 /* MockAVPictureInPictureController.swift */; }; 40B575D82DCCF00200F489B8 /* StreamPictureInPictureControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B575D52DCCEFB500F489B8 /* StreamPictureInPictureControllerProtocol.swift */; }; 40B713692A275F1400D1FE67 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8456E6C5287EB55F004E180E /* AppState.swift */; }; + 40BAD0B32EA7CE3200CCD3D7 /* StreamWebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = 40BAD0B22EA7CE3200CCD3D7 /* StreamWebRTC */; }; 40BBC4792C6227DC002AEF92 /* DemoNoiseCancellationButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BBC4782C6227DC002AEF92 /* DemoNoiseCancellationButtonView.swift */; }; 40BBC47C2C6227F1002AEF92 /* View+PresentDemoMoreMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BBC47B2C6227F1002AEF92 /* View+PresentDemoMoreMenu.swift */; }; 40BBC47E2C62287F002AEF92 /* DemoReconnectionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BBC47D2C62287F002AEF92 /* DemoReconnectionButtonView.swift */; }; @@ -1896,6 +1908,7 @@ 40245F642BE27B2000FCF075 /* StatelessAudioOutputIconView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatelessAudioOutputIconView_Tests.swift; sourceTree = ""; }; 40245F662BE27B8400FCF075 /* StatelessSpeakerIconView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatelessSpeakerIconView_Tests.swift; sourceTree = ""; }; 40245F682BE27CCB00FCF075 /* StatelessParticipantsListButton_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatelessParticipantsListButton_Tests.swift; sourceTree = ""; }; + 4026BEE92EA79FD400360AD0 /* CallFlow_PerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallFlow_PerformanceTests.swift; sourceTree = ""; }; 402778822BD13C62002F4399 /* NoiseCancellationFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseCancellationFilter.swift; sourceTree = ""; }; 4028FE972DC4F638001F9DC3 /* ConsumableBucket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsumableBucket.swift; sourceTree = ""; }; 4028FE992DC4FC8E001F9DC3 /* ConsumableBucketItemTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsumableBucketItemTransformer.swift; sourceTree = ""; }; @@ -1923,6 +1936,9 @@ 402D0E872D0C94CD00E9B83F /* RTCAudioTrack+Clone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RTCAudioTrack+Clone.swift"; sourceTree = ""; }; 402D0E892D0C94E600E9B83F /* RTCVideoTrack+Clone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RTCVideoTrack+Clone.swift"; sourceTree = ""; }; 402D0E8B2D0C94F900E9B83F /* CallSettings+Audio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallSettings+Audio.swift"; sourceTree = ""; }; + 402E699E2EA65FF90082F7FA /* BatteryStore_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStore_Tests.swift; sourceTree = ""; }; + 402E699F2EA65FF90082F7FA /* BatteryStoreDefaultReducer_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStoreDefaultReducer_Tests.swift; sourceTree = ""; }; + 402E69A02EA65FF90082F7FA /* BatteryStoreObservationMiddleware_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStoreObservationMiddleware_Tests.swift; sourceTree = ""; }; 402EE12F2AA8861B00312632 /* DemoChatViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoChatViewModel.swift; sourceTree = ""; }; 402F04A62B70ED8600CA1986 /* StreamCallStatisticsReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamCallStatisticsReporter.swift; sourceTree = ""; }; 402F04A72B70ED8600CA1986 /* Statistics+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Statistics+Convenience.swift"; sourceTree = ""; }; @@ -2185,6 +2201,13 @@ 40A0E95F2B88ABC80089E8D3 /* DemoBackgroundEffectSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoBackgroundEffectSelector.swift; sourceTree = ""; }; 40A0E9612B88D3DC0089E8D3 /* UIInterfaceOrientation+CGOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIInterfaceOrientation+CGOrientation.swift"; sourceTree = ""; }; 40A0E9672B88E04D0089E8D3 /* CIImage_Resize_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImage_Resize_Tests.swift; sourceTree = ""; }; + 40A0FFB02EA63CB900F39D8F /* BatteryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStore.swift; sourceTree = ""; }; + 40A0FFB32EA63D3C00F39D8F /* BatteryStore+Namespace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+Namespace.swift"; sourceTree = ""; }; + 40A0FFB52EA63D8F00F39D8F /* BatteryStore+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+State.swift"; sourceTree = ""; }; + 40A0FFB72EA63D9700F39D8F /* BatteryStore+Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+Action.swift"; sourceTree = ""; }; + 40A0FFBA2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+DefaultReducer.swift"; sourceTree = ""; }; + 40A0FFBD2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+ObservationMiddleware.swift"; sourceTree = ""; }; + 40A0FFBF2EA6418000F39D8F /* Sequence+AsyncReduce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+AsyncReduce.swift"; sourceTree = ""; }; 40A7C5B22E099B1000EEDF9C /* ParticipantEventResetAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantEventResetAdapter.swift; sourceTree = ""; }; 40A7C5B72E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantEventResetAdapter_Tests.swift; sourceTree = ""; }; 40A9416D2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamPictureInPictureAdapter.swift; sourceTree = ""; }; @@ -3186,6 +3209,7 @@ buildActionMask = 2147483647; files = ( 40C4DF522C1C60A80035DBC2 /* StreamVideo.framework in Frameworks */, + 40BAD0B32EA7CE3200CCD3D7 /* StreamWebRTC in Frameworks */, 822FF7212AEAD100000202A7 /* StreamSwiftTestHelpers in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3753,6 +3777,7 @@ isa = PBXGroup; children = ( 4028FEAA2DC536DE001F9DC3 /* Date+millisecondsSince1970.swift */, + 40A0FFBF2EA6418000F39D8F /* Sequence+AsyncReduce.swift */, ); path = Foundation; sourceTree = ""; @@ -3813,6 +3838,16 @@ path = SerialActorQueue; sourceTree = ""; }; + 402E69A12EA65FF90082F7FA /* Battery */ = { + isa = PBXGroup; + children = ( + 402E699E2EA65FF90082F7FA /* BatteryStore_Tests.swift */, + 402E699F2EA65FF90082F7FA /* BatteryStoreDefaultReducer_Tests.swift */, + 402E69A02EA65FF90082F7FA /* BatteryStoreObservationMiddleware_Tests.swift */, + ); + path = Battery; + sourceTree = ""; + }; 402F04A52B70ED8600CA1986 /* Statistics */ = { isa = PBXGroup; children = ( @@ -4834,6 +4869,43 @@ path = Extensions; sourceTree = ""; }; + 40A0FFAF2EA63CB000F39D8F /* Battery */ = { + isa = PBXGroup; + children = ( + 40A0FFB22EA63D1C00F39D8F /* Namespace */, + 40A0FFB02EA63CB900F39D8F /* BatteryStore.swift */, + ); + path = Battery; + sourceTree = ""; + }; + 40A0FFB22EA63D1C00F39D8F /* Namespace */ = { + isa = PBXGroup; + children = ( + 40A0FFBC2EA63F6F00F39D8F /* Middleware */, + 40A0FFB92EA63E9200F39D8F /* Reducers */, + 40A0FFB32EA63D3C00F39D8F /* BatteryStore+Namespace.swift */, + 40A0FFB52EA63D8F00F39D8F /* BatteryStore+State.swift */, + 40A0FFB72EA63D9700F39D8F /* BatteryStore+Action.swift */, + ); + path = Namespace; + sourceTree = ""; + }; + 40A0FFB92EA63E9200F39D8F /* Reducers */ = { + isa = PBXGroup; + children = ( + 40A0FFBA2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift */, + ); + path = Reducers; + sourceTree = ""; + }; + 40A0FFBC2EA63F6F00F39D8F /* Middleware */ = { + isa = PBXGroup; + children = ( + 40A0FFBD2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift */, + ); + path = Middleware; + sourceTree = ""; + }; 40A7C5B42E099B1600EEDF9C /* ParticipantEventResetAdapter */ = { isa = PBXGroup; children = ( @@ -6270,6 +6342,7 @@ 824DBA9F29F6D77B005ACD09 /* ReconnectionTests.swift */, 82FB89362A702A9200AC16A1 /* Authentication_Tests.swift */, 40B499CB2AC1A90F00A53B60 /* DeeplinkTests.swift */, + 4026BEE92EA79FD400360AD0 /* CallFlow_PerformanceTests.swift */, ); path = Tests; sourceTree = ""; @@ -6426,6 +6499,7 @@ 842747F429EEDACB00E063AD /* Utils */ = { isa = PBXGroup; children = ( + 402E69A12EA65FF90082F7FA /* Battery */, 40064BD72E5C88DC007CDB33 /* PermissionStore */, 40C71B572E5355F800733BF6 /* Store */, 40FAAC852DDC9B2D007BF93A /* AnyEncodable.swift */, @@ -6954,6 +7028,7 @@ 84AF64D3287C79220012A503 /* Utils */ = { isa = PBXGroup; children = ( + 40A0FFAF2EA63CB000F39D8F /* Battery */, 40C8F0042E55D21D00A0CC87 /* PermissionsStore */, 40944D132E4E2D8800088AF0 /* Store */, 406568852E0426F600A67EAC /* IdleTimerAdapter */, @@ -7626,6 +7701,7 @@ name = SwiftUIDemoAppUITests; packageProductDependencies = ( 822FF7202AEAD100000202A7 /* StreamSwiftTestHelpers */, + 40BAD0B22EA7CE3200CCD3D7 /* StreamWebRTC */, ); productName = SwiftUIDemoAppUITests; productReference = 82392D512993C9E100941435 /* SwiftUIDemoAppUITests.xctest */; @@ -8099,6 +8175,7 @@ files = ( 82392D5F2993CCB300941435 /* ParticipantRobot.swift in Sources */, 82C837E429A5333700CB6B0E /* CallDetailsPage.swift in Sources */, + 4026BEEA2EA79FD400360AD0 /* CallFlow_PerformanceTests.swift in Sources */, 82C837E229A532C000CB6B0E /* LoginPage.swift in Sources */, 82392D542993C9E100941435 /* StreamTestCase.swift in Sources */, 82C837E029A531ED00CB6B0E /* CallPage.swift in Sources */, @@ -8377,6 +8454,7 @@ 84A7E184288362DF00526C98 /* Atomic.swift in Sources */, 8449824E2C738A830029734D /* StopAllRTMPBroadcastsResponse.swift in Sources */, 40E363522D0A11620028C52A /* AVCaptureDevice+OutputFormat.swift in Sources */, + 40A0FFB42EA63D3C00F39D8F /* BatteryStore+Namespace.swift in Sources */, 84D2E37729DC856D001D2118 /* CallMemberUpdatedEvent.swift in Sources */, 40DFA88D2CC10FF3003DCE05 /* Stream_Video_Sfu_Models_AppleThermalState+Convenience.swift in Sources */, 8409465B29AF4EEC007AF5BF /* ListRecordingsResponse.swift in Sources */, @@ -8460,6 +8538,7 @@ 841BAA462BD15CDE000C73E4 /* CallStatsReportSummaryResponse.swift in Sources */, 84DC38D829ADFCFD00946713 /* JoinCallRequest.swift in Sources */, 40ADB8612D65DFD700B06AAF /* String.StringInterpolation+Nil.swift in Sources */, + 40A0FFB62EA63D8F00F39D8F /* BatteryStore+State.swift in Sources */, 84AF64D2287C78E70012A503 /* User.swift in Sources */, 84274F482884251600CF8794 /* InternetConnection.swift in Sources */, 84DC389129ADFCFD00946713 /* VideoSettings.swift in Sources */, @@ -8488,6 +8567,7 @@ 8490DD1F298D39D9007E53D2 /* JsonEventDecoder.swift in Sources */, 40FB15192BF77EE700D5E580 /* Call+Idle.swift in Sources */, 40ADB85C2D64B00E00B06AAF /* CGSize+Hashable.swift in Sources */, + 40A0FFBE2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift in Sources */, 8438AB062E5F3A2900BA834F /* FileUploadConfig.swift in Sources */, 8438AB072E5F3A2900BA834F /* AppEventResponse.swift in Sources */, 40E3633E2D09EF560028C52A /* CMVideoDimensions+DefaultValues.swift in Sources */, @@ -8511,6 +8591,7 @@ 40B48C472D14E803002C4EAB /* StreamVideoCapturing.swift in Sources */, 842E70D72B91BE1700D2D68B /* CallRecordingFailedEvent.swift in Sources */, 40F1016A2D5A653B00C49481 /* AudioSessionConfiguration.swift in Sources */, + 40A0FFBB2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift in Sources */, 40BBC4BE2C6280E4002AEF92 /* LocalScreenShareMediaAdapter.swift in Sources */, 841BAA332BD15CDE000C73E4 /* SFULocationResponse.swift in Sources */, 84DC38D129ADFCFD00946713 /* Credentials.swift in Sources */, @@ -8599,6 +8680,7 @@ 40C4E85D2E6999A500FC29BC /* RTCAudioStore+RestartAudioSession.swift in Sources */, 40BBC4BA2C627F83002AEF92 /* TrackEvent.swift in Sources */, 406128832CF33000007F5CDC /* SDPParser.swift in Sources */, + 40A0FFC02EA6418000F39D8F /* Sequence+AsyncReduce.swift in Sources */, 84B9A56D29112F39004DE31A /* EndpointConfig.swift in Sources */, 405072672E5F4CF7003D2109 /* CallKitMissingPermissionPolicy+EndCall.swift in Sources */, 4039F0CF2D024DDF0078159E /* MediaTransceiverStorage.swift in Sources */, @@ -8653,6 +8735,7 @@ 4012B1942BFCAC1C006B0031 /* Call+RejectingStage.swift in Sources */, 40BBC4D22C639158002AEF92 /* WebRTCCoordinator+Connecting.swift in Sources */, 40BBC4AF2C627692002AEF92 /* LocalMediaAdapting.swift in Sources */, + 40A0FFB82EA63D9700F39D8F /* BatteryStore+Action.swift in Sources */, 84DCA2152A38A79E000C3411 /* Token.swift in Sources */, 40FB151B2BF77EEE00D5E580 /* Call+JoiningStage.swift in Sources */, 402B34C02DCDEE9000574663 /* WebRTCUpdateSubscriptionsAdapter.swift in Sources */, @@ -8736,6 +8819,7 @@ 40E363452D09F2BD0028C52A /* AVCaptureDevice.Format+Convenience.swift in Sources */, 84DC38CB29ADFCFD00946713 /* SortParamRequest.swift in Sources */, 8490032529D308A000AD9BB4 /* GetCallResponse.swift in Sources */, + 40A0FFB12EA63CB900F39D8F /* BatteryStore.swift in Sources */, 841947982886D9CD0007B36E /* BundleExtensions.swift in Sources */, 40483CB82C9B1DEE00B4FCA8 /* WebRTCCoordinatorProviding.swift in Sources */, 4028FEAB2DC536DE001F9DC3 /* Date+millisecondsSince1970.swift in Sources */, @@ -9088,6 +9172,9 @@ 8414080F29F2838F00FF2D7C /* RawJSON_Tests.swift in Sources */, 40E9B3B72BCD941600ACF18F /* SFUResponse+Dummy.swift in Sources */, 40382F432C89CF9700C2D00F /* Stream_Video_Sfu_Models_ConnectionQuality+Convenience.swift in Sources */, + 402E69A22EA65FF90082F7FA /* BatteryStoreObservationMiddleware_Tests.swift in Sources */, + 402E69A32EA65FF90082F7FA /* BatteryStore_Tests.swift in Sources */, + 402E69A42EA65FF90082F7FA /* BatteryStoreDefaultReducer_Tests.swift in Sources */, 40FAAC862DDC9B2D007BF93A /* AnyEncodable.swift in Sources */, 40B48C1F2D14CAFC002C4EAB /* Comparable_ClampedTests.swift in Sources */, 404A812F2DA3C4FC001F7FA8 /* CallStateMachine_AcceptingStageTests.swift in Sources */, @@ -9882,6 +9969,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -9946,6 +10035,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10004,6 +10095,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10750,6 +10843,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10792,6 +10887,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10868,6 +10965,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10908,6 +11007,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -10984,6 +11085,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -11024,6 +11127,8 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_GENERATE_MAP_FILE = YES; + LD_MAP_FILE_PATH = "linkmaps/$(PRODUCT_NAME)-$(CURRENT_ARCH)-LinkMap.txt"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -11259,7 +11364,7 @@ repositoryURL = "https://github.com/GetStream/stream-video-swift-webrtc"; requirement = { kind = exactVersion; - version = 137.0.41; + version = 137.0.42; }; }; 8423B7542950BB0A00012F8D /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { @@ -11329,6 +11434,11 @@ package = 40AC73B22BE0062B00C57517 /* XCRemoteSwiftPackageReference "stream-video-noise-cancellation-swift" */; productName = StreamVideoNoiseCancellation; }; + 40BAD0B22EA7CE3200CCD3D7 /* StreamWebRTC */ = { + isa = XCSwiftPackageProductDependency; + package = 82EB8F552B0277730038B5A2 /* XCRemoteSwiftPackageReference "stream-video-swift-webrtc" */; + productName = StreamWebRTC; + }; 40C708D52D8D729500D3501F /* Gleap */ = { isa = XCSwiftPackageProductDependency; package = 4014F1012D8C2EBC004E7EFD /* XCRemoteSwiftPackageReference "Gleap-iOS-SDK" */; diff --git a/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/72BCA2C7-BCAB-4F29-B5A2-C1B720A9D6C7.plist b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/72BCA2C7-BCAB-4F29-B5A2-C1B720A9D6C7.plist new file mode 100644 index 000000000..838e7a85e --- /dev/null +++ b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/72BCA2C7-BCAB-4F29-B5A2-C1B720A9D6C7.plist @@ -0,0 +1,22 @@ + + + + + classNames + + CallFlow_PerformanceTests + + test_performance_with2Participants() + + com.apple.dt.XCTMetric_Memory-io.getstream.iOS.VideoDemoApp.physical_absolute + + baselineAverage + 125000.000000 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/A871F8A3-784B-4A04-8235-A22FE4A40D80.plist b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/A871F8A3-784B-4A04-8235-A22FE4A40D80.plist new file mode 100644 index 000000000..baec3a88d --- /dev/null +++ b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/A871F8A3-784B-4A04-8235-A22FE4A40D80.plist @@ -0,0 +1,36 @@ + + + + + classNames + + CallFlow_PerformanceTests + + test_performance_with2Participants() + + com.apple.dt.XCTMetric_Memory-io.getstream.iOS.VideoDemoApp.physical + + baselineAverage + 414000.000000 + baselineIntegrationDisplayName + Local Baseline + + com.apple.dt.XCTMetric_Memory-io.getstream.iOS.VideoDemoApp.physical_absolute + + baselineAverage + 414000.000000 + baselineIntegrationDisplayName + Local Baseline + + com.apple.dt.XCTMetric_Memory-io.getstream.iOS.VideoDemoApp.physical_peak + + baselineAverage + 802000.000000 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/Info.plist b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/Info.plist new file mode 100644 index 000000000..5951734d9 --- /dev/null +++ b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/82392D502993C9E100941435.xcbaseline/Info.plist @@ -0,0 +1,52 @@ + + + + + runDestinationsByUUID + + 72BCA2C7-BCAB-4F29-B5A2-C1B720A9D6C7 + + targetArchitecture + arm64 + targetDevice + + modelCode + iPhone13,1 + platformIdentifier + com.apple.platform.iphoneos + + + A871F8A3-784B-4A04-8235-A22FE4A40D80 + + localComputer + + busSpeedInMHz + 0 + cpuCount + 1 + cpuKind + Apple M1 Pro + cpuSpeedInMHz + 0 + logicalCPUCoresPerPackage + 10 + modelCode + MacBookPro18,1 + physicalCPUCoresPerPackage + 10 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + arm64 + targetDevice + + modelCode + iPhone18,1 + platformIdentifier + com.apple.platform.iphonesimulator + + + + + diff --git a/StreamVideo.xcodeproj/xcshareddata/xcbaselines/84F737F3287C13AD00A363F4.xcbaseline/405B1570-F1EC-4638-9C96-FA56CB97DF5B.plist b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/84F737F3287C13AD00A363F4.xcbaseline/405B1570-F1EC-4638-9C96-FA56CB97DF5B.plist index 8c3fa6470..1ea8c9de0 100644 --- a/StreamVideo.xcodeproj/xcshareddata/xcbaselines/84F737F3287C13AD00A363F4.xcbaseline/405B1570-F1EC-4638-9C96-FA56CB97DF5B.plist +++ b/StreamVideo.xcodeproj/xcshareddata/xcbaselines/84F737F3287C13AD00A363F4.xcbaseline/405B1570-F1EC-4638-9C96-FA56CB97DF5B.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 1.046902 + 2.060000 baselineIntegrationDisplayName Local Baseline diff --git a/StreamVideoArtifacts.json b/StreamVideoArtifacts.json index d976a83c0..e62ba2ff7 100644 --- a/StreamVideoArtifacts.json +++ b/StreamVideoArtifacts.json @@ -1 +1 @@ -{"0.4.2":"https://github.com/GetStream/stream-video-swift/releases/download/0.4.2/StreamVideo-All.zip","0.5.0":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.0/StreamVideo-All.zip","0.5.1":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.1/StreamVideo-All.zip","0.5.2":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.2/StreamVideo-All.zip","0.5.3":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.3/StreamVideo-All.zip","1.0.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.0/StreamVideo-All.zip","1.0.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.1/StreamVideo-All.zip","1.0.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.2/StreamVideo-All.zip","1.0.3":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.3/StreamVideo-All.zip","1.0.4":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.4/StreamVideo-All.zip","1.0.5":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.5/StreamVideo-All.zip","1.0.6":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.6/StreamVideo-All.zip","1.0.7":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.7/StreamVideo-All.zip","1.0.8":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.8/StreamVideo-All.zip","1.0.9":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.9/StreamVideo-All.zip","1.10.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.10.0/StreamVideo-All.zip","1.11.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.11.0/StreamVideo-All.zip","1.12.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.12.0/StreamVideo-All.zip","1.13.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.13.0/StreamVideo-All.zip","1.14.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.14.0/StreamVideo-All.zip","1.14.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.14.1/StreamVideo-All.zip","1.15.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.15.0/StreamVideo-All.zip","1.16.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.16.0/StreamVideo-All.zip","1.17.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.17.0/StreamVideo-All.zip","1.18.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.18.0/StreamVideo-All.zip","1.19.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.0/StreamVideo-All.zip","1.19.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.1/StreamVideo-All.zip","1.19.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.2/StreamVideo-All.zip","1.20.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.20.0/StreamVideo-All.zip","1.21.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.21.0/StreamVideo-All.zip","1.21.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.21.1/StreamVideo-All.zip","1.22.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.0/StreamVideo-All.zip","1.22.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.1/StreamVideo-All.zip","1.22.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.2/StreamVideo-All.zip","1.24.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.24.0/StreamVideo-All.zip","1.25.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.25.0/StreamVideo-All.zip","1.26.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.26.0/StreamVideo-All.zip","1.27.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.0/StreamVideo-All.zip","1.27.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.1/StreamVideo-All.zip","1.27.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.2/StreamVideo-All.zip","1.28.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.28.0/StreamVideo-All.zip","1.28.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.28.1/StreamVideo-All.zip","1.29.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.29.0/StreamVideo-All.zip","1.29.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.29.1/StreamVideo-All.zip","1.30.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.30.0/StreamVideo-All.zip","1.31.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.31.0/StreamVideo-All.zip","1.32.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.32.0/StreamVideo-All.zip","1.33.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.33.0/StreamVideo-All.zip","1.34.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.34.0/StreamVideo-All.zip","1.34.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.34.1/StreamVideo-All.zip"} \ No newline at end of file +{"0.4.2":"https://github.com/GetStream/stream-video-swift/releases/download/0.4.2/StreamVideo-All.zip","0.5.0":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.0/StreamVideo-All.zip","0.5.1":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.1/StreamVideo-All.zip","0.5.2":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.2/StreamVideo-All.zip","0.5.3":"https://github.com/GetStream/stream-video-swift/releases/download/0.5.3/StreamVideo-All.zip","1.0.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.0/StreamVideo-All.zip","1.0.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.1/StreamVideo-All.zip","1.0.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.2/StreamVideo-All.zip","1.0.3":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.3/StreamVideo-All.zip","1.0.4":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.4/StreamVideo-All.zip","1.0.5":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.5/StreamVideo-All.zip","1.0.6":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.6/StreamVideo-All.zip","1.0.7":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.7/StreamVideo-All.zip","1.0.8":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.8/StreamVideo-All.zip","1.0.9":"https://github.com/GetStream/stream-video-swift/releases/download/1.0.9/StreamVideo-All.zip","1.10.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.10.0/StreamVideo-All.zip","1.11.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.11.0/StreamVideo-All.zip","1.12.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.12.0/StreamVideo-All.zip","1.13.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.13.0/StreamVideo-All.zip","1.14.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.14.0/StreamVideo-All.zip","1.14.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.14.1/StreamVideo-All.zip","1.15.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.15.0/StreamVideo-All.zip","1.16.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.16.0/StreamVideo-All.zip","1.17.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.17.0/StreamVideo-All.zip","1.18.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.18.0/StreamVideo-All.zip","1.19.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.0/StreamVideo-All.zip","1.19.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.1/StreamVideo-All.zip","1.19.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.19.2/StreamVideo-All.zip","1.20.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.20.0/StreamVideo-All.zip","1.21.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.21.0/StreamVideo-All.zip","1.21.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.21.1/StreamVideo-All.zip","1.22.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.0/StreamVideo-All.zip","1.22.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.1/StreamVideo-All.zip","1.22.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.22.2/StreamVideo-All.zip","1.24.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.24.0/StreamVideo-All.zip","1.25.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.25.0/StreamVideo-All.zip","1.26.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.26.0/StreamVideo-All.zip","1.27.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.0/StreamVideo-All.zip","1.27.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.1/StreamVideo-All.zip","1.27.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.27.2/StreamVideo-All.zip","1.28.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.28.0/StreamVideo-All.zip","1.28.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.28.1/StreamVideo-All.zip","1.29.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.29.0/StreamVideo-All.zip","1.29.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.29.1/StreamVideo-All.zip","1.30.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.30.0/StreamVideo-All.zip","1.31.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.31.0/StreamVideo-All.zip","1.32.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.32.0/StreamVideo-All.zip","1.33.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.33.0/StreamVideo-All.zip","1.34.0":"https://github.com/GetStream/stream-video-swift/releases/download/1.34.0/StreamVideo-All.zip","1.34.1":"https://github.com/GetStream/stream-video-swift/releases/download/1.34.1/StreamVideo-All.zip","1.34.2":"https://github.com/GetStream/stream-video-swift/releases/download/1.34.2/StreamVideo-All.zip"} \ No newline at end of file diff --git a/StreamVideoSwiftUI-XCFramework.podspec b/StreamVideoSwiftUI-XCFramework.podspec index c542308da..547b4909e 100644 --- a/StreamVideoSwiftUI-XCFramework.podspec +++ b/StreamVideoSwiftUI-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideoSwiftUI-XCFramework' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo SwiftUI Video Components' spec.description = 'StreamVideoSwiftUI SDK offers flexible SwiftUI components able to display data provided by StreamVideo SDK.' diff --git a/StreamVideoSwiftUI.podspec b/StreamVideoSwiftUI.podspec index 835ad3106..cd747365c 100644 --- a/StreamVideoSwiftUI.podspec +++ b/StreamVideoSwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideoSwiftUI' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo SwiftUI Video Components' spec.description = 'StreamVideoSwiftUI SDK offers flexible SwiftUI components able to display data provided by StreamVideo SDK.' diff --git a/StreamVideoSwiftUITests/CallViewModel_Tests.swift b/StreamVideoSwiftUITests/CallViewModel_Tests.swift index a6bb3b522..4b406e51b 100644 --- a/StreamVideoSwiftUITests/CallViewModel_Tests.swift +++ b/StreamVideoSwiftUITests/CallViewModel_Tests.swift @@ -219,7 +219,6 @@ final class CallViewModel_Tests: XCTestCase, @unchecked Sendable { } func test_outgoingCall_callCreatedPriorToStarting_rejectedEventFromOneParticipantCallRemainsOngoing() async throws { - LogConfig.level = .debug // Given let memberResponses = (participants + [thirdUser]).map { MemberResponse( diff --git a/StreamVideoTests/Mock/Store/MockReducer.swift b/StreamVideoTests/Mock/Store/MockReducer.swift index 6ceb62473..f976804fd 100644 --- a/StreamVideoTests/Mock/Store/MockReducer.swift +++ b/StreamVideoTests/Mock/Store/MockReducer.swift @@ -20,7 +20,7 @@ final class MockReducer: Reducer, @uncheck file: StaticString, function: StaticString, line: UInt - ) throws -> Namespace.State { + ) async throws -> Namespace.State { inputs.append(.init(action: action, state: state)) return state } diff --git a/StreamVideoTests/StreamVideo_Tests.swift b/StreamVideoTests/StreamVideo_Tests.swift index a03a0ce83..a5d3cf34a 100644 --- a/StreamVideoTests/StreamVideo_Tests.swift +++ b/StreamVideoTests/StreamVideo_Tests.swift @@ -58,7 +58,6 @@ final class StreamVideo_Tests: StreamVideoTestCase, @unchecked Sendable { } func test_streamVideo_activeCallAndLeave() async throws { - LogConfig.level = .debug // Given let streamVideo = StreamVideo.mock(httpClient: HTTPClient_Mock()) self.streamVideo = streamVideo @@ -79,7 +78,6 @@ final class StreamVideo_Tests: StreamVideoTestCase, @unchecked Sendable { // Then XCTAssert(streamVideo.state.activeCall == nil) - LogConfig.level = .info } func test_streamVideo_ringCallAccept() async throws { @@ -148,7 +146,6 @@ final class StreamVideo_Tests: StreamVideoTestCase, @unchecked Sendable { } func test_streamVideo_incomingCallAccept() async throws { - LogConfig.level = .debug // Given let streamVideo = StreamVideo.mock(httpClient: HTTPClient_Mock()) self.streamVideo = streamVideo @@ -182,7 +179,6 @@ final class StreamVideo_Tests: StreamVideoTestCase, @unchecked Sendable { // Then XCTAssert(streamVideo.state.ringingCall == nil) XCTAssert(streamVideo.state.activeCall?.cId == call.cId) - LogConfig.level = .info } func test_streamVideo_incomingCallReject() async throws { diff --git a/StreamVideoTests/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore_DefaultReducer_Tests.swift b/StreamVideoTests/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore_DefaultReducer_Tests.swift index 30e43a3ee..f058423b0 100644 --- a/StreamVideoTests/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore_DefaultReducer_Tests.swift +++ b/StreamVideoTests/Utils/AudioSession/AudioProcessing/Namespace/Reducers/AudioProcessingStore_DefaultReducer_Tests.swift @@ -16,10 +16,10 @@ final class AudioProcessingStore_DefaultReducer_Tests: XCTestCase, @unchecked Se super.tearDown() } - func test_setInitializedConfiguration_updatesSampleRateAndChannels() throws { + func test_setInitializedConfiguration_updatesSampleRateAndChannels() async throws { let initial = AudioProcessingStore.Namespace.StoreState.initial - let updated = try subject.reduce( + let updated = try await subject.reduce( state: initial, action: .setInitializedConfiguration(sampleRate: 48000, channels: 2), file: #file, @@ -31,11 +31,11 @@ final class AudioProcessingStore_DefaultReducer_Tests: XCTestCase, @unchecked Se XCTAssertEqual(updated.initializedChannels, 2) } - func test_setAudioFilter_setsActiveFilter() throws { + func test_setAudioFilter_setsActiveFilter() async throws { let initial = AudioProcessingStore.Namespace.StoreState.initial let filter = MockAudioFilter(id: "test-filter") - let updated = try subject.reduce( + let updated = try await subject.reduce( state: initial, action: .setAudioFilter(filter), file: #file, @@ -46,11 +46,11 @@ final class AudioProcessingStore_DefaultReducer_Tests: XCTestCase, @unchecked Se XCTAssertEqual(updated.audioFilter?.id, "test-filter") } - func test_setNumberOfCaptureChannels_updatesCount() throws { + func test_setNumberOfCaptureChannels_updatesCount() async throws { var initial = AudioProcessingStore.Namespace.StoreState.initial initial.numberOfCaptureChannels = 1 - let updated = try subject.reduce( + let updated = try await subject.reduce( state: initial, action: .setNumberOfCaptureChannels(2), file: #file, @@ -61,12 +61,12 @@ final class AudioProcessingStore_DefaultReducer_Tests: XCTestCase, @unchecked Se XCTAssertEqual(updated.numberOfCaptureChannels, 2) } - func test_release_resetsInitialization() throws { + func test_release_resetsInitialization() async throws { var initial = AudioProcessingStore.Namespace.StoreState.initial initial.initializedSampleRate = 44100 initial.initializedChannels = 1 - let updated = try subject.reduce( + let updated = try await subject.reduce( state: initial, action: .release, file: #file, diff --git a/StreamVideoTests/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder_DefaultReducerTests.swift b/StreamVideoTests/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder_DefaultReducerTests.swift index 3a2c37b63..52c54bb2b 100644 --- a/StreamVideoTests/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder_DefaultReducerTests.swift +++ b/StreamVideoTests/Utils/AudioSession/AudioRecorder/Namespace/Reducers/StreamCallAudioRecorder_DefaultReducerTests.swift @@ -19,15 +19,15 @@ final class StreamCallAudioRecorder_DefaultReducerTests: XCTestCase, @unchecked // MARK: isRecording - func test_reducer_setIsRecording_true_returnsExpectedState() throws { - try assertState( + func test_reducer_setIsRecording_true_returnsExpectedState() async throws { + try await assertState( action: .setIsRecording(true), validation: { $0.isRecording == true } ) } - func test_reducer_setIsRecording_false_returnsExpectedState() throws { - try assertState( + func test_reducer_setIsRecording_false_returnsExpectedState() async throws { + try await assertState( action: .setIsRecording(false), validation: { $0.isRecording == false } ) @@ -35,15 +35,15 @@ final class StreamCallAudioRecorder_DefaultReducerTests: XCTestCase, @unchecked // MARK: isInterrupted - func test_reducer_setIsInterrupted_true_returnsExpectedState() throws { - try assertState( + func test_reducer_setIsInterrupted_true_returnsExpectedState() async throws { + try await assertState( action: .setIsInterrupted(true), validation: { $0.isInterrupted == true } ) } - func test_reducer_setIsInterrupted_false_returnsExpectedState() throws { - try assertState( + func test_reducer_setIsInterrupted_false_returnsExpectedState() async throws { + try await assertState( action: .setIsInterrupted(false), validation: { $0.isInterrupted == false } ) @@ -51,15 +51,15 @@ final class StreamCallAudioRecorder_DefaultReducerTests: XCTestCase, @unchecked // MARK: shouldRecord - func test_reducer_setShouldRecord_true_returnsExpectedState() throws { - try assertState( + func test_reducer_setShouldRecord_true_returnsExpectedState() async throws { + try await assertState( action: .setShouldRecord(true), validation: { $0.shouldRecord == true } ) } - func test_reducer_setShouldRecord_false_returnsExpectedState() throws { - try assertState( + func test_reducer_setShouldRecord_false_returnsExpectedState() async throws { + try await assertState( action: .setShouldRecord(false), validation: { $0.shouldRecord == false } ) @@ -67,8 +67,8 @@ final class StreamCallAudioRecorder_DefaultReducerTests: XCTestCase, @unchecked // MARK: meter - func test_reducer_setMeter_true_returnsExpectedState() throws { - try assertState( + func test_reducer_setMeter_true_returnsExpectedState() async throws { + try await assertState( action: .setMeter(10.1), validation: { $0.meter == 10.1 } ) @@ -82,8 +82,8 @@ final class StreamCallAudioRecorder_DefaultReducerTests: XCTestCase, @unchecked function: StaticString = #function, line: UInt = #line, validation: (StreamCallAudioRecorder.Namespace.State) -> Bool - ) throws { - let actual = try subject.reduce( + ) async throws { + let actual = try await subject.reduce( state: .initial, action: action, file: file, diff --git a/StreamVideoTests/Utils/Battery/BatteryStoreDefaultReducer_Tests.swift b/StreamVideoTests/Utils/Battery/BatteryStoreDefaultReducer_Tests.swift new file mode 100644 index 000000000..504d45b11 --- /dev/null +++ b/StreamVideoTests/Utils/Battery/BatteryStoreDefaultReducer_Tests.swift @@ -0,0 +1,80 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +@testable import StreamVideo +import UIKit +import XCTest + +final class BatteryStoreDefaultReducer_Tests: XCTestCase, @unchecked Sendable { + + private var reducer: BatteryStore.Namespace.DefaultReducer! + private var state: BatteryStore.Namespace.StoreState! + + override func setUp() { + super.setUp() + reducer = .init() + state = .init(isMonitoringEnabled: false, state: .unknown, level: 0) + } + + override func tearDown() { + state = nil + reducer = nil + super.tearDown() + } + + func test_reduce_setLevel_roundsAndClamps() async throws { + let updated = try await reducer.reduce( + state: state, + action: .setLevel(0.736), + file: #file, + function: #function, + line: #line + ) + + XCTAssertEqual(updated.level, 74) + XCTAssertEqual(state.level, 0) + } + + func test_reduce_setLevel_handlesNegativeValues() async throws { + let updated = try await reducer.reduce( + state: state, + action: .setLevel(-0.2), + file: #file, + function: #function, + line: #line + ) + + XCTAssertEqual(updated.level, 0) + } + + func test_reduce_setState_updatesBatteryState() async throws { + let updated = try await reducer.reduce( + state: state, + action: .setState(.full), + file: #file, + function: #function, + line: #line + ) + + XCTAssertEqual(updated.state, .full) + } + + #if canImport(UIKit) + func test_reduce_setMonitoringEnabled_updatesFlagAndDevice() async throws { + await MainActor.run { + UIDevice.current.isBatteryMonitoringEnabled = false + } + + let updated = try await reducer.reduce( + state: state, + action: .setMonitoringEnabled(true), + file: #file, + function: #function, + line: #line + ) + + XCTAssertTrue(updated.isMonitoringEnabled) + } + #endif +} diff --git a/StreamVideoTests/Utils/Battery/BatteryStoreObservationMiddleware_Tests.swift b/StreamVideoTests/Utils/Battery/BatteryStoreObservationMiddleware_Tests.swift new file mode 100644 index 000000000..37db0d444 --- /dev/null +++ b/StreamVideoTests/Utils/Battery/BatteryStoreObservationMiddleware_Tests.swift @@ -0,0 +1,83 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +#if canImport(UIKit) +import UIKit + +@testable import StreamVideo +import XCTest + +final class BatteryStoreObservationMiddleware_Tests: XCTestCase, @unchecked Sendable { + + func test_batteryStateNotification_dispatchesSetState() async { + let expectation = expectation(description: "Dispatch setState") + let middleware = BatteryStore.Namespace.ObservationMiddleware() + let collector = ActionCollector(expectation: expectation) { action in + if case .setState = action { + return true + } + return false + } + + middleware.dispatcher = .init { actions, _, _, _ in + collector.handle(actions.map(\.wrappedValue)) + } + + await MainActor.run { + UIDevice.current.isBatteryMonitoringEnabled = true + NotificationCenter + .default + .post(name: UIDevice.batteryStateDidChangeNotification, object: nil) + } + + await fulfillment(of: [expectation], timeout: 1.0) + withExtendedLifetime(middleware) {} + } + + func test_batteryLevelNotification_dispatchesSetLevel() async { + let expectation = expectation(description: "Dispatch setLevel") + let middleware = BatteryStore.Namespace.ObservationMiddleware() + let collector = ActionCollector(expectation: expectation) { action in + if case .setLevel = action { + return true + } + return false + } + + middleware.dispatcher = .init { actions, _, _, _ in + collector.handle(actions.map(\.wrappedValue)) + } + + await MainActor.run { + UIDevice.current.isBatteryMonitoringEnabled = true + NotificationCenter + .default + .post(name: UIDevice.batteryLevelDidChangeNotification, object: nil) + } + + await fulfillment(of: [expectation], timeout: 1.0) + withExtendedLifetime(middleware) {} + } +} + +private final class ActionCollector: @unchecked Sendable { + private let expectation: XCTestExpectation + private let matcher: (BatteryStore.Namespace.Action) -> Bool + + init( + expectation: XCTestExpectation, + matcher: @escaping (BatteryStore.Namespace.Action) -> Bool + ) { + self.expectation = expectation + self.matcher = matcher + } + + func handle(_ actions: [BatteryStore.Namespace.Action]) { + guard expectation.isInverted == false else { return } + if actions.contains(where: matcher) { + expectation.fulfill() + } + } +} +#endif diff --git a/StreamVideoTests/Utils/Battery/BatteryStore_Tests.swift b/StreamVideoTests/Utils/Battery/BatteryStore_Tests.swift new file mode 100644 index 000000000..546a923a9 --- /dev/null +++ b/StreamVideoTests/Utils/Battery/BatteryStore_Tests.swift @@ -0,0 +1,71 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +@testable import StreamVideo +import XCTest + +final class BatteryStore_Tests: XCTestCase, @unchecked Sendable { + + private var store: Store! + private var subject: BatteryStore! + + override func setUp() async throws { + try await super.setUp() + store = BatteryStore.Namespace.store( + initialState: .init( + isMonitoringEnabled: true, + state: .unknown, + level: 0 + ), + reducers: BatteryStore.Namespace.reducers(), + middleware: [] + ) + subject = BatteryStore(store: store) + + await wait(for: 0.5) + } + + override func tearDown() { + subject = nil + store = nil + super.tearDown() + } + + // MARK: - Encoding + + func test_encode_capturesCurrentState() async throws { + let task = subject.dispatch([ + .setState(.charging), + .setLevel(0.91) + ]) + try await task.result() + + let currentState = subject.state + XCTAssertTrue(currentState.isMonitoringEnabled) + XCTAssertEqual(currentState.level, 91) + XCTAssertEqual(currentState.state, .charging) + } + + // MARK: - Level Handling + + func test_setLevel_roundsAndClampsValues() async throws { + let midTask = subject.dispatch([.setLevel(0.496)]) + try await midTask.result() + XCTAssertEqual(store.state.level, 50) + + let negativeTask = subject.dispatch([.setLevel(-0.5)]) + try await negativeTask.result() + XCTAssertEqual(store.state.level, 0) + + let highTask = subject.dispatch([.setLevel(1.75)]) + try await highTask.result() + XCTAssertEqual(store.state.level, 100) + } +} + +private struct BatteryStoreSnapshot: Decodable, Equatable { + let isMonitoringEnabled: Bool + let state: String + let level: Int +} diff --git a/StreamVideoTests/Utils/PermissionStore/Namespace/Reducers/PermissionStore_DefaultReducerTests.swift b/StreamVideoTests/Utils/PermissionStore/Namespace/Reducers/PermissionStore_DefaultReducerTests.swift index 8186613f4..90c01b9c2 100644 --- a/StreamVideoTests/Utils/PermissionStore/Namespace/Reducers/PermissionStore_DefaultReducerTests.swift +++ b/StreamVideoTests/Utils/PermissionStore/Namespace/Reducers/PermissionStore_DefaultReducerTests.swift @@ -19,15 +19,15 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: setMicrophonePermission - func test_reducer_setMicrophonePermission_granted_returnsExpectedState() throws { - try assertState( + func test_reducer_setMicrophonePermission_granted_returnsExpectedState() async throws { + try await assertState( action: .setMicrophonePermission(.granted), validation: { $0.microphonePermission == .granted } ) } - func test_reducer_setMicrophonePermission_denied_returnsExpectedState() throws { - try assertState( + func test_reducer_setMicrophonePermission_denied_returnsExpectedState() async throws { + try await assertState( action: .setMicrophonePermission(.denied), validation: { $0.microphonePermission == .denied } ) @@ -35,8 +35,8 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: requestMicrophonePermission - func test_reducer_requestMicrophonePermission_returnsExpectedState() throws { - try assertState( + func test_reducer_requestMicrophonePermission_returnsExpectedState() async throws { + try await assertState( action: .requestMicrophonePermission, validation: { $0.microphonePermission == .requesting } ) @@ -44,15 +44,15 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: setCameraPermission - func test_reducer_setCameraPermission_granted_returnsExpectedState() throws { - try assertState( + func test_reducer_setCameraPermission_granted_returnsExpectedState() async throws { + try await assertState( action: .setCameraPermission(.granted), validation: { $0.cameraPermission == .granted } ) } - func test_reducer_setCameraPermission_denied_returnsExpectedState() throws { - try assertState( + func test_reducer_setCameraPermission_denied_returnsExpectedState() async throws { + try await assertState( action: .setCameraPermission(.denied), validation: { $0.cameraPermission == .denied } ) @@ -60,8 +60,8 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: requestCameraPermission - func test_reducer_requestCameraPermission_returnsExpectedState() throws { - try assertState( + func test_reducer_requestCameraPermission_returnsExpectedState() async throws { + try await assertState( action: .requestCameraPermission, validation: { $0.cameraPermission == .requesting } ) @@ -69,15 +69,15 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: setPushNotificationPermission - func test_reducer_setPushNotificationPermission_granted_returnsExpectedState() throws { - try assertState( + func test_reducer_setPushNotificationPermission_granted_returnsExpectedState() async throws { + try await assertState( action: .setPushNotificationPermission(.granted), validation: { $0.pushNotificationPermission == .granted } ) } - func test_reducer_setPushNotificationPermission_denied_returnsExpectedState() throws { - try assertState( + func test_reducer_setPushNotificationPermission_denied_returnsExpectedState() async throws { + try await assertState( action: .setPushNotificationPermission(.denied), validation: { $0.pushNotificationPermission == .denied } ) @@ -85,8 +85,8 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable // MARK: requestPushNotificationPermission - func test_reducer_requestPushNotificationPermission_returnsExpectedState() throws { - try assertState( + func test_reducer_requestPushNotificationPermission_returnsExpectedState() async throws { + try await assertState( action: .requestPushNotificationPermission([]), validation: { $0.pushNotificationPermission == .requesting } ) @@ -100,8 +100,8 @@ final class PermissionStore_DefaultReducerTests: XCTestCase, @unchecked Sendable function: StaticString = #function, line: UInt = #line, validation: (PermissionStore.Namespace.State) -> Bool - ) throws { - let actual = try subject.reduce( + ) async throws { + let actual = try await subject.reduce( state: .initial, action: action, file: file, diff --git a/StreamVideoTests/Utils/Store/StoreTask_Tests.swift b/StreamVideoTests/Utils/Store/StoreTask_Tests.swift index 05df10e74..d67dfc584 100644 --- a/StreamVideoTests/Utils/Store/StoreTask_Tests.swift +++ b/StreamVideoTests/Utils/Store/StoreTask_Tests.swift @@ -136,7 +136,7 @@ private final class TaskTestReducer: Reducer, @unchecked Send file: StaticString, function: StaticString, line: UInt - ) throws -> TaskTestState { + ) async throws -> TaskTestState { var newState = state switch action { diff --git a/StreamVideoTests/Utils/Store/Store_ConcurrencyTests.swift b/StreamVideoTests/Utils/Store/Store_ConcurrencyTests.swift index c51aa3d84..fb9cb7e1b 100644 --- a/StreamVideoTests/Utils/Store/Store_ConcurrencyTests.swift +++ b/StreamVideoTests/Utils/Store/Store_ConcurrencyTests.swift @@ -349,7 +349,7 @@ private final class ConcurrencyTestReducer: Reducer, @ file: StaticString, function: StaticString, line: UInt - ) throws -> ConcurrencyTestState { + ) async throws -> ConcurrencyTestState { var newState = state switch action { @@ -393,7 +393,7 @@ private final class IncrementReducer: Reducer, @unchec file: StaticString, function: StaticString, line: UInt - ) throws -> ConcurrencyTestState { + ) async throws -> ConcurrencyTestState { var newState = state if case .increment = action { diff --git a/StreamVideoTests/Utils/Store/Store_PerformanceTests.swift b/StreamVideoTests/Utils/Store/Store_PerformanceTests.swift index 04e8fa0d2..23315b701 100644 --- a/StreamVideoTests/Utils/Store/Store_PerformanceTests.swift +++ b/StreamVideoTests/Utils/Store/Store_PerformanceTests.swift @@ -151,10 +151,11 @@ final class Store_PerformanceTests: XCTestCase, @unchecked Sendable { measure { for i in 0.., @ file: StaticString, function: StaticString, line: UInt - ) throws -> PerformanceTestState { + ) async throws -> PerformanceTestState { var newState = state switch action { diff --git a/StreamVideoTests/Utils/Store/Store_RaceConditionTests.swift b/StreamVideoTests/Utils/Store/Store_RaceConditionTests.swift index 1e404058a..5db583534 100644 --- a/StreamVideoTests/Utils/Store/Store_RaceConditionTests.swift +++ b/StreamVideoTests/Utils/Store/Store_RaceConditionTests.swift @@ -311,7 +311,7 @@ private final class RaceTestReducer: Reducer, @unchecked Send file: StaticString, function: StaticString, line: UInt - ) throws -> RaceTestState { + ) async throws -> RaceTestState { var newState = state switch action { diff --git a/StreamVideoTests/Utils/Store/Store_Tests.swift b/StreamVideoTests/Utils/Store/Store_Tests.swift index 7093871e2..eb3b7f4f2 100644 --- a/StreamVideoTests/Utils/Store/Store_Tests.swift +++ b/StreamVideoTests/Utils/Store/Store_Tests.swift @@ -93,7 +93,7 @@ private final class TestStoreReducer: Reducer, @unchecked Se file: StaticString, function: StaticString, line: UInt - ) throws -> TestStoreState { + ) async throws -> TestStoreState { defer { timesCalled += 1 } var updatedState = state diff --git a/StreamVideoTests/Utils/Timers/TimerPublisher_Tests.swift b/StreamVideoTests/Utils/Timers/TimerPublisher_Tests.swift index cc9d206ed..0d4798bbe 100644 --- a/StreamVideoTests/Utils/Timers/TimerPublisher_Tests.swift +++ b/StreamVideoTests/Utils/Timers/TimerPublisher_Tests.swift @@ -64,7 +64,6 @@ final class TimerPublisher_Tests: XCTestCase, @unchecked Sendable { let expectation = expectation(description: "Should receive values after resubscription") - LogConfig.level = .debug var cancellable = subject .log(.debug) { "Received value: \($0.millisecondsSince1970)" } .sink { [weak self] in self?.receivedDates.append($0) } diff --git a/StreamVideoUIKit-XCFramework.podspec b/StreamVideoUIKit-XCFramework.podspec index 645c643de..73a9de9f5 100644 --- a/StreamVideoUIKit-XCFramework.podspec +++ b/StreamVideoUIKit-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideoUIKit-XCFramework' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo UIKit Video Components' spec.description = 'StreamVideoUIKit SDK offers flexible UIKit components able to display data provided by StreamVideo SDK.' diff --git a/StreamVideoUIKit.podspec b/StreamVideoUIKit.podspec index 1b3f1a304..0e000ee81 100644 --- a/StreamVideoUIKit.podspec +++ b/StreamVideoUIKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamVideoUIKit' - spec.version = '1.34.1' + spec.version = '1.34.2' spec.summary = 'StreamVideo UIKit Video Components' spec.description = 'StreamVideoUIKit SDK offers flexible UIKit components able to display data provided by StreamVideo SDK.' diff --git a/SwiftUIDemoAppUITests/Tests/CallFlow_PerformanceTests.swift b/SwiftUIDemoAppUITests/Tests/CallFlow_PerformanceTests.swift new file mode 100644 index 000000000..6fc3454b7 --- /dev/null +++ b/SwiftUIDemoAppUITests/Tests/CallFlow_PerformanceTests.swift @@ -0,0 +1,60 @@ +// +// Copyright © 2025 Stream.io Inc. All rights reserved. +// + +import Foundation +import XCTest + +final class CallFlow_PerformanceTests: StreamTestCase { + + private let callDuration: TimeInterval = 1 * 60 + private let app = XCUIApplication() + + override func setUpWithError() throws { + launchApp = false + try super.setUpWithError() + } + + @MainActor + func test_performance_with4Participants() throws { + try XCTSkipIf(TestRunnerEnvironment.isCI, "https://linear.app/stream/issue/IOS-1220/automate-call-performance-testing") + // Reduce noise: fewer iterations for deterministic flows + let options = XCTMeasureOptions() + options.iterationCount = 3 + options.invocationOptions = [.manuallyStart, .manuallyStop] + + measure( + metrics: [ + XCTMemoryMetric(application: app) + ], + options: options + ) { + app.launch() + app.activate() + + startMeasuring() + WHEN("user starts a new call") { + userRobot + .waitForAutoLogin() + .startCall(callId, waitForCompletion: false) + } + + // ensure AUT is foreground + app.activate() + idle(for: callDuration) + + stopMeasuring() + app.terminate() + } + } +} + +extension XCTestCase { + /// Non-blocking idle that won't freeze the app under test. + func idle(for seconds: TimeInterval, file: StaticString = #file, line: UInt = #line) { + let exp = expectation(description: "Idle \(seconds)s") + // Intentionally never fulfill. We expect a timeout. + let result = XCTWaiter.wait(for: [exp], timeout: seconds) + XCTAssertEqual(result, .timedOut, "Idle wait was interrupted", file: file, line: line) + } +} diff --git a/SwiftUIDemoAppUITests/Tests/CallViewsTests.swift b/SwiftUIDemoAppUITests/Tests/CallViewsTests.swift index d985d2f28..5d39dbca5 100644 --- a/SwiftUIDemoAppUITests/Tests/CallViewsTests.swift +++ b/SwiftUIDemoAppUITests/Tests/CallViewsTests.swift @@ -147,8 +147,9 @@ final class CallViewsTests: StreamTestCase { THEN("there are \(participants) participants on the call") { userRobot .assertCallControls() - .assertSpotlightView(with: participants - - 1 + .assertSpotlightView( + with: participants - + 1 ) // We get one less due to the LazyHStack that initializes only a few items after the visible ones } WHEN("user enables fullscreen view") { diff --git a/TestTools/TestData/StreamVideoTestResources.swift b/TestTools/TestData/StreamVideoTestResources.swift index 506105f29..67018d567 100644 --- a/TestTools/TestData/StreamVideoTestResources.swift +++ b/TestTools/TestData/StreamVideoTestResources.swift @@ -14,10 +14,10 @@ extension Bundle { let candidates = [ // Bundle should be present here when the package is linked into an App. Bundle.main.resourceURL, - + // Bundle should be present here when the package is linked into a framework. Bundle(for: StreamVideoTestResources.self).resourceURL, - + // For command-line tools. Bundle.main.bundleURL ] diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 2a9c99078..353d43803 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -776,7 +776,7 @@ lane :update_img_shields_sdk_sizes do |options| ) end -def frameworks_sizes +private_lane :frameworks_sizes do root_dir = 'Build/SDKSize' archive_dir = "#{root_dir}/DemoApp.xcarchive" @@ -786,9 +786,12 @@ def frameworks_sizes gym( scheme: 'DemoAppUIKit', + configuration: 'Release', archive_path: archive_dir, export_method: 'ad-hoc', - export_options: 'fastlane/sdk_size_export_options.plist' + export_options: 'fastlane/sdk_size_export_options.plist', + derived_data_path: derived_data_path, + cloned_source_packages_path: source_packages_path ) frameworks_path = "../#{archive_dir}/Products/Applications/DemoAppUIKit.app/Frameworks" @@ -838,3 +841,20 @@ lane :update_webrtc do |options| github_repo: github_repo ) end + +lane :size_analyze do + next unless is_check_required(sources: sources_matrix[:size], force_check: @force_check) + + gym( + scheme: 'DemoAppUIKit', + configuration: 'Release', + skip_archive: true, + skip_package_ipa: true, + skip_package_pkg: true, + skip_codesigning: true, + derived_data_path: derived_data_path, + cloned_source_packages_path: source_packages_path + ) + + show_detailed_sdk_size(sdk_names: sdk_names, threshold: 42) +end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile index 05b95c430..a3db622f0 100644 --- a/fastlane/Pluginfile +++ b/fastlane/Pluginfile @@ -1,3 +1,4 @@ gem 'fastlane-plugin-versioning' gem 'fastlane-plugin-create_xcframework' -gem 'fastlane-plugin-stream_actions', '0.3.90' +gem 'fastlane-plugin-stream_actions', '0.3.101' +gem 'fastlane-plugin-xcsize', '1.1.0'