Handle Video Stats
In this step you implement OTSubscriberKitNetworkStatsDelegate to diagnose stuttering and surface network health in your app.
| What you'll monitor | How it is delivered | How it can be used |
|---|---|---|
videoPacketsLost / videoBytesReceived |
Periodic — videoNetworkStatsUpdated |
Confirm the receive path is struggling (stutter, freezes) |
transport (local downlink bandwidth) |
Periodic — mediaLinkStatsUpdated |
Suggest “Your connection looks weak” |
remotePublisherTransport (remote uplink) |
Periodic — mediaLinkStatsUpdated |
Suggest “The other participant’s network may be unstable” |
networkDegradationSource |
Periodic and on change | Pick local vs remote messaging |
| Significant network change | Event — networkConditionChanged |
Banners, toasts, support logs |
iOS-specific: Periodic stats and network condition events are methods on the same OTSubscriberKitNetworkStatsDelegate—not a separate listener type.
Adopt the delegate in an extension:
extension VonageVideoManager: OTSubscriberKitNetworkStatsDelegate {
// ...
}
1. Handle Subscriber Video Stats
func subscriber(_ subscriber: OTSubscriberKit, videoNetworkStatsUpdated stats: OTSubscriberKitVideoNetworkStats) {
print("videoBytesReceived=\(stats.videoBytesReceived)")
print("videoPacketsLost=\(stats.videoPacketsLost)")
print("videoPacketsReceived=\(stats.videoPacketsReceived)")
DispatchQueue.main.async {
self.latestObservabilityStats = ObservabilityStats.fromVideoStats(
stats,
mediaLink: self.latestMediaLinkSnapshot
)
}
}
Callbacks may arrive off the main queue. Update @Published overlay state on the main queue.
2. Handle Periodic Media Link Stats
func subscriber(_ subscriber: OTSubscriberKit, mediaLinkStatsUpdated mediaLinkStats: OTSubscriberKitMediaLinkStats) {
applyMediaLinkStats(mediaLinkStats)
}
3. Handle Network Condition Events
Implement subscriber(_:networkConditionChanged:reason:) for significant changes—see Network condition:
func subscriber(
_ subscriber: OTSubscriberKit,
networkConditionChanged mediaLinkStats: OTSubscriberKitMediaLinkStats,
reason: OTNetworkReason
) {
print("networkConditionChanged reason=\(reason.rawValue)")
applyMediaLinkStats(mediaLinkStats, networkConditionChangeReason: reason)
handleNetworkDegradation(mediaLinkStats)
}
Shared merge helper:
private func applyMediaLinkStats(
_ mediaLinkStats: OTSubscriberKitMediaLinkStats,
networkConditionChangeReason: OTNetworkReason? = nil
) {
let localBandwidth = mediaLinkStats.transport?.connectionEstimatedBandwidth
let remoteBandwidth = mediaLinkStats.remotePublisherTransport?.connectionEstimatedBandwidth
let localCondition = mediaLinkStats.transport?.networkCondition
let remoteCondition = mediaLinkStats.remotePublisherTransport?.networkCondition
DispatchQueue.main.async {
self.latestMediaLinkSnapshot = MediaLinkSnapshot(
localEstimatedBandwidth: localBandwidth,
remoteEstimatedBandwidth: remoteBandwidth,
networkDegradationSource: mediaLinkStats.networkDegradationSource,
localNetworkCondition: localCondition,
remoteNetworkCondition: remoteCondition,
lastNetworkConditionChangeReason: networkConditionChangeReason
)
if var current = self.latestObservabilityStats {
current.merge(mediaLink: self.latestMediaLinkSnapshot)
self.latestObservabilityStats = current
}
}
}
4. React to Degradation on Network Condition Events
private func handleNetworkDegradation(_ mediaLinkStats: OTSubscriberKitMediaLinkStats) {
let localCondition = mediaLinkStats.transport?.networkCondition ?? .unknown
let remoteCondition = mediaLinkStats.remotePublisherTransport?.networkCondition ?? .unknown
switch mediaLinkStats.networkDegradationSource {
case .local:
print("Local network degraded (condition=\(localCondition.rawValue))")
// e.g. show: "Your connection looks weak"
case .remote:
print("Remote publisher network degraded (condition=\(remoteCondition.rawValue))")
// e.g. show: "The other participant's network may be unstable"
case .bothOrUnclear:
print("Degradation unclear — local=\(localCondition.rawValue) remote=\(remoteCondition.rawValue)")
default:
break
}
}
Define ObservabilityStats, MediaLinkSnapshot, and helpers in your project (mirror the Android sample’s model). If remotePublisherTransport stays nil, confirm the remote publisher used settings.senderStatsTrack = true.
Next: Pass merged stats into SwiftUI and render the overlay.
Client Observability
Learn how to use client observability to monitor real-time quality metrics for a video call with Vonage Video SDK.