let call = streamVideo.call(callType: "default", callId: callId)
// Set the disconnection timeout to 60 seconds
call.setDisconnectionTimeout(60)
Managing network disruptions during a call
Summary
This tutorial guides you through using the setDisconnectionTimeout
method within the Call object to manage user disconnections due to network issues. By setting a timeout, users are given a grace period to reconnect before they are removed from the call, ensuring that temporary network disruptions don’t immediately end their participation.
Overview
The setDisconnectionTimeout
method allows you to specify how long a user can remain disconnected before being removed from the call. This is particularly useful when users experience brief network interruptions but can reconnect quickly. By setting a timeout, you ensure that users are only dropped if their disconnection persists beyond the specified duration.
:::note
By default the disconnectionTimeout
is set to 0
, allowing the user either to remain in the call until their connection restores or select to hang up.
:::
Setting Up the Disconnection Timeout
Once the call has been created, you can set a disconnection timeout that defines how long a user can stay disconnected before being dropped. Here’s how to do it:
Inform the user after a disconnection occurs
With that set, we want to make sure to inform the user once they get disconnected due to a network disruption. To do that, we are going to extend the FeedbackView we created here. Specifically we are going to update the DemoFeedbackView
like below:
struct DemoFeedbackView: View {
@Environment(\.openURL) private var openURL
@Injected(\.appearance) private var appearance
@State private var email: String = ""
@State private var comment: String = ""
@State private var rating: Int = 5
@State private var isSubmitting = false
@State private var toast: Toast?
private weak var call: Call?
private var dismiss: () -> Void
private var isSubmitEnabled: Bool { !email.isEmpty && !isSubmitting }
init(_ call: Call, dismiss: @escaping () -> Void) {
self.call = call
self.dismiss = dismiss
}
var body: some View {
ScrollView {
VStack(spacing: 32) {
Image("feedbackLogo")
VStack(spacing: 8) {
Text("How is your call going?")
.font(appearance.fonts.headline)
.foregroundColor(appearance.colors.text)
.lineLimit(1)
Text("All feedback is celebrated!")
.font(appearance.fonts.subheadline)
.foregroundColor(.init(appearance.colors.textLowEmphasis))
.lineLimit(2)
}
.frame(maxWidth: .infinity, alignment: .center)
.multilineTextAlignment(.center)
VStack(spacing: 27) {
VStack(spacing: 16) {
TextField(
"Email Address *",
text: $email
)
.textFieldStyle(DemoTextfieldStyle())
DemoTextEditor(text: $comment, placeholder: "Message")
}
HStack {
Text("Rate Quality")
.font(appearance.fonts.body)
.foregroundColor(.init(appearance.colors.textLowEmphasis))
.frame(maxWidth: .infinity, alignment: .leading)
DemoStarRatingView(rating: $rating)
}
}
HStack {
Button {
resignFirstResponder()
openURL(.init(string: "https://getstream.io/video/#contact")!)
} label: {
Text("Contact Us")
}
.frame(maxWidth: .infinity)
.foregroundColor(appearance.colors.text)
.padding(.vertical, 4)
.clipShape(Capsule())
.overlay(Capsule().stroke(Color(appearance.colors.textLowEmphasis), lineWidth: 1))
Button {
resignFirstResponder()
isSubmitting = true
Task {
do {
try await call?.collectUserFeedback(
rating: rating,
reason: """
\(email)
\(comment)
"""
)
Task { @MainActor in
dismiss()
}
isSubmitting = false
} catch {
log.error(error)
dismiss()
isSubmitting = false
}
}
} label: {
if isSubmitting {
ProgressView()
} else {
Text("Submit")
}
}
.frame(maxWidth: .infinity)
.foregroundColor(appearance.colors.text)
.padding(.vertical, 4)
.background(isSubmitEnabled ? appearance.colors.accentBlue : appearance.colors.lightGray)
.disabled(!isSubmitEnabled)
.clipShape(Capsule())
}
Spacer()
}
.padding(.horizontal)
}
.toastView(toast: $toast)
.onAppear { checkIfDisconnectionErrorIsAvailable() }
}
// MARK: - Private helpers
@MainActor
func checkIfDisconnectionErrorIsAvailable() {
if call?.state.disconnectionError is ClientError.NetworkNotAvailable {
toast = .init(
style: .error,
message: "Your call was ended because it seems your internet connection is down."
)
}
}
}
The parts that we changed here are:
@State private var toast: Toast?
We define a state property for the Toast that is going to be presented to the user..toastView(toast: $toast)
We attach thetoastView
ViewModifier on our view (similar to how we are doing withalert
)..onAppear { checkIfDisconnectionErrorIsAvailable() }
On appear we are checking if there is an error of typeNetworkNotAvailable
and if there, we setup a toast to be presented.checkIfDisconnectionErrorIsAvailable()
We define a method that will do the error checking for us.
Conclusion
By configuring the setDisconnectionTimeout
and handling disconnection errors using the disconnectionError
property, you can provide a more seamless experience for users, allowing them a grace period to reconnect during temporary network issues. Additionally, by integrating user feedback mechanisms, you can give users clear notifications when they have been disconnected due to network problems, helping them understand the issue and take appropriate action. This approach enhances both the reliability of your video calls and user satisfaction, even in less-than-ideal network conditions.