ECV Video Görüşme iOS SDK [TR]
ECV Video Call iOS SDK [EN]
Created by Zeynep Baysun on Jun 12, 2025

ECV Video Call iOS SDK
Table of Contents
- ECV Video Call iOS SDK
- Usage Guide
- Configuration Guide
- Demo Application Guide
- Events and State Management
- Customization Guide
- Troubleshooting Guide
Introduction
Welcome to the ECV Video Call iOS SDK! This SDK enables you to seamlessly integrate real-time video call functionality into your iOS applications. It handles the complexities of signaling, WebRTC peer connections, media streams, and call state management, allowing you to focus on your application's user interface and business logic.
This repository contains:
- The SDK: Provided as a pre-compiled iOS Framework (
.frameworkfile). You will add this as a dependency to your project. - Demo Application: Full source code for a demonstration app (
ECV175_iOS_DEMO) showcasing how to use the SDK features. This demo is intended as a reference and example, not production-ready UI code.
Key Features
- Establishes connection to ECV backend services (v1.5 and v1.75+)
- Handles WebRTC setup and peer-to-peer connection
- Manages local and remote video/audio streams
- Provides controls for muting/unmuting audio and video
- Supports camera switching
- Manages call lifecycle events (dialing, queuing, connected, hold, ended)
- Configurable environments and call parameters
Getting Started
Prerequisites
- Xcode (Latest Stable Version Recommended)
- Basic knowledge of iOS development (Swift)
- Minimum iOS Version: 13.0
- Target iOS Version: 17.0
1. Add Required Frameworks
Your application needs to include the ECV SDK framework and its required dependencies: WebRTC. You will receive these as .framework files.
-
Required Framework Files:
- ECV SDK: (e.g.,
ECV175_iOS_SDK.xcframework) - WebRTC:
WebRTC.xcframework
- ECV SDK: (e.g.,
-
Add the frameworks to your project:
- Drag and drop the framework files into your Xcode project
- Make sure "Copy items if needed" is checked
- Add to your target's "Frameworks, Libraries, and Embedded Content" section
- Set "Embed & Sign" for each framework
-
In your project's Build Settings:
- Set "Enable Bitcode" to No
- Add the frameworks to "Framework Search Paths"
- Ensure "Build Active Architecture Only" is set appropriately for your needs
2. Explore the Demo Application
The included demo application (ECV175_iOS_DEMO) provides a practical example of SDK integration.
- Open the
ECV175_iOS_DEMO.xcodeprojin Xcode - Build and run the demo app on a simulator or physical device
- Examine the source code, particularly the files in the
UIdirectory and theAppState.swiftto understand how SDK components are initialized and used
3. Basic SDK Initialization (Conceptual)
// Somewhere in your AppDelegate or a central management object
import ECV175_iOS_SDK
// 1. Define the SDK Configuration (See Configuration for details)
let sdkConfig = ECVSdkConfiguration(
name: "Environment_Name" // Give it any name that refers to your app or backend
serviceUrl: "YOUR_SERVICE_URL", // Replace with your actual service URL
useQueueSocket: true // or false, Make sure this matches your backend version (true for 1.75+, false for 1.5)
// Optionally override webrtcUrl, signalingUrl, etc.
)
// 2. Initialize the SDK Instance
// It's recommended to have a single instance managed centrally
let ecvSdk = ECVSdk(config: sdkConfig)
// Store this instance for access throughout your app (e.g., in AppDelegate, DI, Singleton)
// Example: MyApp.sdkInstance = ecvSdk
Core Concepts
ECVSdk: The main entry point for interacting with the SDK. Used to create calls and access global settings/modules.ECVSdkConfiguration: Defines the backend URLs and connection parameters for the SDK.VideoCall: Represents a single video call session. Manages the call state, events, and connection details.WebRtcConnection: Handles the underlying WebRTC peer connection, media streams, and renderers. Accessed viaVideoCall.connect().- Events: The SDK uses a delegate system to notify your application about call state changes, errors, and media events (
VideoCallEvent). - Renderers (
RTCMTLVideoView): Standard WebRTC views used to display local and remote video. You need to manage their lifecycle and attach/detach them usingWebRtcConnection.
Demo Application Flow
The demo app follows this basic flow:
EnvironmentsScreen: Allows selecting or defining backend connection configurations (Service URL, etc.). This is primarily for demonstration purposes; your app will likely have a fixed configuration or fetch it dynamically.MainScreen: Collects necessary call information (e.g., caller name, attributes), requests permissions (Camera, Mic), and initiates the call usingsdk.createCall().QueueScreen: Displays a waiting state while the call connects to an agent. Listens forAgentConnectedorError/Hangupevents.CallScreen: The main video call interface. ManagesRTCMTLVideoViews for local and remote video, displays call controls (mute, switch camera, hangup), and reacts to SDK events to update the UI.
Next Steps
- Follow the steps in Getting Started to set up the SDK and demo app.
- Read
Usage Guidefor a detailed understanding of the SDK's core components. - Consult
Demo Application Guidewhile examining the demo app source code.
Usage Guide
This document provides detailed information about using the ECV Video Call iOS SDK in your application.
SDK Initialization
Basic Initialization
import ECV175_iOS_SDK
// Create SDK configuration
let config = ECVSdkConfiguration(
name: "Environment_Name" // Give it any name that refers to your app or backend
serviceUrl: "YOUR_SERVICE_URL",
useQueueSocket: true // or false based on your backend version (false: 1.5 or true or false: 1.75)
)
// Initialize SDK instance
let sdk = ECVSdk(config: config)
Configuration Options
The ECVSdkConfiguration class supports various configuration options:
let config = ECVSdkConfiguration(
name: "Environment_Name",
serviceUrl: "YOUR_SERVICE_URL",
useQueueSocket: true,
webrtcUrl: "YOUR_WEBRTC_URL", // Optional, default: serviceUrl
signalingUrl: "YOUR_SIGNALING_URL", // Optional, default: serviceUrl
waitForRoomIntervalDelay: Int, // Optional ( For 1.5 only ), default: 3000
waitForRoomIntervalEndpoint: String // Optional ( For 1.5 only ), default: "serviceUrl + API.waitForRoomInterval + "?rtcId=""
)
Extra Configs
Logger.setLevel(logLevel: .debug) // .debug To enable debugging everything in SDK (.debug, .info, .warn, .error) // default: .info
Creating and Managing Calls
Creating a Call
// Create and start the video call
let call = await sdk.createVideoCall()
If you already have an rtcId, you can provide it to the createVideoCall function, otherwise it will be generated from the backend automatically (default)
let call = await sdk.createVideoCall(rtcId: "your_rtcId")
VideoCall Events
Now before we start the call we can add event listener to the call
sdk.modules.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(videoCall.rtcId != call.rtcId){
return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events
}
if(newEvent.type == .AgentConnected) {
Logger.info("From sdk.modules.onCallEvent: \(newEvent.type)")
}
else if(newEvent.type == .Error){
Logger.error("From sdk.modules.onCallEvent: \(newEvent.detail ?? "")")
}
else {
Logger.info("From sdk.modules.onCallEvent: \(newEvent)")
}
}
VideoCall Parameters
To start the call, we need to prepare DialCallParameters object
let dialCallParameters = DialCallParameters(
callerName: "Caller_Name", // Optional but should be provided, otherwise a random name will be generated
attributes: [String: String], // Optional, Attach data for the call
queue: String?, // Optional, Queue name, if not provided, will use system's default queue
// Advanced Optional Params
acdAttributes: [String: String],
customPayload: [String: String],
additionalHeaders: [String: String],
)
Example:
let dialCallParameters = DialCallParameters(
callerName: "John Doe",
attributes: [
"department": "support",
"priority": "high"
],
queue: "TechSupport",
)
Starting the VideoCall
If we want to start a VideoCall, we use call.start
await call.start(dialCallParameters: dialCallParameters)
Wait for an Agent to connect (inQueue)
There are two simple ways to wait for an agent to connect
1- (1.5 and 1.75+) Using VideoCall events we already implemented above if(newEvent.type == .AgentConnected)
2- (1.75+ Only) Using awaited promisified task:
let isAgentConnected = await call.agentConnected?.value
Logger.info("isAgentConnected: \(String(describing: isAgentConnected))")
if(isAgentConnected == true) {
// Openning the call in a saperate web browser maybe? // useful for Development
if let urlString = call.getUrl(), let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
else {
Logger.error("Failed to open URL: invalid URL string")
}
}
else {
Logger.warn("Agent not connected!")
}
Connect the VideoCall
1- Using the videoCall.connect()
In your CallScreen.swift
func setup() {
Task {
guard let rtcId = self.rtcId else {
self.callError = "RtcId not initialized"
Logger.error("Setup failed: RtcId is nil")
return
}
if let videoCall = appState.sdk?.getVideoCall(rtcId: rtcId){
self.videoCall = videoCall
}
else {
self.videoCall = await appState.sdk?.createVideoCall(rtcId: rtcId)
}
// Handle call events
let ECVModules = appState.sdk?.modules
ECVModules?.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(self.videoCall?.rtcId != videoCall.rtcId){
return // Making sure we only catch the events related to the current call. // comment this line to catch all calls' events
}
if(newEvent.type == .CallEnded){
ECVModules?.onCallEvent = [] // reset events to prevent overflow of events
path.append(NavigationRoute.mainScreen)
}
}
guard let webRtcConnection = self.videoCall?.connect() else {
self.callError = "WebRtcConnection failed to connect"
Logger.error("Setup failed: WebRtcConnection is nil")
return
}
self.webRtcConnection = webRtcConnection
Logger.debug("Instantiating and assigning renderers...")
let local = Renderer()
let remote = Renderer()
webRtcConnection.localVideoRenderer = local
webRtcConnection.remoteVideoRenderer = remote
self.localRenderer = local
self.remoteRenderer = remote
Logger.info("Starting WebRTC room...")
await webRtcConnection.startRoom()
Logger.info("Call setup complete.")
}
}
2- Or the caller can join the video call simply using web browser (not recommended / useful for dev)
if let urlString = call.getUrl(), let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
else {
Logger.error("Failed to open URL: invalid URL string")
}
Video Call Interface
Setting Up Video Views
import Foundation
import SwiftUI
import Combine
import ECV175_iOS_SDK
import WebRTC
struct CallScreen: View {
let rtcId: String?
@EnvironmentObject var appState: AppState
@Binding var path: NavigationPath
@State private var videoCall: VideoCall?
@State private var webRtcConnection: WebRtcConnection?
@State private var localRenderer: Renderer?
@State private var remoteRenderer: Renderer?
let localStreamWidth: CGFloat = 80
let localStreamHeight: CGFloat = 120
let margin: CGFloat = 10
let pipBottomPadding: CGFloat = 70
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
WebRtcVideoView(renderer: remoteRenderer).ignoresSafeArea()
WebRtcVideoView(renderer: localRenderer)
.frame(width: localStreamWidth, height: localStreamHeight)
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.white.opacity(0.5), lineWidth: 1)
)
.padding(.trailing, margin)
.padding(.bottom, margin + pipBottomPadding)
// ... Call Buttons
}
.navigationBarHidden(true)
.statusBar(hidden: true)
.onAppear(perform: setup)
.onDisappear {
hangup(by: "onDisappear")
}
}
// MARK: - Video Renderer SwiftUI Wrapper
struct WebRtcVideoView: UIViewRepresentable {
let renderer: (any RTCVideoRenderer)?
func makeUIView(context: Context) -> UIView {
if let renderer = renderer as? Renderer {
return renderer.view
} else if let renderer = renderer as? Renderer {
return renderer.view
} else {
return UIView()
}
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
// MARK: - Custom Renderer Classes
class Renderer: NSObject, RTCVideoRenderer {
let view: RTCMTLVideoView
override init() {
self.view = RTCMTLVideoView()
self.view.videoContentMode = .scaleAspectFill
super.init()
Logger.debug("Renderer created")
}
deinit { Logger.debug("Renderer deallocated") }
func setSize(_ size: CGSize) {
view.setSize(size)
}
func renderFrame(_ frame: RTCVideoFrame?) {
view.renderFrame(frame)
}
}
func hangup(by: String = "HangUpButton") {
Logger.debug("Hangup requested by: \(by)")
videoCall?.hangup()
videoCall = nil
webRtcConnection = nil
localRenderer = nil
remoteRenderer = nil
}
// func setup(){
// ... paste it here
//}
}
Call Controls
// Mute/Unmute Audio
webRtcConnection.setAudioEnabled(state: false) // Mute
webRtcConnection.setAudioEnabled(state: true) // Unmute
// Enable/Disable Video
webRtcConnection.setVideoEnabled(state: false) // Disable video
webRtcConnection.setVideoEnabled(state: true) // Enable video
// Switch Camera
try await webRtcConnection.switchCamera()
// Enable/Disable Flashlight
webRtcConnection.setFlashlightEnabled(state: true)
webRtcConnection.setFlashlightEnabled(state: false)
// End Call
videoCall.hangup()
Video Call Event Handling
When an event gets fired in sdk.modules.onCallEvent, the newEvent is VideoCallEvent and newEvent.type is VideoCallEventType
sdk.modules.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(videoCall.rtcId != call.rtcId){
return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events
}
if(newEvent.type == .DialSuccess){
Logger.debug("From sdk.modules.onCallEvent: \(newEvent.detail ?? "")")
}
else if(newEvent.type == .CallEnded){
// handle it
}
//else if(){
//}
}
each type is self explanatory:
public enum VideoCallEventType {
case None
case Init
case GotRtcId
case DialSuccess
case Error
case OnQueue
case AgentConnected
case CallEnded
case SignalingConnected
case GotLocalStream
case GotRemoteStream
case JoinedRoom
case SignalingRegistered
case RtcPeerConnectionCreated
case GotOffer
case SentOffer
case GotAnswer
case SentAnswer
case AudioMuted
case AudioUnmuted
case VideoMuted
case VideoUnmuted
case RemoteAudioMuted
case RemoteAudioUnmuted
case RemoteVideoMuted
case RemoteVideoUnmuted
case StartHold
case EndHold
case TakePhotoOn
case TakePhotoOff
case WebRtcOnDataChannel
case TextChannelConnected
case TextChannelMessage
case ConnectionTimeoutStart
case ConnectionTimeoutEnd
case WebRtcConnected
case WebRtcRenegotiationNeeded
case WebRtcDisconnected
case WebRtcFailed
case Hangup
case TransferPending
case TransferCancelled
case TransferCompleted
}
Error Handling
To handle errors more precisely, we can check videoCall.lastError when an error happens under .onCallEvent
sdk.modules.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(videoCall.rtcId != call.rtcId){
return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events
}
if(newEvent.type == .Error){
Logger.error("From sdk.modules.onCallEvent: \(newEvent.detail ?? "")")
Logger.error(videoCall.lastError)
}
}
videoCall.lastError's type is ErrorObject and inside it there are:
public class ErrorObject {
public var name: ErrorName
public var message: String
public init(name: ErrorName = ErrorName.NO_ERROR, message: String = "No error.") {
self.name = name
self.message = message
}
}
public enum ErrorName {
case NO_ERROR
case SERVICE_URL_REQUIRED
case CALL_NOT_INITIALIZED
case CALL_ALREADY_DIALED
case QUEUE_SOCKET_CLOSED
case ERROR_DIALING
case CALL_NOT_DIALED
case ERROR_WAITING_FOR_ROOM
case NO_ICE_SERVERS_FOUND
case HANGUP_FAILED
}
So we can basically check for a specific error:
sdk.modules.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(videoCall.rtcId != call.rtcId){
return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events
}
if(newEvent.type == .Error){
Logger.error("From sdk.modules.onCallEvent: \(newEvent.detail ?? "")")
Logger.error(videoCall.lastError)
switch videoCall.lastError.name {
case .SERVICE_URL_REQUIRED
break
case .CALL_NOT_DIALED
break
case .NO_ICE_SERVERS_FOUND
break
// etc..
}
}
}
Permission Handling
// Request camera and microphone permissions
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
// Camera permission granted
} else {
// Camera permission denied
}
}
AVCaptureDevice.requestAccess(for: .audio) { granted in
if granted {
// Microphone permission granted
} else {
// Microphone permission denied
}
}
Best Practices
- Single SDK Instance: Maintain a single instance of
ECVSdkthroughout your application's lifecycle. - Proper Cleanup: Always clean up resources when the call ends:
- Error Recovery: Implement proper error recovery mechanisms:
- Memory Management: Be careful with retain cycles:
weak var call: VideoCall?
weak var webRtcConnection: WebRtcConnection?
Configuration Guide
This document details the configuration options available in the ECV Video Call iOS SDK.
SDK Configuration
Basic Configuration
The ECVSdkConfiguration class is used to configure the video call instance:
let config = ECVSdkConfiguration(
name: "Environment_Name"
serviceUrl: "YOUR_SERVICE_URL",
)
Required Parameters
name: String Give it any name that refers to your app or backendserviceUrl: String The base URL of your ECV backend service
Optional Parameters
webrtcUrl: String Custom WebRTC server URL, default: same as serviceUrlsignalingUrl: String Custom signaling server URL, default: same as serviceUrluseQueueSocket: Bool Whether to use the queue socket (true for v1.75+, false for v1.5), default: truewaitForRoomIntervalDelay: Integer It will be active/used only if useQueueSocket: false, in ECV 1.5, It is the interval time to check if agent connected using http request, default: 3000waitForRoomIntervalEndpoint: String It will be active/used only if useQueueSocket: false, in ECV 1.5, It is "the waiting for agent to connect" http endpoint, default: serviceUrl + API.waitForRoomInterval + "?rtcId="
Example Usages:
let configV175 = ECVSdkConfiguration(
name: "Some ECV 1.75 App"
serviceUrl: "https://prod-v175.ecv.example.com"
)
let configV15 = ECVSdkConfiguration(
name: "Some ECV 1.5 App"
serviceUrl: "https://prod-v15.ecv.example.com",
useQueueSocket: false
)
let configSeparateV15 = ECVSdkConfiguration(
name: "Some custom ECV 1.5 App",
serviceUrl: "https://api.example.com",
webrtcUrl: "https://webrtc-v15.example.com", // Separate endpoint for /join
signalingUrl: "https://websocket-v15.example.com", // Separate WebSocket server (if applicable for v1.5 signaling)
useQueueSocket: false, // Required for v1.5
waitForRoomIntervalDelay: 3000, // Poll every 5 seconds
waitForRoomIntervalEndpoint: "/custom/pollstatus?callid=" // Custom polling endpoint
)
Call Configuration
Basic Call Configuration
The DialCallParameters class is used to configure individual calls:
let dialCallParameters = DialCallParameters() // No required parameters
Optional DialCallParameters Parameters
callerName: String Name of the caller, Optional but should be provided, otherwise a random name will be generatedattributes: [String: String] Attach data for the callqueue: String if not provided, will use system's default queueacdAttributes: [String: String] Extra custom acdAttributes for the call (Advanced Backend Settings)customPayload: [String: String] Extra custom payload for the call (Advanced Backend Settings)additionalHeaders: [String: String] Extra http headers can be applied to the dialRequest
Example Usages:
let dialCallParameters = DialCallParameters(
callerName: "Jane Doe",
attributes: [
"accountNumber" to "ACC9876",
"lastInteraction" to "2023-10-26"
],
queue: "SupportQueue",
additionalHeaders: ["X-Auth-Token": "user_session_token"]
)
await call.start(dialCallParameters: dialCallParameters)
WebRTC Connection Parameters
These parameters are optionally provided when you call videoCall.connect() and fine-tune the WebRTC peer connection behavior. Most parameters have sensible defaults.
-
Connection & Signaling:
connectionTimeout: Int = 10000: Timeout (ms) for the overall WebRTC connection attempt (used by a manual timer inWebRtcConnection).joinHttpConnectTimeout: Int = 3000: HTTP connect timeout (ms) for the/joinrequest.joinHttpReadTimeout: Int = 3000: HTTP read timeout (ms) for the/joinrequest.additionalHeaders: [String: String]: Custom HTTP headers added to the/joinrequest and WebSocket signaling connection handshake. Useful for authentication tokens that need to reach the WebRTC/Signaling infrastructure.
-
Media Control & Behavior:
videoCallEnabled: Boolean = true: Whether video tracks should be set up. Set tofalsefor audio-only calls.useSpeakerPhone: Boolean = true: Start the call using the speakerphone.autoHandleCameraSwitch: Boolean = true: Allows the SDK to automatically switch cameras when certain events occur (e.g., agent requests photo with back camera).autoHandleHoldSideEffects: Boolean = true: Automatically mutes/unmutes local audio/video when the agent puts the call on hold or resumes it.
-
Video Configuration:
videoWidth: Int = 480/videoHeight: Int = 640: Requested video resolution.videoFps: Int = 0(Defaults to ~30): Requested video frame rate.videoMaxBitrate: Int = 0(WebRTC default): Maximum bitrate (kbps) for video encoding.videoCodec: String = "VP8": Preferred video codec ("VP8", "VP9", "H264").videoCodecHwAcceleration: Boolean = true: Enable hardware acceleration if available.
-
Audio Configuration:
audioCodec: String = "OPUS": Preferred audio codec ("OPUS", "ISAC").audioStartBitrate: Int = 0(WebRTC default): Initial audio bitrate (kbps).noAudioProcessing: Boolean = false: Disable platform audio processing (AEC, AGC, NS).disableBuiltInAEC: Boolean = false/disableBuiltInNS: Boolean = false: Disable hardware AEC/NS if available.
-
Debugging:
tracing: Boolean = false: Enable WebRTC internal tracing.
-
Data Channel (
textChannelConfig):textChannelConfig: DataChannelConfig? = DataChannelConfig(...): Configuration for the built-in text data channel used for commands like FLASH_ON/OFF, HOLD/PICKUP, etc. Usually left at default. SeeDataChannelConfigclass for details.
Example Usage:
let customConnectParams = ConnectParameters(
videoWidth: 1280,
videoHeight: 720,
videoCodec: "H264",
additionalHeaders: ["X-WebRTC-Auth": "webrtc_token"]
)
// Get the WebRtcConnection using the custom parameters
let webRtcConnection = videoCall.connect(parameters: customConnectParams)
webRtcConnection.startRoom()
DataChannelConfig
DataChannelConfig is a configuration model used to initialize a data channel in a peer-to-peer communication environment.
Properties
enabled:BoolWhether the data channel is enabled.label:StringA human-readable label for the data channel.id:IntID for the data channel (-1 if not set).negotiated:BoolWhether the channel is pre-negotiated (manual setup).protocol:StringSubprotocol name (renamed to avoid Swift keyword conflict).ordered:BoolWhether messages must arrive in order.maxRetransmits:IntMax number of retransmits (-1 if unset).maxRetransmitTimeMs:IntMax time for retransmissions in milliseconds (-1 if unset).
Initializer
init(
enabled: Bool,
label: String,
id: Int = -1,
negotiated: Bool = false,
protocol: String = "",
ordered: Bool = true,
maxRetransmits: Int = -1,
maxRetransmitTimeMs: Int = -1
)
Demo Application Guide
Overview
The ECV175_iOS_DEMO application serves as a practical example of how to integrate and utilize the ECV Video Call SDK in an iOS application. This guide will walk you through the application's architecture, key components, and how to use it effectively.
Application Architecture
Core Components
-
App Structure
- Built using SwiftUI framework with modern iOS development practices
- Follows MVVM (Model-View-ViewModel) architecture pattern
- Uses SwiftUI's navigation system for screen management
- Implements type-safe navigation using
NavigationRouteenum - Utilizes environment objects for dependency injection
-
State Management
-
AppState: Central state management class that handles:- Environment configuration persistence using UserDefaults
- SDK instance lifecycle management
- Environment selection and storage
- Reactive UI updates using
@Publishedproperties
-
ConfigurationManager: Manages environment configurations- Handles predefined and custom environments
- Provides CRUD operations for environment configurations
- Persists environment settings
-
-
Navigation System
-
Type-safe navigation using
NavigationRouteenum with three main routes:mainScreen: Entry point after environment selectionqueueScreen(rtcId:name:queue:): Queue management interfacecallScreen(rtcId:): Video call interface
-
Implements proper back navigation and deep linking support
-
Uses SwiftUI's
NavigationStackfor modern navigation
-
Key Features
-
Environment Selection
-
Supports multiple environment configurations:
- Predefined environments
- Custom environments with configurable settings
-
Environment configuration includes:
- Service URL
- WebRTC URL (optional)
- Signaling URL (optional)
- Queue socket configuration
- Room interval settings
-
Persists selected environment using UserDefaults
-
Automatic SDK initialization with selected environment
-
-
Video Call Integration
-
Seamless integration with ECV Video Call SDK
-
Features include:
- Real-time video streaming
- Audio controls (mute/unmute)
- Video controls (enable/disable)
- Camera switching
- Flashlight control
- Picture-in-Picture support
-
Queue management system:
- Join existing queues
- Create new queues
- Monitor queue status
-
Call event handling:
- Call state management
- Error handling
- Connection status monitoring
-
Getting Started
Prerequisites
- Xcode 14.0 or later
- iOS 15.0 or later
- ECV175_iOS_SDK.xcframework
- WebRTC.xcframework
- Camera and microphone permissions
- Internet connectivity
Setup Process
-
Project Configuration
-
Open
ECV175_iOS_DEMO.xcodeproj -
Ensure both demo app target is properly configured
-
Check that all dependencies are correctly linked
-
Configure necessary permissions in Info.plist:
- Camera usage description
- Microphone usage description
-
-
Environment Configuration
-
Launch the application
-
Select your desired environment from the environment selection screen
-
Configure environment settings:
- Service URL (required)
- WebRTC URL (optional)
- Signaling URL (optional)
- Queue socket settings
-
The app will automatically initialize the SDK with the selected configuration
-
Application Flow
-
Initial Launch
- App checks for previously saved environment configuration
- If found, automatically navigates to main screen
- If not, presents environment selection screen
- Requests necessary permissions (camera, microphone)
-
Main Screen
-
Displays available options for video calls
-
Shows current environment configuration
-
Provides access to:
- Queue selection
- Call initiation
- Environment settings
-
Handles permission management
-
Initializes video call instance
-
-
Queue Management
- Join existing queues
- Create new queues
- Monitor queue status
- Configure queue settings
- Handle queue events
-
Video Call Interface
-
Real-time video streaming with:
- Local video preview
- Remote video display
- Picture-in-Picture support
-
Call controls:
- Audio mute/unmute
- Video enable/disable
- Camera switching
- Flashlight control
-
Call management:
- Hang up functionality
- Error handling
- Connection status monitoring
-
Code Structure
Key Files
-
ECV175_iOS_DEMOApp.swift
-
Application entry point
-
Sets up main navigation structure
-
Initializes core state objects:
- AppState
- ConfigurationManager
-
Configures environment objects
-
-
AppState.swift
- Manages application state
- Handles environment persistence
- Controls SDK lifecycle
- Implements state restoration
- Manages SDK instance
-
Views Directory
-
Screens/:EnvironmentsScreen.swift: Environment selection and configurationMainScreen.swift: Main interface and call initiationCallScreen.swift: Video call interfaceQueueScreen.swift: Queue management
-
Components/: Reusable UI components -
Implements SwiftUI views with proper state management
-
Best Practices
-
State Management
- Use
@EnvironmentObjectfor dependency injection - Maintain clean separation of concerns
- Implement proper error handling
- Use
@Publishedproperties for reactive updates - Handle state persistence appropriately
- Use
-
Navigation
- Use type-safe navigation routes
- Implement proper back navigation
- Handle deep linking scenarios
- Manage navigation state
- Handle navigation errors
-
SDK Integration
- Initialize SDK only when needed
- Properly handle SDK lifecycle
- Implement error handling for SDK operations
- Manage resources efficiently
- Handle SDK events properly
Troubleshooting
-
Common Issues
-
SDK initialization failures:
- Check environment configuration
- Verify network connectivity
- Ensure proper permissions
-
Navigation state inconsistencies:
- Verify navigation routes
- Check state management
- Handle navigation errors
-
Environment configuration problems:
- Validate configuration settings
- Check URL formats
- Verify required fields
-
-
Debug Tips
- Check console logs for detailed error messages
- Verify environment configuration
- Ensure proper SDK initialization
- Monitor network connectivity
- Check permission status
- Use Xcode debugging tools
SDK Integration and Technical Details
Core Configuration and Build System
-
Build.swift
-
Manages different build flavors and environments
-
Supports multiple bundle identifiers:
ccr.ECV175-iOS-DEMO.CORE: Core demo environment
-
Each environment configuration includes:
-
Service URL
-
Queue configurations
-
Predefined settings
- such as
callAttributes
- such as
-
-
-
AppState.swift
-
Central state management for SDK integration
-
Key responsibilities:
- SDK instance lifecycle management
- Environment configuration persistence
- State restoration
- Event handling coordination
-
Uses
@Publishedproperties for reactive updates -
Implements UserDefaults persistence for environment selection
-
Screen-by-Screen SDK Integration
-
EnvironmentsScreen
-
Environment selection and configuration
-
SDK Integration:
- Manages environment configurations
- Handles predefined and custom environments
- Persists environment selection
- Initializes SDK with selected configuration
-
-
MainScreen
-
Primary interface for call initiation
-
SDK Integration:
-
Initializes video call instance
-
Manages permissions (camera, microphone)
-
Handles queue selection
-
Sets up call event listeners
-
Key features:
// SDK initialization let call = await appState.sdk?.createVideoCall() // Event handling ECVModules?.onCallEvent.append { (videoCall, newEvent, lastEvent) in // Handle call events }
-
-
-
QueueScreen
-
Queue management and agent connection
-
SDK Integration:
-
Handles queue joining
-
Manages agent connection
-
Supports both ECV 1.75 and 1.5 versions
-
Key features:
``` // Queue joining let dialCallParameters = DialCallParameters( callerName: name, queue: queue, attributes: [queueKey: queueValue] )// Event listening let ECVModules = appState.sdk?.modules ECVModules?.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in if(videoCall.rtcId != call?.rtcId){ return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events }
// Agent connection handling if(newEvent.type == .AgentConnected) { // AGENT IS CONNECTED // GO TO CALL SCREEN } else if(newEvent.type == .Error){ // showError = true }}
// Start the call await call?.start(dialCallParameters: dialCallParameters)
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl"> </div></div>
-
-
-
CallScreen
-
Video call interface
-
SDK Integration:
-
Manages WebRTC connection
-
Handles video/audio streams
-
Controls call features
-
Key features:
// WebRTC setup let webRtcConnection = videoCall?.connect() // Stream management localRenderer = webRtcConnection?.localStream remoteRenderer = webRtcConnection?.remoteStream // Call controls func toggleLocalAudio() func toggleLocalVideo() func toggleFlashlight() func switchCamera() func switchViewRenderers()
-
-
SDK Version Compatibility
-
ECV 1.75 Features
-
Queue socket support
-
Enhanced event handling
-
Improved agent connection management
-
Better state management
-
-
ECV 1.5 Features
- Traditional queue management
- Interval-based agent connection
- Basic event handling
- Legacy support
Event Handling System
-
Call Events
-
Event types:
AgentConnectedCallEndedError
-
Event handling:
ECVModules?.onCallEvent.append { (videoCall, newEvent, lastEvent) in switch newEvent.type { case .AgentConnected: // Handle agent connection case .CallEnded: // Handle call end case .Error: // Handle errors } }
-
-
State Management
- Call state tracking
- Connection state monitoring
- Error state handling
- Queue state management
Best Practices for SDK Integration
-
Initialization
- Initialize SDK only when needed
- Properly handle configuration
- Manage SDK lifecycle
- Handle initialization errors
-
Event Handling
- Use proper event filtering
- Handle all event types
- Implement error handling
- Clean up event listeners
-
Resource Management
- Properly dispose of resources
- Handle cleanup on app termination
- Manage memory efficiently
- Handle background/foreground transitions
-
Error Handling
- Implement comprehensive error handling
- Provide user feedback
- Handle network issues
- Manage SDK-specific errors
Conclusion
The ECV175_iOS_DEMO application provides a solid foundation for implementing video call functionality using the ECV Video Call SDK. By following this guide and understanding the application's architecture, you can effectively integrate video calling features into your own iOS applications.
For more detailed information about specific features or implementation details, please refer to the SDK documentation and API reference.
Events and State Management
Introduction
The ECV iOS SDK operates asynchronously. It uses an event-driven architecture to communicate status changes, errors, and other important occurrences to your application. Understanding these events is crucial for building a responsive and robust UI.
This guide details the structure of SDK events, lists the key event types, and mentions relevant state properties.
Subscribing to Events
You listen for events on a sdk.modules.onCallEvent instance:
sdk.modules.onCallEvent.append { (videoCall: VideoCall, newEvent: VideoCallEvent, lastEvent: VideoCallEvent) in
if(videoCall.rtcId != call?.rtcId){
return // Making sure we only catch the events related to the current call. // comment this code to catch all calls' events
}
if(newEvent.type == .Error){
// Handle Error event
}
else if(newEvent.type == .AgentConnected) {
// Handle AgentConnected event
}
else {
print("From ECVModules?.onCallEvent: \(newEvent)")
}
}
Refer to the "Video Call Event Handling" section in Usage Guide for detailed code examples of both subscription methods.
Event Structure (VideoCallEvent)
Key Fields:
type: VideoCallEventType: (Required) An enum value indicating the kind of event that occurred. This is the primary field you'll check to determine how to react.detail: String?: (Optional) Provides additional string information specific to the event (e.g., thertcIdforGotRtcId, an interaction ID forDialSuccess, a reason forCallEnded).objData: Any?: (Optional) Can contain related objects, such as theWebRtcConnectionduringSignalingConnectedor an HTTP response object on certain errors. Use type checking (isorinstanceof) if accessing this data.data: [String: String]: (Optional, often empty) A generic map for potential key-value data associated with the event.createdAt: Date: Date when the event was created within the SDK.
Event Type Reference (VideoCallEventType)
Here's a breakdown of common event types, grouped by the typical phase of a call:
Phase 1: Initialization & Dialing
-
GotRtcId- When: Fired after
sdkInstance.createVideoCall()successfully retrieves a unique call identifier from the backend. - Data:
event.detailcontains thertcId(String). - Response: Store the
rtcId. You need it to retrieve theVideoCallinstance later and to initiate dialing (call.start()).
- When: Fired after
-
DialSuccess- When: Fired after
call.start()successfully sends the dial request to the backend and receives confirmation (often includes an Interaction ID). - Data:
event.detailmay contain the Interaction ID.event.objDatamight hold the raw JSON response. - Response: Usually indicates you can proceed to the waiting/queue state.
- When: Fired after
-
Error(During Init/Dial)- When: Can occur if fetching
rtcIdfails,call.start()parameters are invalid, the backend rejects the dial request, or network issues occur. - Data:
event.errorcontains theECVError.event.detailmay have more context.event.objDatamight hold an HTTP response. Checkerror.errorfor specificErrorTypeslikeCALL_NOT_INITIALIZED,DIAL_FAILED. - Response: Display an error message to the user. Prevent further call progress. Potentially clean up the
VideoCallinstance if it's unusable.
- When: Can occur if fetching
Phase 2: Queue & Agent Connection
-
OnQueue- When: Fired after a successful dial, indicating the caller is now waiting for an agent.
- Data: Typically no specific data needed.
- Response: Show a waiting UI (like
QueueScreen).
-
AgentConnected-
When: Fired when an agent accepts the call. This is the trigger to establish the peer-to-peer media connection.
-
Data: None specific.
-
Response: Initiate the WebRTC connection:
- Call
videoCall.connect()to get theWebRtcConnection. - Call
webRtcConnection.startRoom()to begin WebRTC setup. - Navigate to the main call screen (
CallScreen).
- Call
-
-
Error(During Queue)- When: Can occur due to queue timeouts, network issues with the queue mechanism (
QUEUE_SOCKET_ERROR), or backend problems (WAIT_FOR_ROOM_FAILEDif using polling (in 1.5)). - Data:
event.errorcontains theECVError. - Response: Inform the user the call couldn't connect. Navigate back from the queue screen. Call
videoCall.hangup()to ensure cleanup if the SDK doesn't automatically triggerCallEnded.
- When: Can occur due to queue timeouts, network issues with the queue mechanism (
-
Hangup/CallEnded(During Queue)- When: If the user explicitly hangs up (
videoCall.hangup()), or the call is terminated by the backend before an agent connects. - Data:
event.detailmight contain a reason ("LOCAL_HANGUP", etc.). - Response: Navigate back from the queue screen. Ensure UI is cleaned up.
- When: If the user explicitly hangs up (
Phase 3: WebRTC Connection Establishment
These events are typically handled internally by WebRtcConnection but can be observed for debugging or advanced use cases. The key event signaling success is WebRtcConnected.
-
SignalingConnected: WebSocket connection to the signaling server established. -
SignalingRegistered: Client successfully registered with the signaling server for the room (rtcId). -
JoinedRoom: HTTP/joinrequest successful, received initial parameters (like ICE servers, initiator status).event.objDatacontainsJoinRoomResponse. -
RtcPeerConnectionCreated: The underlying WebRTCPeerConnectionobject has been created.event.objDataholds thePeerConnectionClient. -
GotOffer/SentOffer: SDP Offer received from / sent to the peer.event.objDatahas the JSON message. -
GotAnswer/SentAnswer: SDP Answer received from / sent to the peer.event.objDatahas the JSON message. -
WebRtcConnected- When: The WebRTC PeerConnection (ICE and DTLS) is fully established and media can start flowing.
- Data: None specific.
- Response: Often used as a trigger to know the media path is ready. You might hide initial loading indicators on the
CallScreen.
-
TextChannelConnected: The specific data channel used for text commands (hold, flash, etc.) is open.event.objDataholds theDataChannel. -
WebRtcDisconnected/WebRtcFailed: The WebRTC connection dropped or failed unexpectedly. Usually leads to aCallEndedevent shortly after.
Phase 4: Active Call & Media
-
GotLocalStream: Local camera capture has started.event.objDatamight contain theVideoCapturer. -
GotRemoteStream: Remote media stream is available (often triggered aroundWebRtcConnected).- Response: Ensure remote
SurfaceViewRendereris attached.
- Response: Ensure remote
-
AudioMuted/AudioUnmuted: Local audio sending state changed (triggered bysetAudioEnabled).- Response: Update the mute button UI state.
-
VideoMuted/VideoUnmuted: Local video sending state changed (triggered bysetVideoEnabled).- Response: Update the video mute button UI state. Potentially show/hide the local video preview overlay.
-
RemoteAudioMuted/RemoteAudioUnmuted: The remote participant muted/unmuted their audio.- Response: Optionally display an indicator that the remote party is muted.
-
RemoteVideoMuted/RemoteVideoUnmuted: The remote participant muted/unmuted their video.- Response: Show/hide an overlay or indicator on the remote video renderer.
-
StartHold/EndHold: The call was put on hold or taken off hold by the agent (usually triggered via the text data channel).- Response: Show/hide a "Call on Hold" overlay. The SDK (if
autoHandleHoldSideEffectsis true) might automatically mute/unmute local media in response.
- Response: Show/hide a "Call on Hold" overlay. The SDK (if
-
TakePhotoOn/TakePhotoOff: Signals related to agent-initiated photo capture features.- Response: Adjust UI if needed (e.g., potentially force fullscreen local video). The SDK might automatically switch cameras if
autoHandleCameraSwitchis true.
- Response: Adjust UI if needed (e.g., potentially force fullscreen local video). The SDK might automatically switch cameras if
-
TextChannelMessage: A command or message received over the text data channel.- Data:
event.detailcontains the raw message string (e.g., "FLASH_ON", "STATUS|VIDEO|MUTED|rtcId"). - Response: SDK handles many standard messages internally (like HOLD, PICKUP, MUTE statuses, FLASH commands). You might react to custom messages specific to your implementation.
- Data:
Phase 5: Call Termination
-
Hangup- When: Fired immediately when
videoCall.hangup()is called locally. - Data: None specific.
- Response: Can be used to initiate UI cleanup before the backend confirms the call end. Note that
CallEndedis the definitive end event.
- When: Fired immediately when
-
CallEnded-
When: The final event indicating the call session is completely terminated, either locally (
hangup()completion), remotely (agent/peer hangs up), or due to an unrecoverable error. -
Data:
event.detailoften contains a reason (e.g., "LOCAL_HANGUP", "REMOTE_HANGUP", "PEER_DISCONNECTED", "CONNECTION_TIMEOUT", error description). -
Response: This is the definitive signal to:
- Clean up all call-related UI (renderers, controls).
- Navigate away from the call screen.
- Release any resources tied to the specific
VideoCall. The SDK typically removes its internal reference to theVideoCallinstance after this event.
-
State Properties
While events signal changes, you can also check some current state properties:
-
On
VideoCall:rtcId: String: The unique call identifier (available afterGotRtcId).dialResponse: [String: Any]?: When theDialSuccessevent occurs, this property contains the raw dial response.agentConnected: Boolean: True if theAgentConnectedevent has occurred.webRtcConnection: WebRtcConnection?: The associated WebRTC connection (available afterconnect()is called).
It's generally best practice to react to events to update your UI state rather than constantly polling these properties. Use the properties for initial state checks or decisions where needed (e.g., checking AgentConnected before calling connect()).
Customization Guide
Introduction
The provided Demo Application (ECV175_iOS_DEMO) showcases the core functionalities of the ECV SDK. However, your production application will undoubtedly have different UI/UX requirements, configuration methods, and data needs. This guide provides advice on adapting the SDK and demo concepts to your specific application.
1. UI Implementation
The demo uses SwiftUI, but the SDK itself is UI-agnostic. You can integrate it into applications built with:
- SwiftUI
- UIKit
- Any combination thereof.
Key Considerations for Your UI:
-
Build Your Own UI: Do not simply copy the demo UI screens. Design screens that match your application's look, feel, and user flow.
-
Core SDK Interactions Remain the Same: Regardless of your UI toolkit, the fundamental interactions with the SDK persist:
-
Permissions: You must request and handle Camera, Microphone (and Bluetooth Connect for S+) permissions before initiating a call.
-
Video Renderers: You need to provide
RTCMTLVideoViewinstances in your layout to display video. -
Renderer Lifecycle: This is critical. You MUST correctly manage the lifecycle of
RTCVideoRenderers:- Initialize them using
UIView. - Attach them to local/remote streams using
webRtcConnection.localVideoRenderer/remoteVideoRenderer.
- Initialize them using
-
Call State Display: Your UI must reflect the current call status (e.g., "Connecting...", "Waiting for agent...", "Connected", "On Hold") based on SDK events.
-
Call Controls: Implement UI elements (Buttons, Icons) for:
- Muting/Unmuting Audio (
webRtcConnection.setAudioEnabled(...)) - Toggling Video (
webRtcConnection.setVideoEnabled(...)) - Switching Camera (
webRtcConnection.switchCamera(...)) - Hanging Up (
videoCall.hangup())
- Muting/Unmuting Audio (
-
Event Handling: Your UI layer must listen to SDK events (see
Events and State Management) to update its state, navigate between screens, and handle errors.
-
2. Configuration (ECVSdkConfiguration)
The demo's EnvironmentsScreen.swift is designed for easily testing different backend environments. You will likely not need this screen in your production application.
Production Configuration Strategies:
-
Fixed Configuration: If your app only ever connects to one backend environment (e.g., production), you can hardcode the
ECVSdkConfigurationvalues when initializing theECVSdk.// Example: Inside Application.onCreate or DI module let prodConfig = ECVSdkConfiguration( name: "My App Prod" serviceUrl = "https://your-prod.ecv.example.com", useQueueSocket = true // Assuming backend is v1.75+ // ... other params if needed ... ) let sdkInstance = ECVSdk("MyAppProdInstance", prodConfig) -
Dynamic Configuration: Fetch configuration details (like
serviceUrl,webrtcUrl) from your own application backend before initializing theECVSdk. This allows you to manage endpoints centrally without requiring app updates.- Your app fetches the config from your server.
- Uses the fetched URLs to create the
ECVSdkConfiguration. - Initializes
ECVSdk.
Important: Ensure the useQueueSocket parameter in your chosen ECVSdkConfiguration matches the capabilities of your target ECV backend (false for v1.5, true recommended for v1.75+).
3. Call Data (DialCallParameters)
The demo uses selectableQueues in Build.swift to dynamically add dropdowns (queue selection) for call data based on the selected environment. This is specific to the demo's multi-environment setup.
In your application, you should populate DialCallParameters with relevant contextual data just before calling videoCall.start().
Example:
// User profile data
val userId = "user123"
val accountType = "Premium"
val intent = "Billing Inquiry"
// Target queue/skill
val targetQueue = "BillingSupport"
// Get SDK instance and create call
val videoCall = sdkInstance.createVideoCall()
// ... wait for GotRtcId ...
// Build parameters with app-specific data
val dialParams = DialCallParameters(
callerName = "User $userId", // Or actual user name
attributes = [
"userId": userId,
"accountType": accountType,
"callIntent": intent
],
acdAttributes = [
"skill": "Billing" // Example ACD attribute
],
queue = targetQueue, // Explicitly set the target queue
additionalHeaders = ["Authorization": "Bearer \(getUserAuthToken())"] // Example auth
)
// Start the call
videoCall.start(dialParams)
// Navigate to Queue/Waiting screen
4. Event Handling Logic
While the demo shows how to subscribe to events, what you do in response is application-specific. Ensure your implementation correctly handles key events for:
5. Advanced Customization (Optional)
- Custom Data Channel Messages: If your backend sends custom commands via the text data channel (beyond HOLD, FLASH, etc.), you can listen for the
TextChannelMessageevent and parseevent.detailto implement custom application behavior. ConnectParametersTuning: For specific network environments or quality requirements, you might experiment with adjusting parameters inConnectParameters, such asvideoMaxBitrate, codecs, or ICE filtering modes. Consult WebRTC documentation for details on these advanced options.- Audio Management: While defaults (
useSpeakerPhone, etc..) are provided inConnectParameters, more complex audio routing might require deeper integration or customization beyond the scope of the basic SDK usage.
6. Permissions
Always follow iOS's best practices for requesting permissions:
- Request permissions only when needed (just before initiating the call).
- Clearly explain why the permissions (Camera, Microphone) are required.
- Handle cases where the user denies permissions gracefully (e.g., disable calling functionality, provide instructions on how to enable them in settings).
Troubleshooting Guide
This guide provides solutions and debugging steps for common issues encountered while integrating or using the ECV iOS SDK.
Common Issues & Solutions
Build & Dependency Errors
-
Error: "Could not find module 'ECV175_iOS_SDK'"
-
Cause: Xcode cannot locate the required framework files.
-
Solution:
- Ensure the ECV SDK framework is properly embedded in your project.
- Verify that the framework is listed in your target's "Frameworks, Libraries, and Embedded Content" section.
- Check that the framework search paths in your project settings include the correct location.
- Clean the build folder (Cmd + Shift + K) and rebuild the project.
-
-
Error: "Undefined symbols for architecture"
-
Cause: Missing required dependencies or architecture mismatches.
-
Solution:
- Ensure all required frameworks are properly linked in your project.
- Verify that the deployment target and architectures match between your app and the SDK.
- Check that you're using compatible versions of dependencies.
- Clean and rebuild the project.
-
-
Error: "Code signing failed"
-
Cause: Issues with code signing or provisioning profiles.
-
Solution:
- Verify your Apple Developer account is active.
- Check that your provisioning profile includes the necessary entitlements.
- Ensure your signing certificate is valid and properly installed.
- Update your provisioning profiles if needed.
-
Runtime Crashes
-
Crash: "Thread 1: EXC_BAD_ACCESS"
-
Cause: Memory access issues, often related to WebRTC or native code.
-
Solution:
- Ensure proper initialization of the SDK before use.
- Check for proper memory management in your implementation.
- Verify that all required permissions are granted.
- Use the debugger to identify the exact point of crash.
-
-
Crash: "Fatal error: Unexpectedly found nil while unwrapping an Optional value"
-
Cause: Accessing an SDK object before it's initialized or after it's been cleaned up.
-
Solution:
- SDK Not Initialized: Ensure
ECVSdk.shared.initialize(...)is called before accessing any SDK functionality. - VideoCall is Nil: Verify that the
rtcIdis valid and the call hasn't ended. - WebRTC Connection is Nil: Ensure
videoCall.connect()was called successfully after theAgentConnectedevent. - View Controllers: Ensure proper view controller lifecycle management.
- SDK Not Initialized: Ensure
-
-
Crash: Related to WebRTC or Media Handling
-
Cause: Improper lifecycle management of WebRTC components.
-
Solution:
- Ensure proper cleanup of WebRTC resources in
deinitorviewWillDisappear. - Verify camera and microphone permissions are granted.
- Check for proper handling of background/foreground transitions.
- Ensure proper thread handling for WebRTC operations.
- Ensure proper cleanup of WebRTC resources in
-
-
Crash: "Permission Denial"
-
Cause: Attempting to access camera or microphone without proper permissions.
-
Solution:
- Implement proper permission request flow using
AVCaptureDevice.requestAccess. - Check permission status before enabling call functionality.
- Handle permission denial gracefully in the UI.
- Verify Info.plist contains necessary permission descriptions.
- Implement proper permission request flow using
-
Media Quality Issues
-
Poor Video/Audio Quality (Lag, Freezing, Pixelation, Garbled Audio)
-
Debugging:
- Network: Check network bandwidth, latency, and packet loss. Test on different networks (Wi-Fi vs Cellular).
- Device Performance: Older devices might struggle with encoding/decoding at higher resolutions.
- Configuration: Consider adjusting video resolution, frame rate, or bitrate settings.
- WebRTC Stats: Enable WebRTC statistics collection for detailed performance metrics.
-
Specific Feature Issues
-
Camera Switch Doesn't Work
-
Debugging:
- Ensure
webRTCConnection.switchCamera()is being called. - Check logs for errors during the switch attempt.
- Verify the device has multiple cameras available.
- Ensure proper camera initialization.
- Ensure
-
-
Hold/Pickup Doesn't Mute/Unmute Automatically
-
Debugging:
- Verify
autoHandleHoldSideEffectsis enabled in configuration. - Check if hold/pickup events are being received correctly.
- Verify text channel connection status.
- Check event handling implementation.
- Verify
-
General Debugging Steps
- Check Console Logs: Use Xcode's console to monitor SDK logs. Filter by relevant tags and look for warnings and errors.
- Verify Configuration: Double-check
ECVSdkConfiguration,DialCallParameters, and any custom parameters against your environment requirements. - Check Network Connectivity: Verify the device can reach required endpoints. Test on different networks and check for any firewall issues.
- Simplify Implementation: Start with basic configuration and gradually add complexity to isolate issues.
- Isolate the Phase: Determine when the failure occurs: SDK initialization, dialing, queue, WebRTC connection, or media rendering.
- Consult Demo App: Compare your implementation with the provided demo source code, especially around event handling and view controller lifecycle management.
- Log All Events: Add logging to your event handling to track the call flow and identify missing or unexpected events.
Contacting Support
When contacting support, please provide:
- SDK Version: (e.g., ECV-175-iOS-SDK-v1.2.3)
- Device(s) Tested: iPhone/iPad model, iOS version
- Backend Version: ECV v1.5 or v1.75+
- Configuration: Your
ECVSdkConfiguration, relevantDialCallParameters, and any custom parameters - Detailed Problem Description: Symptoms, reproduction steps, expected vs actual behavior
- Relevant Console Logs: Capture logs during the issue, including SDK and application logs
- Screenshots/Videos: If helpful to illustrate the problem