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 @@
-
+
@@ -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'