Skip to content

Commit 07537e0

Browse files
authored
Merge branch 'develop' into fix/ios-15-failing-tests
2 parents 50d1ef4 + abc0c81 commit 07537e0

File tree

27 files changed

+742
-88
lines changed

27 files changed

+742
-88
lines changed

.github/workflows/vale-doc-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- uses: actions/checkout@v4.1.1
17-
- uses: errata-ai/vale-action@reviewdog
17+
- uses: errata-ai/vale-action@v2.1.0
1818
with:
1919
# added, diff_context, file, nofilter
2020
filter_mode: nofilter

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
### ✅ Added
88
- You can now provide the incoming video quality setting for some or all participants [#571](https://github.com/GetStream/stream-video-swift/pull/571)
9+
- You can now set the time a user can remain in the call - after their connection disrupted - while waiting for their network connection to recover [#573](https://github.com/GetStream/stream-video-swift/pull/573)
910

1011
# [1.13.0](https://github.com/GetStream/stream-video-swift/releases/tag/1.13.0)
1112
_October 08, 2024_

DemoApp/Sources/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
9999
.current()
100100
.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
101101
if granted {
102-
DispatchQueue.main.async {
102+
Task { @MainActor in
103103
UIApplication.shared.registerForRemoteNotifications()
104104
}
105105
}

DemoApp/Sources/Components/AppEnvironment.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,36 @@ extension AppEnvironment {
442442

443443
static var autoLeavePolicy: AutoLeavePolicy = .default
444444
}
445+
446+
extension AppEnvironment {
447+
448+
enum DisconnectionTimeout: Hashable, Debuggable {
449+
case never
450+
case twoMinutes
451+
case custom(TimeInterval)
452+
453+
var title: String {
454+
switch self {
455+
case .never:
456+
return "Never"
457+
case .twoMinutes:
458+
return "2'"
459+
case let .custom(value):
460+
return "\(value)\""
461+
}
462+
}
463+
464+
var duration: TimeInterval {
465+
switch self {
466+
case .never:
467+
return 0
468+
case .twoMinutes:
469+
return 2 * 60
470+
case let .custom(value):
471+
return value
472+
}
473+
}
474+
}
475+
476+
static var disconnectionTimeout: DisconnectionTimeout = .never
477+
}

DemoApp/Sources/Components/Feedback/DemoFeedbackView.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct DemoFeedbackView: View {
1616
@State private var comment: String = ""
1717
@State private var rating: Int = 5
1818
@State private var isSubmitting = false
19+
@State private var toast: Toast?
1920

2021
private weak var call: Call?
2122
private var dismiss: () -> Void
@@ -121,6 +122,20 @@ struct DemoFeedbackView: View {
121122
.padding(.horizontal)
122123
}
123124
.withModalNavigationBar(title: "", closeAction: dismiss)
125+
.toastView(toast: $toast)
126+
.onAppear { checkIfDisconnectionErrorIsAvailable() }
127+
}
128+
129+
// MARK: - Private helpers
130+
131+
@MainActor
132+
func checkIfDisconnectionErrorIsAvailable() {
133+
if call?.state.disconnectionError is ClientError.NetworkNotAvailable {
134+
toast = .init(
135+
style: .error,
136+
message: "Your call was ended because it seems your internet connection is down."
137+
)
138+
}
124139
}
125140
}
126141

DemoApp/Sources/Components/Router.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ final class Router: ObservableObject {
226226
appState.userState = .loggedIn
227227
appState.streamVideo = streamVideo
228228
ReactionsAdapter.currentValue.streamVideo = streamVideo
229+
_ = DemoStatsAdapter.currentValue
229230

230231
let utils = UtilsKey.currentValue
231232
utils.userListProvider = appState

DemoApp/Sources/Views/CallView/CallingView/DemoCallingViewModifier.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct DemoCallingViewModifier: ViewModifier {
7272
}
7373
.onReceive(appState.$activeCall) { call in
7474
viewModel.setActiveCall(call)
75+
call?.setDisconnectionTimeout(AppEnvironment.disconnectionTimeout.duration)
7576
}
7677
.onReceive(appState.$userState) { userState in
7778
if userState == .notLoggedIn {

DemoApp/Sources/Views/Login/DebugMenu.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ struct DebugMenu: View {
7676
didSet { AppEnvironment.callExpiration = callExpiration }
7777
}
7878

79+
@State private var disconnectionTimeout: AppEnvironment.DisconnectionTimeout = AppEnvironment.disconnectionTimeout {
80+
didSet { AppEnvironment.disconnectionTimeout = disconnectionTimeout }
81+
}
82+
7983
@State private var isLogsViewerVisible: Bool = false
8084

8185
@State private var presentsCustomEnvironmentSetup: Bool = false
@@ -86,6 +90,9 @@ struct DebugMenu: View {
8690
@State private var customTokenExpirationValue: Int = 0
8791
@State private var presentsCustomTokenExpiration: Bool = false
8892

93+
@State private var customDisconnectionTimeoutValue: TimeInterval = 0
94+
@State private var presentsCustomDisconnectionTimeout: Bool = false
95+
8996
@State private var autoLeavePolicy: AppEnvironment.AutoLeavePolicy = AppEnvironment.autoLeavePolicy {
9097
didSet { AppEnvironment.autoLeavePolicy = autoLeavePolicy }
9198
}
@@ -149,6 +156,13 @@ struct DebugMenu: View {
149156
label: "Auto Leave policy"
150157
) { self.autoLeavePolicy = $0 }
151158

159+
makeMenu(
160+
for: [.never, .twoMinutes],
161+
currentValue: disconnectionTimeout,
162+
additionalItems: { customDisconnectionTimeoutView },
163+
label: "Disconnection Timeout"
164+
) { self.disconnectionTimeout = $0 }
165+
152166
makeMenu(
153167
for: [.visible, .hidden],
154168
currentValue: performanceTrackerVisibility,
@@ -213,6 +227,14 @@ struct DebugMenu: View {
213227
transformer: { Int($0) ?? 0 },
214228
action: { self.tokenExpiration = .custom(customTokenExpirationValue) }
215229
)
230+
.alertWithTextField(
231+
title: "Enter disconnection timeout in seconds",
232+
placeholder: "Interval",
233+
presentationBinding: $presentsCustomDisconnectionTimeout,
234+
valueBinding: $customDisconnectionTimeoutValue,
235+
transformer: { TimeInterval($0) ?? 0 },
236+
action: { self.disconnectionTimeout = .custom(customDisconnectionTimeoutValue) }
237+
)
216238
}
217239

218240
@ViewBuilder
@@ -290,6 +312,31 @@ struct DebugMenu: View {
290312
}
291313
}
292314

315+
@ViewBuilder
316+
private var customDisconnectionTimeoutView: some View {
317+
if case let .custom(value) = AppEnvironment.disconnectionTimeout {
318+
Button {
319+
presentsCustomDisconnectionTimeout = true
320+
} label: {
321+
Label {
322+
Text("Custom (\(value)\")")
323+
} icon: {
324+
Image(systemName: "checkmark")
325+
}
326+
}
327+
} else {
328+
Button {
329+
presentsCustomDisconnectionTimeout = true
330+
} label: {
331+
Label {
332+
Text("Custom")
333+
} icon: {
334+
EmptyView()
335+
}
336+
}
337+
}
338+
}
339+
293340
@ViewBuilder
294341
private func makeMenu<Item: Debuggable>(
295342
for items: [Item],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// Copyright © 2024 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Combine
6+
import StreamVideo
7+
8+
final class DemoStatsAdapter {
9+
10+
@Injected(\.streamVideo) private var streamVideo
11+
12+
private var activeCallCancellable: AnyCancellable?
13+
private var callStatsReportCancellable: AnyCancellable?
14+
15+
@Published private(set) var reports: [CallStatsReport] = []
16+
17+
init() {
18+
activeCallCancellable = streamVideo
19+
.state
20+
.$activeCall
21+
.sinkTask { @MainActor [weak self] in self?.didUpdateActiveCall($0) }
22+
}
23+
24+
@MainActor
25+
private func didUpdateActiveCall(_ call: Call?) {
26+
reports = []
27+
callStatsReportCancellable?.cancel()
28+
callStatsReportCancellable = call?
29+
.state
30+
.$statsReport
31+
.sink { [weak self] in
32+
if let report = $0 { self?.reports.append(report) }
33+
}
34+
}
35+
}
36+
37+
extension DemoStatsAdapter: InjectionKey {
38+
static var currentValue: DemoStatsAdapter = .init()
39+
}
40+
41+
extension InjectedValues {
42+
var demoStatsAdapter: DemoStatsAdapter {
43+
get { Self[DemoStatsAdapter.self] }
44+
set { Self[DemoStatsAdapter.self] = newValue }
45+
}
46+
}

DemoApp/Sources/Views/StatsView/DemoStatsView.swift

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct DemoStatsView: View {
2727
title: "Call Latency",
2828
description: "Very high latency values may reduce call quality, cause lag, and make the call less enjoyable."
2929
) {
30-
DemoLatencyChartView(viewModel)
30+
DemoLatencyChartView()
3131
.frame(height: 150)
3232
}
3333
.withoutListSeparator()
@@ -382,7 +382,8 @@ private struct DemoLatencyChartView: View {
382382

383383
private final class DemoLatencyChartViewModel: ObservableObject {
384384

385-
@ObservedObject private var viewModel: CallViewModel
385+
@Injected(\.demoStatsAdapter) private var demoStatsAdapter
386+
386387
private var cancellable: AnyCancellable?
387388
private let visibleItems = 8
388389

@@ -396,30 +397,23 @@ private struct DemoLatencyChartView: View {
396397
@Published var values: [(offset: Int, element: Double)] = []
397398
@Published var visibleRange: ClosedRange<Int> = 0...0
398399

399-
init(
400-
viewModel: CallViewModel
401-
) {
402-
self.viewModel = viewModel
400+
init() {
403401
values = []
404-
cancellable = viewModel
405-
.call?
406-
.state
407-
.$statsReport
402+
cancellable = demoStatsAdapter
403+
.$reports
408404
.receive(on: DispatchQueue.global(qos: .utility))
409-
.compactMap { $0?.publisherStats.averageRoundTripTimeInMs }
405+
.map { $0.map(\.publisherStats.averageRoundTripTimeInMs) }
410406
.receive(on: DispatchQueue.main)
411-
.sink { [weak self] in self?.internalValues.append($0) }
407+
.assign(to: \.internalValues, onWeak: self)
412408
}
413409
}
414410

415411
@Injected(\.colors) private var colors
416412

417413
@StateObject private var viewModel: DemoLatencyChartViewModel
418414

419-
init(
420-
_ callViewModel: CallViewModel
421-
) {
422-
_viewModel = .init(wrappedValue: .init(viewModel: callViewModel))
415+
init() {
416+
_viewModel = .init(wrappedValue: .init())
423417
}
424418

425419
var body: some View {
@@ -439,7 +433,6 @@ private struct DemoLatencyChartView: View {
439433
.foregroundStyle(colors.accentGreen)
440434
}
441435
}
442-
.aspectRatio(1, contentMode: .fill)
443436
.chartXScale(domain: viewModel.visibleRange)
444437
.chartXAxis(.hidden)
445438
.padding(.vertical)

DocumentationTests/DocumentationTests/DocumentationTests.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616
400D91D12B63DEA200EBA47D /* 04-camera-and-microphone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400D91D02B63DEA200EBA47D /* 04-camera-and-microphone.swift */; };
1717
400D91D32B63DFA500EBA47D /* 06-querying-calls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400D91D22B63DFA500EBA47D /* 06-querying-calls.swift */; };
1818
400D91D52B63E27300EBA47D /* 07-dependency-injection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400D91D42B63E27300EBA47D /* 07-dependency-injection.swift */; };
19-
4029E95E2CB94EAE00E1D571 /* 21-manual-quality-selection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4029E95D2CB94EA700E1D571 /* 21-manual-quality-selection.swift */; };
19+
4029E95E2CB94EAE00E1D571 /* 22-manual-quality-selection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4029E95D2CB94EA700E1D571 /* 22-manual-quality-selection.swift */; };
2020
404CAED82B8E3874007087BC /* 06-apply-video-filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404CAED72B8E3874007087BC /* 06-apply-video-filters.swift */; };
2121
4068C1252B67C056006B0BEE /* 03-callkit-integration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4068C1242B67C056006B0BEE /* 03-callkit-integration.swift */; };
2222
408CE0F52BD91B490052EC3A /* 19-transcriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 408CE0F42BD91B490052EC3A /* 19-transcriptions.swift */; };
23+
409774B02CC19F5500E0D3EE /* 23-network-disruption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409774AF2CC19F4900E0D3EE /* 23-network-disruption.swift */; };
2324
409C39692B67CC5C0090044C /* 04-screensharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409C39682B67CC5C0090044C /* 04-screensharing.swift */; };
2425
409C396B2B67CD0B0090044C /* 05-picture-in-picture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409C396A2B67CD0B0090044C /* 05-picture-in-picture.swift */; };
2526
409C396D2B67CD780090044C /* 08-recording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409C396C2B67CD780090044C /* 08-recording.swift */; };
@@ -94,10 +95,11 @@
9495
400D91D02B63DEA200EBA47D /* 04-camera-and-microphone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "04-camera-and-microphone.swift"; sourceTree = "<group>"; };
9596
400D91D22B63DFA500EBA47D /* 06-querying-calls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "06-querying-calls.swift"; sourceTree = "<group>"; };
9697
400D91D42B63E27300EBA47D /* 07-dependency-injection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "07-dependency-injection.swift"; sourceTree = "<group>"; };
97-
4029E95D2CB94EA700E1D571 /* 21-manual-quality-selection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "21-manual-quality-selection.swift"; sourceTree = "<group>"; };
98+
4029E95D2CB94EA700E1D571 /* 22-manual-quality-selection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "22-manual-quality-selection.swift"; sourceTree = "<group>"; };
9899
404CAED72B8E3874007087BC /* 06-apply-video-filters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "06-apply-video-filters.swift"; sourceTree = "<group>"; };
99100
4068C1242B67C056006B0BEE /* 03-callkit-integration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "03-callkit-integration.swift"; sourceTree = "<group>"; };
100101
408CE0F42BD91B490052EC3A /* 19-transcriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "19-transcriptions.swift"; sourceTree = "<group>"; };
102+
409774AF2CC19F4900E0D3EE /* 23-network-disruption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "23-network-disruption.swift"; sourceTree = "<group>"; };
101103
409C39682B67CC5C0090044C /* 04-screensharing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "04-screensharing.swift"; sourceTree = "<group>"; };
102104
409C396A2B67CD0B0090044C /* 05-picture-in-picture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "05-picture-in-picture.swift"; sourceTree = "<group>"; };
103105
409C396C2B67CD780090044C /* 08-recording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "08-recording.swift"; sourceTree = "<group>"; };
@@ -274,7 +276,8 @@
274276
408CE0F42BD91B490052EC3A /* 19-transcriptions.swift */,
275277
40F18B8F2BEBC97F00ADF76E /* 18-call-quality-rating.swift */,
276278
40F290AC2BDFB3CA00DCF136 /* 20-noise-cancellation.swift */,
277-
4029E95D2CB94EA700E1D571 /* 21-manual-quality-selection.swift */,
279+
4029E95D2CB94EA700E1D571 /* 22-manual-quality-selection.swift */,
280+
409774AF2CC19F4900E0D3EE /* 23-network-disruption.swift */,
278281
);
279282
path = "05-ui-cookbook";
280283
sourceTree = "<group>";
@@ -465,12 +468,13 @@
465468
4068C1252B67C056006B0BEE /* 03-callkit-integration.swift in Sources */,
466469
40FFDC762B63F7D6004DA7A2 /* ChatGloballyUsedVariables.swift in Sources */,
467470
84BA15AE2CA2EF420018DC51 /* 07-querying-call-members.swift in Sources */,
471+
409774B02CC19F5500E0D3EE /* 23-network-disruption.swift in Sources */,
468472
40FFDC672B63F430004DA7A2 /* 04-connection-quality-indicator.swift in Sources */,
469473
40B468982B67B6DF009B5B3E /* 01-deeplinking.swift in Sources */,
470474
40FFDC3B2B63E493004DA7A2 /* 10-view-slots.swift in Sources */,
471475
40FFDC442B63E95D004DA7A2 /* 14-swiftui-vs-uikit.swift in Sources */,
472476
40FFDC872B63FEAE004DA7A2 /* 05-incoming-call.swift in Sources */,
473-
4029E95E2CB94EAE00E1D571 /* 21-manual-quality-selection.swift in Sources */,
477+
4029E95E2CB94EAE00E1D571 /* 22-manual-quality-selection.swift in Sources */,
474478
40FFDC942B6401CC004DA7A2 /* 07-video-fallback.swift in Sources */,
475479
400D91C72B63D96800EBA47D /* 03-quickstart.swift in Sources */,
476480
40FFDC9E2B64063D004DA7A2 /* 12-connection-unstable.swift in Sources */,

0 commit comments

Comments
 (0)