Skip to contentSkip to navigationSkip to topbar
Rate this Page:

Getting Started with iOS v3.x


(warning)

Warning

This page 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, 2024(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.

To make sure your app is ready for iOS 14 please visit this page(link takes you to an external page).


Contents

contents page anchor

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, take a look at the open source video collaboration app(link takes you to an external page) and quickstart apps(link takes you to an external page). Then 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:

  • A Room represents a real-time audio, video, and/or screen-share session, and is the basic building block for a Programmable Video application.
  • In a Peer-to-peer Room , media flows directly between participants. Supports up to 10 participants in a mesh topology.
  • In a 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.


1. Get the Programmable Video iOS SDK

1-get-the-programmable-video-ios-sdk page anchor

The Twilio Video iOS SDK dynamic framework can be installed using Carthage, CocoaPods or manually, as you prefer. The Twilio Video iOS SDK is also distributed as a static library which can be manually installed.

Carthage

carthage page anchor

You can add Programmable Video for iOS by adding the following line to your Cartfile:


_10
github "twilio/twilio-video-ios" ~> 3.8

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


_10
source 'https://github.com/CocoaPods/Specs'
_10
_10
platform :ios, '11.0'
_10
_10
target 'TARGET_NAME' do
_10
pod 'TwilioVideo', '~> 3.8'
_10
end

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(link takes you to an external page) or just download the latest Video dynamic framework here(link takes you to an external page).

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"

Manual static library integration

manual-static-library-integration page anchor

libTwilioVideo is distributed as a static iOS library that you can drag and drop into you existing projects.

View all Video iOS Releases here(link takes you to an external page) or just download the latest Video static library here(link takes you to an external page).

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.

Background modes.

Twilio Video requires user permission for features like sharing video from the camera or audio from the microphone. Consider how your applications might function with reduced functionality in case some permissions (like the camera) are declined.

Friendly NamePrivacy Usage Description KeyiOS VersionRecommendation
CameraNSCameraUsageDescriptionAllRequest permissions using AVCaptureDevice.requestAccess(for:completionHandler:)(link takes you to an external page) before calling TwilioVideoSDK.connect(options:delegate:)(link takes you to an external page) with a TVICameraSource.
MicrophoneNSMicrophoneUsageDescriptionAllRequest permissions using AVAudioSession.requestRecordPermission()(link takes you to an external page) before calling TwilioVideoSDK.connect(options:delegate:)(link takes you to an external page) when Participants publish or subscribe to audio tracks with TVIDefaultAudioDevice. (*)
Local NetworkNSLocalNetworkUsageDescription14.0 +This permission is not recommended unless your app is already using NWConnection(link takes you to an external page) APIs on the local network. Twilio Video only requests permissions after you connect to a Peer-to-Peer Room with TVILocalNetworkPrivacyPolicyAllowAll(link takes you to an external page) on iOS 14.

The iOS SDK supports iOS 11.0 or higher. It is built for arm64 and x86_64 architectures with Bitcode slices for arm64 devices.

Supported Xcode versions and Bitcode

supported-xcode-versions-and-bitcode page anchor

The TwilioVideo.framework is built with Xcode 11. The framework can be consumed with previous versions of Xcode. However, re-compiling Bitcode when exporting for Ad Hoc or Enterprise distribution requires the use of Xcode 11.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.

3. Generate an Access Token

3-generate-an-access-token page anchor

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.


_19
@IBAction func createARoom(sender: AnyObject) {
_19
let connectOptions = ConnectOptions(token: accessToken) { (builder) in
_19
builder.roomName = "my-room"
_19
}
_19
room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)
_19
}
_19
_19
// MARK: RoomDelegate
_19
_19
func roomDidConnect(room: Room) {
_19
print("Did connect to Room")
_19
_19
if let localParticipant = room.localParticipant {
_19
print("Local identity \(localParticipant.identity)")
_19
_19
// Set the delegate of the local particiant to receive callbacks
_19
localParticipant.delegate = self
_19
}
_19
}

