Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

iOS Video Application Recommendations and Best Practices


(warning)

Warning

This documentation is for reference only. We are no longer onboarding new customers to Programmable Video. Existing customers can continue to use the product until December 5, 2026(link takes you to an external page).

We recommend migrating your application to the API provided by our preferred video partner, Zoom. We've prepared this migration guide(link takes you to an external page) to assist you in minimizing any service disruption.

This guide provides recommendations and best practices for building an iOS video app using the Twilio Programmable Video iOS SDK.


Contents

contents page anchor

Please take a look at this Developing High Quality Video Applications guide to choose the right ConnectOptions(link takes you to an external page) when initializing the video call for your use case.


Mute and Unmute the Microphone

mute-and-unmute-the-microphone page anchor

When the local participant mutes the microphone, it is recommended to unpublish the microphone track instead of disabling it(link takes you to an external page). When the microphone track is only disabled, the orange indicator in the status bar(link takes you to an external page) is still displayed and this could be confusing for users.


_26
var room: Room? // Set when connected to a video room
_26
var micTrack: LocalAudioTrack?
_26
_26
var isLocalMicOn = false {
_26
didSet {
_26
guard oldValue != isLocalMicOn else {
_26
return
_26
}
_26
_26
if isLocalMicOn {
_26
guard let micTrack = LocalAudioTrack(options: nil, enabled: true, name: "mic") else {
_26
return
_26
}
_26
_26
self.micTrack = micTrack
_26
room?.localParticipant?.publishAudioTrack(micTrack)
_26
} else {
_26
guard let micTrack = micTrack else {
_26
return
_26
}
_26
_26
room?.localParticipant?.unpublishAudioTrack(micTrack)
_26
self.micTrack = nil
_26
}
_26
}
_26
}

See the reference iOS video collaboration app(link takes you to an external page) for a similar implementation working in an app.

Turn the Camera On and Off

turn-the-camera-on-and-off page anchor

When the local participant turns off the camera, it is recommended to unpublish the camera track instead of disabling it(link takes you to an external page). Unpublishing the camera track will minimize resources consumed and there is no impact to the user experience.


_35
var room: Room? // Set when connected to a video room
_35
var cameraSource: CameraSource?
_35
var cameraTrack: CameraTrack?
_35
_35
var isLocalCameraOn = false {
_35
didSet {
_35
guard oldValue != isLocalCameraOn else {
_35
return
_35
}
_35
_35
if isLocalCameraOn {
_35
guard
_35
let cameraSource = CameraSource(delegate: self),
_35
let cameraTrack = LocalVideoTrack(source: cameraSource, enabled: true, name: "camera")
_35
let captureDevice = CameraSource.captureDevice(position: .front),
_35
else {
_35
return
_35
}
_35
_35
cameraSource.startCapture(device: captureDevice, completion: nil)
_35
_35
room?.localParticipant?.publishVideoTrack(cameraTrack)
_35
self.cameraSource = cameraSource
_35
self.cameraTrack = cameraTrack
_35
} else {
_35
if let cameraTrack = cameraTrack {
_35
participant?.unpublishVideoTrack(cameraTrack)
_35
}
_35
_35
cameraSource?.stopCapture()
_35
cameraSource = nil
_35
cameraTrack = nil
_35
}
_35
}
_35
}

See the reference iOS video collaboration app(link takes you to an external page) for a similar implementation working in an app.

Display Media Status in the User Interface

display-media-status-in-the-user-interface page anchor

When displaying track status in the user interface, check if the track is enabled. Tracks may be disabled instead of unpublished for some edge cases or to optimize the experience for users on a platform that isn't using the iOS Video SDK.


_19
extension RemoteParticipant {
_19
var isMicOn: Bool {
_19
// Make sure to use the same track name that your app is using to create the microphone track
_19
guard let track = participant.remoteAudioTracks.first(where: { $0.trackName == "mic" }) else {
_19
return false
_19
}
_19
_19
return track.isTrackSubscribed && track.isTrackEnabled
_19
}
_19
_19
var isCameraOn: Bool {
_19
// Make sure to use the same track name that your app is using to create the camera track
_19
guard let track = participant.remoteVideoTracks.first(where: { $0.trackName == "camera" }) else {
_19
return false
_19
}
_19
_19
return track.isTrackSubscribed && track.isTrackEnabled
_19
}
_19
}

See the reference iOS video collaboration app(link takes you to an external page) for a similar implementation working in an app.


Run the App in the Background

run-the-app-in-the-background page anchor

Handle Camera Interruptions

handle-camera-interruptions page anchor

When the app moves to the background, the system will interrupt camera capture. We recommend that you disable the camera track instead of unpublish it, since interruptions can be frequent due to things like notifications. When the camera interruption ends, enable the track again.


_10
var cameraTrack: CameraTrack? // Set when camera is turned on
_10
_10
// CameraSourceDelegate
_10
func cameraSourceWasInterrupted(source: CameraSource, reason: AVCaptureSession.InterruptionReason) {
_10
cameraTrack?.isEnabled = false
_10
}
_10
_10
func cameraSourceInterruptionEnded(source: CameraSource) {
_10
cameraTrack?.isEnabled = true
_10
}

See the reference iOS video collaboration app(link takes you to an external page) for a similar implementation working in an app.

Handle Audio Interruptions

handle-audio-interruptions page anchor

When another app interrupts audio (e.g. receiving a phone call in the Phone app), audio recording and playback in the video app will be interrupted. This could cause your app to be suspended because it is not playing or recording audio. When the app is suspended the app will be disconnected from the video room.


This section lists some of the important errors raised by the iOS Video SDK and provides recommendations on how best to handle them in order to provide the optimal user experience.

These errors are raised when the app is not able to connect to a Room. The app can use the RoomDelegate to receive connection errors. Your app should handle errors that may happen when trying to connect to a Room.


_10
// RoomDelegate function
_10
func roomDidFailToConnect(room: Room, error: Error) {
_10
// Handle error
_10
}

The following table describes the most common connection errors and proposes ways for the application to handle them:

ErrorCodeCauseSolution
SignalingConnectionError53000The client could not establish a connection to Twilio's signaling serverUser should make sure to have a stable internet connection
SignalingServerBusyError53006Twilio's signaling server is too busy to accept new clientsUser should try joining the Room again after some time
RoomMaxParticipantsExceededError53105The Room cannot allow in any more Participants to joinYour app should notify the user that the Room is full
RoomNotFoundError53106The client attempted to connect to a Room that does not existIf ad-hoc Room creation is disabled, then your app should make sure that the Room is created using the REST API before clients attempt to join
MediaConnectionError53405The client failed to establish a media connection with the Room1. User should make sure to have a stable internet connection 2. If the user is behind a firewall, then it should allow media traffic to and from Twilio to go through

These errors are raised when the app is inadvertently disconnected from the Room. The app can use the RoomDelegate to receive disconnect errors.


_10
// RoomDelegate function
_10
func roomDidDisconnect(room: Room, error: Error?) {
_10
if let error = error {
_10
// Handle error
_10
}
_10
}

The following table describes the most common disconnection errors and proposes ways for the application to handle them:

ErrorCodeCauseSolution
SignalingConnectionDisconnectedError53001The client failed to reconnect to Twilio's signaling server after a network disruption or handoffUser should make sure to have a stable internet connection
SignalingConnectionTimeoutError53002The liveliness checks for the connection to Twilio's signaling server failed, or the current session expiredUser should rejoin the Room
ParticipantDuplicateIdentityError53205Another client joined the Room with the same identityYour app should make sure each client creates an AccessToken with a unique identity string
MediaConnectionError53405The client failed to re-establish its media connection with the Room after a network disruption or handoff1. User should make sure to have a stable internet connection 2. If the user is behind a firewall, then the firewall should allow media traffic to and from Twilio to go through
RoomNotFoundError53106The client tried to reconnect to Twilio's signaling server after a network disruption or handoff, but room they had joined ended while they were disconnectedYour app should notify user that Room has ended

Rate this page: