This guide describes how to integrate the SDK and implement a basic one-on-one video call using ZEGOCLOUD's Video Call SDK.
Before you begin, make sure you complete the following:
Skip this step if a project already exists.
Open Xcode, select File > New > Project. In the dialog that appears, select the iOS tab, then select App in the Application section.
Fill in items or choose options for the new project, click Next after the configuration is completed.
Product Name and Organization Identifier are required, which will be used for creating the App Bundle Identifier.
Choose a directory to store the project, and click Create to create a project.
Choose either of the following methods to integrate the Video Call SDK into your project.
Open Xcode and select File > Add Packages..., and enter the following URL in the search box of Search or Enter Package URL:
https://github.com/zegolibrary/express-video-ios
Specify the SDK version you want to integrate into Dependency Rule (Recommended: use the default rule "Up to Next Major Version"), and then click Add Package to import the SDK. For more, refer to Apple Documentation.
Install CocoaPods. For more details, see CocoaPods Installation Guide .
Open the Terminal, enter the root directory of the project, and execute the command pod init
to create a Podfile
.
Open Podfile
, add pod 'ZegoExpressEngine'
, and change MyProject
to your target name.
target 'MyProject' do
use_frameworks!
pod 'ZegoExpressEngine'
end
Because the SDK is XCFramwork, therefore, you will need to use CocoaPods 1.10.0 or later to integrate the Video Call SDK.
Since version v3.2.0, the Pod name of the Video Call SDK is changed from ZegoExpressEngine/Video
to ZegoExpressEngine
; the Pod name of the Voice Call SDK is changed from ZegoExpressEngine/Audio
to ZegoExpressAudio
.
Execute pod repo update
to update the local index to make sure the latest version of SDK can be installed. For the latest version number, see ZEGOCLOUD Express-Video iOS SDK Release History .
Execute pod install
to install the SDK.
Download the latest version of SDK from SDK downloads . We recommend you use XCFramework, and then extract files from the downloaded SDK package.
Copy the SDK dynamic library file ZegoExpressEngine.xcframework
to the project directory.
Open Xcode and select File > Add Files to "xxx" (xxx is the project name) to add the SDK dynamic library files to the project.
Do the following to add the framework file to the project target.
a) Select the project target.
b) Click General, then under Frameworks, Libraries, and Embedded Content, click the Add button (+) below the table.
c) Add ZegoExpressEngine.framework to the target, and set the Embed field to Embed & Sign.
Permissions can be set as needed.
Open Xcode, select the target object, and then click Info > Custom iOS Target Properties.
Click the Add button (+) to add camera and microphone permissions.
Privacy - Camera Usage Description
Privacy - Microphone Usage Description
The following is the sample code for implementing video call functions. Feel free to refer to it when developing.
//
// ViewController.swift
//
import UIKit
import ZegoExpressEngine
// Get your AppID and AppSign from ZEGOCLOUD Console
// [My Projects -> AppID] : https://console.zegocloud.com/project
let appID : UInt32 = <#YourAppID#>
let appSign: String = "<#YourAppSign#>"
class ViewController: UIViewController {
// The video stream for the local user is displayed here
var localView: UIView!
// The video stream for the remote user is displayed here
var remoteView: UIView!
// Click to log in or log out a room
var callButton: UIButton!
var localUserID = "user_" + String(Int.random(in: 1...100))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
createEngine()
initViews()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
destroyEngine()
}
private func createEngine() {
let profile = ZegoEngineProfile()
// Get your AppID and AppSign from ZEGOCLOUD Console
//[My Projects -> AppID] : https://console.zegocloud.com/project
profile.appID = appID
profile.appSign = appSign
// Use the default scenario.
profile.scenario = .default
// Create a ZegoExpressEngine instance and set eventHandler to [self].
ZegoExpressEngine.createEngine(with: profile, eventHandler: self)
}
private func destroyEngine() {
ZegoExpressEngine.destroy(nil)
}
private func initViews() {
// Initializes the remote video view. This view displays video when a remote host joins the channel.
remoteView = UIView()
remoteView.frame = self.view.frame
self.view.addSubview(remoteView)
// Initializes the local video window. This view displays video when the local user is a host.
localView = UIView()
localView.frame = CGRect(x: 200, y: 80, width: 135, height: 240)
self.view.addSubview(localView)
// Button to log in or log out a room
callButton = UIButton(type: .system)
callButton.frame = CGRect(x: (self.view.frame.width - 80) / 2.0, y: self.view.frame.height - 150, width: 80, height: 80)
callButton.setBackgroundImage(UIImage(named: "call_icon"), for: .normal)
callButton.setBackgroundImage(UIImage(named: "call_hand_up_icon"), for: .selected)
callButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(callButton)
}
private func startPreview() {
// Set up a view for the local video preview
let canvas = ZegoCanvas(view: self.localView)
ZegoExpressEngine.shared().startPreview(canvas)
}
private func stopPreview() {
ZegoExpressEngine.shared().stopPreview()
}
private func startPublish() {
// After calling the `loginRoom` method, call this method to publish streams.
let streamID = "stream_" + localUserID
ZegoExpressEngine.shared().startPublishingStream(streamID)
}
private func stopPublish() {
ZegoExpressEngine.shared().stopPublishingStream()
}
private func startPlayStream(streamID: String) {
// Start to play streams. Set the view for rendering the remote streams.
let canvas = ZegoCanvas(view: self.remoteView)
ZegoExpressEngine.shared().startPlayingStream(streamID, canvas: canvas)
}
private func stopPlayStream(streamID: String) {
ZegoExpressEngine.shared().stopPlayingStream(streamID)
}
private func loginRoom() {
// The value of `userID` is generated locally and must be globally unique.
let user = ZegoUser(userID: localUserID)
// The value of `roomID` is generated locally and must be globally unique.
// Users must log in to the same room to call each other.
let roomID = "room_1"
let roomConfig = ZegoRoomConfig()
// onRoomUserUpdate callback can be received when "isUserStatusNotify" parameter value is "true".
roomConfig.isUserStatusNotify = true
// log in to a room
ZegoExpressEngine.shared().loginRoom(roomID, user: user, config: roomConfig) { errorCode, extendedData in
if errorCode == 0 {
// Login room successful
self.startPreview()
self.startPublish()
} else {
// Login room failed
}
}
}
private func logoutRoom() {
ZegoExpressEngine.shared().logoutRoom()
}
@objc func buttonAction(sender: UIButton!) {
sender.isSelected = !sender.isSelected;
if sender.isSelected {
loginRoom()
} else {
logoutRoom()
}
}
}
extension ViewController : ZegoEventHandler {
// Callback for updates on the status of the streams in the room.
func onRoomStreamUpdate(_ updateType: ZegoUpdateType, streamList: [ZegoStream], extendedData: [AnyHashable : Any]?, roomID: String) {
// If users want to play the streams published by other users in the room, call the startPlayingStream method with the corresponding streamID obtained from the `streamList` parameter where ZegoUpdateType == ZegoUpdateTypeAdd.
if updateType == .add {
for stream in streamList {
startPlayStream(streamID: stream.streamID)
}
} else {
for stream in streamList {
stopPlayStream(streamID: stream.streamID)
}
}
}
// Callback for updates on the current user's room connection status.
func onRoomStateUpdate(_ state: ZegoRoomState, errorCode: Int32, extendedData: [AnyHashable : Any]?, roomID: String) {
}
// Callback for updates on the status of other users in the room.
// Users can only receive callbacks when the isUserStatusNotify property of ZegoRoomConfig is set to `true` when logging in to the room (loginRoom).
func onRoomUserUpdate(_ updateType: ZegoUpdateType, userList: [ZegoUser], roomID: String) {
}
}
Create a UI for video calls for your project based on your scenario requirements. We recommend you add the following UI elements to your project:
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController {
// The video stream for the local user is displayed here
var localView: UIView!
// The video stream for the remote user is displayed here
var remoteView: UIView!
// Click to log in or log out of a room
var callButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
initViews()
}
private func initViews() {
// Initializes the remote video view. This view displays video when a remote host logins the room.
remoteView = UIView()
remoteView.frame = self.view.frame
self.view.addSubview(remoteView)
// Initializes the local video window. This view displays video when the local user is a host.
localView = UIView()
localView.frame = CGRect(x: 200, y: 80, width: 135, height: 240)
self.view.addSubview(localView)
// Button to log in or log out of a room
callButton = UIButton(type: .system)
callButton.frame = CGRect(x: (self.view.frame.width - 80) / 2.0, y: self.view.frame.height - 150, width: 80, height: 80)
callButton.setBackgroundImage(UIImage(named: "call_icon"), for: .normal)
callButton.setBackgroundImage(UIImage(named: "call_hand_up_icon"), for: .selected)
callButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(callButton)
}
}
The following diagram shows the basic process of User A playing a stream published by User B:
For a better understanding, you can check the key concepts of Video Call SDK:
First, run the following to import the header file.
// Import the header file ZegoExpressEngine
import ZegoExpressEngine
Then, call the createEngineWithProfile
method to initialize the Video Call SDK. And config the following:
profile
: the ZegoEngineProfile object, used to config the appID and appSign, as well as the scenario you are applying the SDK to. eventHandler
: an event handler object, used to listen for core event callbacks, such as the callback for updates on the room connection stats changes, updates on in-room participants log in or log out, and more. You can call the setEventHandler
method to set up the event handler object. To destroy the SDK and release the resources it occupies, call the destroy
method.
private func createEngine() {
let profile = ZegoEngineProfile()
// Get your AppID and AppSign from ZEGOCLOUD Console
//[My Projects -> AppID] : https://console.zegocloud.com/project
profile.appID = appID
profile.appSign = appSign
// Use the default scenario.
profile.scenario = .default
// Create a ZegoExpressEngine instance and set eventHandler to [self].
ZegoExpressEngine.createEngine(with: profile, eventHandler: self)
}
private func destroyEngine() {
ZegoExpressEngine.destroy(nil)
}
Implement the ZegoEventHandler
event handler to listen for event callbacks, such as the event callback on the updates when the in-room streams are added or deleted, the updates when in-room participants log in or log out, the updates when room connection state changes, and more.
onRoomStreamUpdate
: Callback for updates on the status of the streams in the room. When new streams are published to the room or existing streams in the room stop, the SDK sends out the event notification through this callback. You can call startPlayStream()
and stopPlayStream()
methods in this callback.
onRoomStateUpdate
: Callback for updates on current room connection status. When the current room connection status changes (for example, when the current user is disconnected from the room or login authentication fails), the SDK sends out the event notification through this callback.
onRoomUserUpdate
: Callback for updates on the status of other users in the room. When other users log in or log out of the room, the SDK sends out the event notification through this callback.
extension ViewController : ZegoEventHandler {
// Callback for updates on the status of the streams in the room.
func onRoomStreamUpdate(_ updateType: ZegoUpdateType, streamList: [ZegoStream], extendedData: [AnyHashable : Any]?, roomID: String) {
// If users want to play the streams published by other users in the room, call the startPlayingStream method with the corresponding streamID obtained from the `streamList` parameter where ZegoUpdateType == ZegoUpdateTypeAdd.
if updateType == .add {
for stream in streamList {
startPlayStream(streamID: stream.streamID)
}
} else {
for stream in streamList {
stopPlayStream(streamID: stream.streamID)
}
}
}
// Callback for updates on the current user's room connection status.
func onRoomStateUpdate(_ state: ZegoRoomState, errorCode: Int32, extendedData: [AnyHashable : Any]?, roomID: String) {
}
// Callback for updates on the status of other users in the room.
// Users can only receive callbacks when the isUserStatusNotify property of ZegoRoomConfig is set to `true` when logging in to the room (loginRoom).
func onRoomUserUpdate(_ updateType: ZegoUpdateType, userList: [ZegoUser], roomID: String) {
}
}
To log in to a room, you can call the loginRoom
method.
To log out, you can call the logoutRoom
method.
private func loginRoom() {
// The value of `userID` is generated locally and must be globally unique.
let user = ZegoUser(userID: localUserID)
// The value of `roomID` is generated locally and must be globally unique.
// Users must log in to the same room to call each other.
let roomID = "room_1"
let roomConfig = ZegoRoomConfig()
// onRoomUserUpdate callback can be received when "isUserStatusNotify" parameter value is "true".
roomConfig.isUserStatusNotify = true
// log in to a room
ZegoExpressEngine.shared().loginRoom(roomID, user: user, config: roomConfig) { errorCode, extendedData in
if errorCode == 0 {
// Login room successful
self.startPreview()
self.startPublish()
} else {
// Login room failed
}
}
}
private func logoutRoom() {
ZegoExpressEngine.shared().logoutRoom()
}
To start the local video preview and to render it, call the startPreview
method.
And you call the stopPreview
method to stop the rendering.
private func startPreview() {
// Set up a view for the local video preview
let canvas = ZegoCanvas(view: self.localView)
ZegoExpressEngine.shared().startPreview(canvas)
}
private func stopPreview() {
ZegoExpressEngine.shared().stopPreview()
}
To start publishing a local audio or video stream to remote users, call the startPublishingStream
method.
And you can call the stopPublishingStream
method to stop the stream publishing.
private func startPublish() {
// After calling the `loginRoom` method, call this method to publish streams.
// The StreamID must be unique in the room.
let streamID = "stream_" + localUserID
ZegoExpressEngine.shared().startPublishingStream(streamID)
}
private func stopPublish() {
ZegoExpressEngine.shared().stopPublishingStream()
}
You can call startPlayingStream
method to start playing a remote video stream.
And to stop the stream playing, call the stopPlayingStream
method to stop.
private func startPlayStream(streamID: String) {
// Start to play streams. Set the view for rendering the remote streams.
let canvas = ZegoCanvas(view: self.remoteView)
ZegoExpressEngine.shared().startPlayingStream(streamID, canvas: canvas)
}
private func stopPlayStream(streamID: String) {
ZegoExpressEngine.shared().stopPlayingStream(streamID)
}
When your app starts, you need to call the createEngine
method to initialize the SDK.
When your app is about to exit, you can call the destroyEngine
to release SDK resources.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
createEngine()
initViews()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
destroyEngine()
}
To test your implementation, run your app project on a real device. Upon successful running, you can view the local video.
For your convenience of testing experience, we got you a Web platform for debugging. On the debugging page, you can enter the AppID and room ID of the real device user, and a different user ID to log in to the same room for communicating with the real device user. After a video call starts successfully, you can hear the remote audio and view the remote video.
For a detailed demo source code, check it here.
When running the App, it prompts Building for iOS Simulator, but the linked and embedded framework'ZegoExpressEngine.framework' was built for iOS + iOS Simulator.
After Xcode 12.3, the framework with iOS + iOS (Simulator) dual-platform architecture is prohibited by default. It is recommended to replace it with XCFramework recommended by Apple (you can choose to download XCFramework in the drop-down box in Download SDK Package). Or you can select "TARGETS > Build Settings > Validate Workspace" through Xcode and set the value of this parameter to YES to continue using the traditional framework.
Resolution And Pricing Attention!
Please pay close attention to the relationship between video resolution and price when implementing video call, live streaming, and other video scenarios.
When playing multiple video streams in the same room, the billing will be based on the sum of the resolutions, and different resolutions will correspond to different billing tiers.
The video streams that are included in the calculation of the final resolution are as follows:
Before your app goes live, please make sure you have reviewed all configurations and confirmed the billing tiers for your business scenario to avoid unnecessary losses. For more details, please refer to Pricing.