You must pass the Access Token when connecting to a Room. You may also optionally pass the following:

  • Local audio, video or data tracks , to begin sharing pre-created local media with other Participants in the Room upon connecting.
  • A room name , which allows you to dynamically specify the name of the Room you wish to join. (Note: You can also encode the Room name in the Access Token, which will allow the user to connect to only the Room specified in the token.)
  • An ICE transport policy, which allows you to force calls through TURN relay for testing purposes.

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(link takes you to an external page) 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(link takes you to an external page) 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'


Enabling Room Recordings

enabling-room-recordings page anchor

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.


_10
curl -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.


_19
@IBAction func joinRoom(sender: AnyObject) {
_19
let connectOptions = ConnectOptions(token: accessToken) { (builder) in
_19
builder.roomName = "existing-room"
_19
})
_19
room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)
_19
}
_19
_19
// MARK: RoomDelegate
_19
_19
func roomDidConnect(room: Room) {
_19
print("Did connect to room")
_19
_19
if let localParticipant = room.localParticipant {
_19
print("Local identity \(localParticipant.identity)")
_19
_19
// Set the delegate of the local particiant to receive callbacks
_19
localParticipant.delegate = self
_19
}
_19
}


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
_13
var localAudioTrack = LocalAudioTrack()
_13
_13
// Create a data track
_13
var localDataTrack = LocalDataTrack()
_13
_13
// Create a CameraSource to provide content for the video track
_13
var localVideoTrack : LocalVideoTrack?
_13
_13
// Create a video track with the capturer.
_13
if let camera = CameraSource(delegate: self) {
_13
localVideoTrack = LocalVideoTrack(source: camera)
_13
}


Specify tracks at connect time

specify-tracks-at-connect-time page anchor

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.


_15
let connectOptions = ConnectOptions(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
_15
var room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)


Connect as a publish-only Participant

connect-as-a-publish-only-participant page anchor

For some use cases (such as a ReplayKit broadcast extension(link takes you to an external page)) 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.


_10
let connectOptions = ConnectOptions(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
_10
var room = TwilioVideo.connect(options: connectOptions, delegate: self)


Working with Remote Participants

working-with-remote-participants page anchor

Handle Connected Participants

handle-connected-participants page anchor

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. To receive RemoteParticipantDelegate callbacks you will need to set the RemoteParticipant.delegate property for each connected Remote Participant.


_32
room = TwilioVideo.connect(options: connectOptions, delegate: self)
_32
_32
// MARK: RoomDelegate
_32
_32
func roomDidConnect(room: Room) {
_32
// The Local Participant
_32
if let localParticipant = room.localParticipant {
_32
print("Local identity \(localParticipant.identity)")
_32
_32
// Set the delegate of the local participant to receive callbacks
_32
localParticipant.delegate = self
_32
}
_32
_32
// Connected participants already in the room
_32
print("Number of connected Participants \(room.remoteParticipants.count)")
_32
_32
// Set the delegate of the remote participants to receive callbacks
_32
for remoteParticipant in room.remoteParticipants {
_32
remoteParticipant.delegate = self
_32
}
_32
}
_32
_32
func participantDidConnect(room: Room, participant: RemoteParticipant) {
_32
print ("Participant \(participant.identity) has joined Room \(room.name)")
_32
_32
// Set the delegate of the remote participant to receive callbacks
_32
participant.delegate = self
_32
}
_32
_32
func participantDidDisconnect(room: Room, participant: RemoteParticipant) {
_32
print ("Participant \(participant.identity) has left Room \(room.name)")
_32
}

Handle Participant Connection Events

handle-participant-connection-events page anchor

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: RoomDelegate
_10
_10
// First, we set a Participant Delegate when a Participant first connects:
_10
func participantDidConnect(room: Room, participant: RemoteParticipant) {
_10
print("Participant connected: \(participant.identity)")
_10
participant.delegate = self
_10
}

Display a Remote Participant's Video

display-a-remote-participants-video page anchor

To see the Video Tracks being sent by remote Participants, we need to render them to the screen:


_28
// MARK: RemoteParticipantDelegate
_28
_28
/*
_28
* In the Participant Delegate, we can respond when the Participant adds a Video
_28
* Track by rendering it on screen.
_28
*/
_28
func didSubscribeToVideoTrack(videoTrack: RemoteVideoTrack,
_28
publication: RemoteVideoTrackPublication,
_28
participant: RemoteParticipant) {
_28
_28
print("Participant \(participant.identity) added a video track.")
_28
_28
if let remoteView = VideoView.init(frame: self.view.bounds,
_28
delegate:self) {
_28
_28
videoTrack.addRenderer(remoteView)
_28
self.view.addSubview(remoteView)
_28
self.remoteView = remoteView
_28
}
_28
}
_28
_28
// MARK: VideoViewDelegate
_28
_28
// Lastly, we can subscribe to important events on the VideoView
_28
func videoViewDimensionsDidChange(view: VideoView, dimensions: CMVideoDimensions) {
_28
print("The dimensions of the video track changed to: \(dimensions.width)x\(dimensions.height)")
_28
self.view.setNeedsLayout()
_28
}


Display a Camera Preview

display-a-camera-preview page anchor

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:


_23
// Use CameraSource to produce video from the device's front camera.
_23
_23
if let camera = CameraSource(delegate: self),
_23
let videoTrack = LocalVideoTrack(source: camera) {
_23
_23
// VideoView is a VideoRenderer and can be added to any VideoTrack.
_23
let renderer = VideoView(frame: view.bounds)
_23
}
_23
if let camera = TVICameraCapturer(source: .frontCamera),
_23
let videoTrack = TVILocalVideoTrack(capturer: camera) {
_23
_23
// TVIVideoView is a TVIVideoRenderer and can be added to any TVIVideoTrack.
_23
let renderer = TVIVideoView(frame: view.bounds)
_23
_23
// Add renderer to the video track
_23
videoTrack.addRenderer(renderer)
_23
_23
self.localVideoTrack = videoTrack
_23
self.camera = camera
_23
self.view.addSubview(renderer)
_23
} else {
_23
print("Couldn't create CameraCapturer or LocalVideoTrack")
_23
}

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:
_10
room?.disconnect()
_10
_10
// This results in a callback to RoomDelegate#roomDidDisconnect(room: Room, error: Error?)
_10
_10
// MARK: RoomDelegate
_10
_10
func roomDidDisconnect(room: Room, error: Error?) {
_10
print("Disconnected from room \(room.name)")
_10
}

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: RoomDelegate
_10
_10
// Error will be either TwilioVideoSDK.Error.signalingConnectionError or TwilioVideoSDK.Error.mediaConnectionError
_10
func roomIsReconnecting(room: Room, error: Error) {
_10
print("Reconnecting to room \(room.name), error = \(String(describing: error))")
_10
}
_10
_10
func roomDidReconnect(room: Room) {
_10
print("Reconnected to room \(room.name)")
_10
}

Reconnections in a Peer-to-Peer Room

reconnections-in-a-peer-to-peer-room page anchor

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.


Reconnections and the UIApplication lifecycle

reconnections-and-the-uiapplication-lifecycle page anchor

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:

  • When connected to a Peer-to-Peer Room with no Remote Participants.
  • When connected to a Peer-to-Peer Room with other Remote Participants and no shared audio tracks.
  • When connected to a Group Room with no shared audio tracks.

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.


We love feedback and questions especially those with helpful debugging information so we can diagnose and respond quickly. When submitting issues or support tickets, it would be great if you add the following:

  • Description - A description of the issue
  • Steps to reproduce - List the steps necessary to reproduce the issue
  • Code - Include any applicable code snippets that would assist in reproduction and troubleshooting
  • Expected Behavior - What you expect to happen
  • Actual Behavior - What actually happens
  • Reproduces How Often - What percentage of the time does it reproduce?
  • Logs - Any log output when the issue occurs. See below for enabling debug level logging.
  • Video iOS SDK - The version(s) of the Video iOS SDK where this issue is apparent
  • Xcode - The version(s) of Xcode where this issue is apparent
  • iOS Version - The version(s) of iOS where this issue is apparent
  • iOS Device - The iOS device(s) where this issue is apparent
  • Room SID - Room SIDs can be useful for tracing backend issues

After gathering the above information, you can get help in a few ways:

For issues related to the Twilio Video iOS SDK itself:

For issues related to the Twilio Video iOS Quickstarts:


To enable debug level logging, add the following code in your application:


_10
TwilioVideoSDK.setLogLevel(.debug)


Rate this Page: