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.
We recommend migrating your application to the API provided by our preferred video partner, Zoom. We've prepared this migration guide to assist you in minimizing any service disruption.
This guide provides you with an overview of the key objects you'll use in the Programmable Video API to build your video application with the Twilio Programmable Video iOS SDK.
Note: If you haven't already done so, then take a look at the Twilio Video iOS QuickStart Application. Once you've played with the QuickStart, come back to this guide for more detail on how to add video to your own app.
If you've worked with WebRTC in the past, you'll find that Programmable Video provides a simple wrapper around WebRTC's lower-level APIs to make it easy to build rich audio and video applications. You can still access lower-level primitives, but that's not required to get started.
Additionally, Programmable Video provides the missing pieces required to use WebRTC to build sophisticated applications: Global STUN/TURN relays, media services for large-scale group conferences and recording, and signaling infrastructure are all included.
Let's start with an overview of the Programmable Video API:
Room
represents a real-time audio, video, and/or screen-share session, and is the basic building block for a Programmable Video application.
Peer-to-peer Room
, media flows directly between participants. Supports up to 10 participants in a mesh topology.
Group Room
, media is routed through Twilio's Media Servers.
Supports up to 50 participants.
Participants
represent client applications that are connected to a Room and sharing audio and/or video media with one another.
Tracks
represent the individual audio and video media streams that are shared within a Room.
LocalTracks
represent the audio and video captured from the local microphone and camera.
RemoteTracks
represent the audio and video tracks from other participants connected to the Room.
The following code samples illustrate common tasks that you as a developer may wish to perform related to a Room and its Participants.
To start using the iOS Programmable Video SDK in your apps, you need to perform a few basic tasks first.
The Twilio Video iOS SDK dynamic framework can be installed using Carthage, CocoaPods or manually, as you prefer. As of the 2.5.2
release, the Twilio Video iOS SDK is also distributed as a static library which can be manually installed.
You can add Programmable Video for iOS by adding the following line to your Cartfile
:
_10github "twilio/twilio-video-ios" ~> 2.12
Then run carthage bootstrap
(or carthage update
if you are updating your SDKs)
On your application targets' General settings page, in the Linked Frameworks and Libraries section, drag and drop each framework you want to use from the Carthage/Build folder on disk.
On your application targets' Build Phases settings tab, click the "+" icon and choose New Run Script Phase. Create a Run Script in which you specify your shell (ex: /bin/sh
), add the following contents to the script area below the shell:
_10/usr/local/bin/carthage copy-frameworks
Add the paths to the frameworks you want to use under Input Files, e.g.:
_10$(SRCROOT)/Carthage/Build/iOS/TwilioVideo.framework
_10source 'https://github.com/CocoaPods/Specs'_10_10platform :ios, '9.0'_10_10target 'TARGET_NAME' do_10 pod 'TwilioVideo', '~> 2.12'_10end
Then run pod install
to install the dependencies to your project.
TwilioVideo.framework
is distributed as a dynamic iOS framework that you can drag and drop into your existing projects.
View all Video iOS Releases here or just download the latest Video dynamic framework here.
Once you've downloaded and unpacked the framework, navigate to your Xcode project's General settings page. Drag and drop TwilioVideo.framework
onto the Embedded Binaries section. Ensure that "Copy items if needed" is checked and press Finish. This will add TwilioVideo.framework
to both the Embedded Binaries and Linked Frameworks and Libraries sections.
Next, you will need to open your project's Linked Frameworks and Libraries configuration. You should see TwilioVideo.framework
there already. Add the following frameworks to that list:
AudioToolbox.framework
VideoToolbox.framework
AVFoundation.framework
CoreTelephony.framework
GLKit.framework
CoreMedia.framework
SystemConfiguration.framework
libc++.tbd
In your Build Settings, you will also need to modify Other Linker Flags
to include -ObjC
.
Before distributing your app to the App Store, you will need to strip the simulator binaries from the embedded framework. Navigate to your target's Build Phases screen and create a new Run Script Phase. Ensure that this new run script phase is after the Embed Frameworks phase. Paste the following command in the script text field:
_10/bin/bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/TwilioVideo.framework/remove_archs"
libTwilioVideo
is distributed as a static iOS library that you can drag and drop into you existing projects.
View all Video iOS Releases here or just download the latest Video static library here.
Once you've downloaded and unpacked the static library, drag and drop the lib
and headers
folders into your project. Ensure that "Copy items if needed" is checked and press Finish. This will add libTwilioVideo.a
to the Linked Frameworks and Libraries section.
Next, you will need to open your project's Linked Frameworks and Libraries configuration. You should see libTwilioVideo.a
there already. Add the following frameworks to that list:
AudioToolbox.framework
VideoToolbox.framework
AVFoundation.framework
CoreTelephony.framework
GLKit.framework
CoreMedia.framework
SystemConfiguration.framework
libc++.tbd
In your Build Settings, you will also need to modify Other Linker Flags
to include -ObjC
.
To allow a connection to a Room to be persisted while an application is running in the background, you must select the Audio, AirPlay, and Picture in Picture
background mode from the Capabilities
project settings page.
The iOS SDK supports iOS 9.0 or higher. It is built for armv7, arm64, x86 and x86_64 architectures with Bitcode slices for armv7 and arm64 devices. Devices based on the A5 SoC (iPhone 4s, iPad 2, iPad Mini 1, iPod Touch 5G) are not supported, and the SDK will not perform well on them.
The TwilioVideo.framework
is built with Xcode 9.4. The framework can successfully be consumed with previous versions of Xcode. However, re-compiling Bitcode when exporting for Ad Hoc or Enterprise distribution requires the use of Xcode 9.x.
API Keys represent credentials to access the Twilio API. They are used for two purposes:
For the purposes of this guide, we will create our API Key from the Twilio Console.
To execute the code samples below, you can use the Testing Tools page in the Twilio Console to generate an Access Token. An Access Token is a short-lived credential used to authenticate your client-side application to Twilio.
In a production application, your back-end server will need to generate an Access Token for every user in your application. An Access Token is a short-lived credential used to authenticate your client-side application to Twilio. Visit the User Identity and Access Token guide to learn more.
Call TwilioVideo.connect()
to connect to a Room from your iOS application. Once connected, you can send and receive audio and video streams with other Participants who are connected to the Room.
_12@IBAction func createARoom(sender: AnyObject) {_12 let connectOptions = TVIConnectOptions(token: accessToken) { (builder) in_12 builder.roomName = "my-room"_12 }_12 room = TwilioVideo.connect(with: connectOptions, delegate: self)_12}_12_12// MARK: TVIRoomDelegate_12_12func didConnectToRoom(room: TVIRoom) {_12 print("Did connect to Room")_12}
You must pass the Access Token when connecting to a Room. You may also optionally pass the following:
The name of the Room specifies which Room you wish to join. If a Room by that name does not already exist, it will be created upon connection. If a Room by that name is already active, you'll be connected to the Room and receive notifications from any other Participants also connected to the same Room. Room names must be unique within an account.
You can also create a Room using the Rooms REST API. Look at the REST API Rooms resource docs for more details.
Example: Create a Room called DailyStandup
_10 curl -XPOST 'https://video.twilio.com/v1/Rooms' \_10 -u '{API Key SID}:{API Secret}' \_10 -d 'UniqueName=DailyStandup'
Note: If you don't specify a Type attribute, then Twilio will create a Group Room by default.
You can also set the room type from the Room Settings page in the Twilio Video Console. Twilio will use the room type set on Room Settings page, when you create a room from the client-side or the REST API.
Note: Twilio will set the Room Type as Group by default on the Room Settings page.
Once a Room is created, Twilio will fire a room-created
webhook event, if the StatusCallback URL is set. You can set the StatusCallback URL on the Room Settings page, if you want create a room from the client-side.
If you create a room using the REST API, then you need to provide a StatusCallback URL while creating the room.
_10 curl -XPOST 'https://video.twilio.com/v1/Rooms' \_10 -u '{API Key SID}:{API Secret}' \_10 -d 'UniqueName=DailyStandup' \_10 -d 'StatusCallback=https://hooks.yoursite.com/room-events' \_10 -d 'StatusCallbackMethod=POST' \_10 -d 'Type=group'
Recordings can be enabled only on Group Rooms. Set Recordings to Enabled
to record participants when they connect to a Group Room.
Recordings can also be enabled on Group Rooms through via the Rest API at Room creation time by setting the RecordParticipantsOnConnect
property to true
.
_10curl -XPOST 'https://video.twilio.com/v1/Rooms' \_10-u 'SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_api_key_secret' \_10-d 'UniqueName=DailyStandup' \_10-d 'Type=group' \_10-d 'RecordParticipantsOnConnect=true' \_10-d 'StatusCallback=http://example.org'
If you'd like to join a Room you know already exists, you handle that exactly the same way as creating a room: just pass the Room name to the connect
method.
Once in a Room, you'll receive a room:participantDidConnect:
callback for each Participant that successfully joins. Querying the remoteParticipants
getter will return any existing Participants who have already joined the Room.
_12@IBAction func joinRoom(sender: AnyObject) {_12 let connectOptions = TVIConnectOptions.init(block: { (builder) in_12 builder.roomName = "existing-room"_12 })_12 room = TwilioVideo.connect(with: connectOptions, delegate: self)_12}_12_12// MARK: TVIRoomDelegate_12_12func didConnectToRoom(room: TVIRoom) {_12 print("Did connect to room")_12}
You can capture local media from your device's microphone, camera or screen-share on different platforms in the following ways:
In an iOS application, begin capturing audio data by creating a TVILocalAudioTrack
, and begin capturing video by creating a TVILocalVideoTrack
with an associated TVIVideoCapturer
. The iOS Video SDK provides customizable video capturers for both camera and screen capture.
_13// Create an audio track_13var localAudioTrack = TVILocalAudioTrack()_13_13// Create a data track_13var localDataTrack = TVILocalDataTrack()_13_13// Create a Capturer to provide content for the video track_13var localVideoTrack : TVILocalVideoTrack?_13_13// Create a video track with the capturer._13if let camera = TVICameraCapturer(source: .frontCamera) {_13 localVideoTrack = TVILocalVideoTrack.init(capturer: camera)_13}
When the client joins a Room, the client can specify which Tracks they wish to share with other Participants. Imagine we want to share the audio and video Tracks we created earlier.
_15let connectOptions = TVIConnectOptions(token: accessToken) { (builder) in_15 builder.roomName = "my-room"_15_15 if let audioTrack = localAudioTrack {_15 builder.audioTracks = [ audioTrack ]_15 }_15 if let dataTrack = localDataTrack {_15 builder.dataTracks = [ dataTrack ]_15 }_15 if let videoTrack = localVideoTrack {_15 builder.videoTracks = [ videoTrack ]_15 }_15}_15_15var room = TwilioVideo.connect(with: connectOptions, delegate: self)
For some use cases (such as a ReplayKit broadcast extension) you may wish to connect as a publish-only Participant that is not subscribed to any Tracks. If you are connecting to a Group Room, you may disable automatic subscription behavior via ConnectOptions.
_10let connectOptions = TVIConnectOptions(token: accessToken) { (builder) in_10 builder.isAutomaticSubscriptionEnabled = false_10 builder.roomName = "my-room"_10_10 if let audioTrack = localAudioTrack {_10 builder.audioTracks = [ audioTrack ]_10 }_10}_10_10var room = TwilioVideo.connect(with: connectOptions, delegate: self)
When you join a Room, Participants may already be present. You can check for existing Participants in the roomDidConnect:
callback by using the remoteParticipants
getter.
_22room = TwilioVideo.connect(with: connectOptions, delegate: self)_22_22// MARK: TVIRoomDelegate_22_22func didConnect(to room: TVIRoom) {_22 // The Local Participant_22 if let localParticipant = room.localParticipant {_22 print("Local identity \(localParticipant.identity)")_22 }_22_22 // Connected participants_22 let participants = room.remoteParticipants;_22 print("Number of connected Participants \(participants.count)")_22}_22_22func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {_22 print ("Participant \(participant.identity) has joined Room \(room.name)")_22}_22_22func room(_ room: TVIRoom, participantDidDisconnect participant: TVIRemoteParticipant) {_22 print ("Participant \(participant.identity) has left Room \(room.name)")_22}
When Participants connect to or disconnect from a Room that you're connected to, you'll be notified via an event listener: Similar to Room Events, Twilio will fire Participant events if the StatusCallback webhook URL is set when the Room is created. These events help your application keep track of the participants who join or leave a Room.
_10// MARK: TVIRoomDelegate_10_10// First, we set a Participant Delegate when a Participant first connects:_10func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {_10 print("Participant connected: \(participant.identity!)")_10 participant.delegate = self_10}
To see the Video Tracks being sent by remote Participants, we need to render them to the screen:
_26// MARK: TVIRemoteParticipantDelegate_26_26/*_26 * In the Participant Delegate, we can respond when the Participant adds a Video_26 * Track by rendering it on screen._26 */_26func subscribed(to videoTrack: TVIRemoteVideoTrack,_26 publication: TVIRemoteVideoTrackPublication,_26 for participant: TVIRemoteParticipant) {_26_26 print("Participant \(participant.identity) added a video track.")_26_26 self.remoteView = TVIVideoView.init(frame: self.view.bounds,_26 delegate:self)_26_26 videoTrack.addRenderer(self.remoteView)_26 self.view.addSubview(self.remoteView!)_26}_26_26// MARK: TVIVideoViewDelegate_26_26// Lastly, we can subscribe to important events on the VideoView_26func videoView(_ view: TVIVideoView, videoDimensionsDidChange dimensions: CMVideoDimensions) {_26 print("The dimensions of the video track changed to: \(dimensions.width)x\(dimensions.height)")_26 self.view.setNeedsLayout()_26}
Sometimes you need to make sure you're looking fantastic before entering a Room. We get it. The iOS SDK provides a means to render a local camera preview outside the context of an active Room:
_17// Use TVICameraCapturer to produce video from the device's front camera._17_17if let camera = TVICameraCapturer(source: .frontCamera),_17 let videoTrack = TVILocalVideoTrack(capturer: camera) {_17_17 // TVIVideoView is a TVIVideoRenderer and can be added to any TVIVideoTrack._17 let renderer = TVIVideoView(frame: view.bounds)_17_17 // Add renderer to the video track_17 videoTrack.addRenderer(renderer)_17_17 self.localVideoTrack = videoTrack_17 self.camera = camera_17 self.view.addSubview(renderer)_17} else {_17 print("Couldn't create TVICameraCapturer or TVILocalVideoTrack")_17}
You can disconnect from a Room you're currently participating in. Other Participants will receive a participantDisconnected
event.
_10// To disconnect from a Room, we call:_10room?.disconnect()_10_10// This results in a callback to TVIRoomDelegate#room\:didDisconnectWithError_10_10// MARK: TVIRoomDelegate_10_10func room(_ room: TVIRoom, didDisconnectWithError error: Error?) {_10 print("Disconnected from room \(room.name)")_10}
As of the 2.7.0
release, the Video SDK will raise notifications when a Room is reconnecting due to a network disruption. A Room reconnection is triggered due to a signaling or media reconnection event. To capture when a reconnection is triggered or that it has reconnected:
_10// MARK: TVIRoomDelegate_10_10// Error will be either TVIErrorSignalingConnectionDisconnectedError or TVIErrorMediaConnectionError_10func room(_ room: TVIRoom, isReconnectingWithError error: Error) {_10 print("Reconnecting to room \(room.name), error = \(String(describing: error))")_10}_10_10func didReconnect(to room: TVIRoom) {_10 print("Reconnected to room \(room.name)")_10}
In a Peer-to-Peer Room, each Participant has media connections to all the other Participants in the Room. If all media connections between the LocalParticipant and all other Participants are broken, then the LocalParticipant's Room will enter the reconnecting state until media connectivity with at least one Participant is re-established.
There are certain instances when an application is put into the background that both the signaling and media connection are closed, which will cause the reconnecting delegate method to be invoked:
The Programmable Video REST API
allows you to control your video applications from your back-end server via HTTP requests. To learn more check out the Programmable Video REST API docs.