Skip to main content

Overview

Add one-on-one and group audio/video calling to your iOS app. Once integrated, call buttons automatically appear in chat headers, and incoming/outgoing call screens are handled for you.

Quick Setup

1

Add Dependencies

Add to your Podfile:
platform :ios, '13.0'
use_frameworks!

target 'YourApp' do
  pod 'CometChatUIKitSwift', '5.1.7'
  pod 'CometChatCallsSDK', '4.2.2'
end
Run:
pod install
2

Add Permissions

Add to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access required for video calls</string>

<key>NSMicrophoneUsageDescription</key>
<string>Microphone access required for voice and video calls</string>
3

Initialize CometChat

In SceneDelegate.swift:
import CometChatUIKitSwift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    
    let uikitSettings = UIKitSettings()
        .set(appID: "YOUR_APP_ID")
        .set(authKey: "YOUR_AUTH_KEY")
        .set(region: "YOUR_REGION")  // "us", "eu", or "in"
        .build()
    
    CometChatUIKit.init(uiKitSettings: uikitSettings) { result in
        switch result {
        case .success:
            CometChatUIKit.login(uid: "cometchat-uid-1") { loginResult in
                switch loginResult {
                case .success:
                    // Calling is now enabled automatically
                    print("✅ Ready for calls")
                case .onError(let error):
                    print("❌ Login failed: \(error.description)")
                @unknown default:
                    break
                }
            }
        case .failure(let error):
            print("❌ Init failed: \(error.localizedDescription)")
        }
    }
}
That’s it! Call buttons now appear in CometChatMessages header automatically.

Production-Ready Implementation

Complete app with calling, chat, and call history:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        
        // Initialize CometChat
        let uikitSettings = UIKitSettings()
            .set(appID: "YOUR_APP_ID")
            .set(authKey: "YOUR_AUTH_KEY")
            .set(region: "YOUR_REGION")
            .subscribePresenceForAllUsers()
            .build()
        
        CometChatUIKit.init(uiKitSettings: uikitSettings) { [weak self] result in
            switch result {
            case .success:
                print("✅ CometChat initialized")
                self?.loginAndSetupUI(windowScene: windowScene)
                
            case .failure(let error):
                print("❌ Init failed: \(error.localizedDescription)")
            }
        }
    }
    
    private func loginAndSetupUI(windowScene: UIWindowScene) {
        CometChatUIKit.login(uid: "cometchat-uid-1") { [weak self] result in
            switch result {
            case .success:
                print("✅ Logged in")
                DispatchQueue.main.async {
                    self?.setupMainInterface(windowScene: windowScene)
                }
                
            case .onError(let error):
                print("❌ Login failed: \(error.description)")
                
            @unknown default:
                break
            }
        }
    }
    
    private func setupMainInterface(windowScene: UIWindowScene) {
        let tabBar = MainTabBarController()
        
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = tabBar
        window?.makeKeyAndVisible()
    }
}

Initiate Calls Programmatically

Voice Call to User

import CometChatSDK

func startVoiceCall(to userUID: String) {
    let call = CometChat.Call(
        receiverId: userUID,
        callType: .audio,
        receiverType: .user
    )
    
    CometChat.initiateCall(call: call) { call in
        print("✅ Voice call started: \(call?.sessionId ?? "")")
    } onError: { error in
        print("❌ Call failed: \(error?.errorDescription ?? "")")
    }
}

// Usage
startVoiceCall(to: "cometchat-uid-2")

Video Call to User

import CometChatSDK

func startVideoCall(to userUID: String) {
    let call = CometChat.Call(
        receiverId: userUID,
        callType: .video,
        receiverType: .user
    )
    
    CometChat.initiateCall(call: call) { call in
        print("✅ Video call started: \(call?.sessionId ?? "")")
    } onError: { error in
        print("❌ Call failed: \(error?.errorDescription ?? "")")
    }
}

// Usage
startVideoCall(to: "cometchat-uid-2")

Group Call

import CometChatSDK

func startGroupCall(to groupGUID: String, type: CometChat.CallType) {
    let call = CometChat.Call(
        receiverId: groupGUID,
        callType: type,
        receiverType: .group
    )
    
    CometChat.initiateCall(call: call) { call in
        print("✅ Group call started: \(call?.sessionId ?? "")")
    } onError: { error in
        print("❌ Call failed: \(error?.errorDescription ?? "")")
    }
}

// Usage
startGroupCall(to: "group-guid", type: .video)

Custom Call Buttons

Add call buttons anywhere in your app:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class UserProfileViewController: UIViewController {
    
    var user: CometChat.User!
    
    private let voiceCallButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "phone.fill"), for: .normal)
        button.setTitle(" Voice Call", for: .normal)
        button.backgroundColor = .systemGreen
        button.tintColor = .white
        button.layer.cornerRadius = 12
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    private let videoCallButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "video.fill"), for: .normal)
        button.setTitle(" Video Call", for: .normal)
        button.backgroundColor = .systemBlue
        button.tintColor = .white
        button.layer.cornerRadius = 12
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    
    private func setupUI() {
        view.backgroundColor = .systemBackground
        
        let stackView = UIStackView(arrangedSubviews: [voiceCallButton, videoCallButton])
        stackView.axis = .horizontal
        stackView.spacing = 16
        stackView.distribution = .fillEqually
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(stackView)
        
        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32),
            
            voiceCallButton.heightAnchor.constraint(equalToConstant: 50),
            videoCallButton.heightAnchor.constraint(equalToConstant: 50)
        ])
        
        voiceCallButton.addTarget(self, action: #selector(voiceCallTapped), for: .touchUpInside)
        videoCallButton.addTarget(self, action: #selector(videoCallTapped), for: .touchUpInside)
    }
    
    @objc private func voiceCallTapped() {
        initiateCall(type: .audio)
    }
    
    @objc private func videoCallTapped() {
        initiateCall(type: .video)
    }
    
    private func initiateCall(type: CometChat.CallType) {
        guard let uid = user.uid else { return }
        
        let call = CometChat.Call(
            receiverId: uid,
            callType: type,
            receiverType: .user
        )
        
        CometChat.initiateCall(call: call) { [weak self] call in
            print("✅ Call initiated")
        } onError: { [weak self] error in
            self?.showError(error?.errorDescription ?? "Call failed")
        }
    }
    
    private func showError(_ message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Using CometChatCallButtons Component

The built-in call buttons component:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class ChatHeaderViewController: UIViewController {
    
    var user: CometChat.User!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCallButtons()
    }
    
    private func setupCallButtons() {
        let callButtons = CometChatCallButtons()
        callButtons.user = user
        
        // Customize actions
        callButtons.set(onVoiceCallClick: { user, group in
            print("Voice call to: \(user?.name ?? group?.name ?? "")")
        })
        
        callButtons.set(onVideoCallClick: { user, group in
            print("Video call to: \(user?.name ?? group?.name ?? "")")
        })
        
        callButtons.set(onError: { error in
            print("Error: \(error.errorDescription)")
        })
        
        // Add to navigation bar
        let hostingController = UIHostingController(rootView: callButtons)
        addChild(hostingController)
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: hostingController.view)
    }
}

Handle Incoming Calls

Incoming calls are handled automatically. For custom handling:
import CometChatSDK

class CallHandler: NSObject, CometChatCallDelegate {
    
    static let shared = CallHandler()
    
    func registerForCalls() {
        CometChat.calldelegate = self
    }
    
    // Called when receiving an incoming call
    func onIncomingCallReceived(incomingCall: CometChat.Call?, error: CometChat.CometChatException?) {
        guard let call = incomingCall else { return }
        
        print("📞 Incoming call from: \(call.sender?.name ?? "")")
        print("Call type: \(call.callType == .audio ? "Audio" : "Video")")
        
        // The UI Kit automatically shows the incoming call screen
        // Add custom logic here if needed
    }
    
    // Called when an outgoing call is accepted
    func onOutgoingCallAccepted(acceptedCall: CometChat.Call?, error: CometChat.CometChatException?) {
        guard let call = acceptedCall else { return }
        print("✅ Call accepted: \(call.sessionId ?? "")")
    }
    
    // Called when an outgoing call is rejected
    func onOutgoingCallRejected(rejectedCall: CometChat.Call?, error: CometChat.CometChatException?) {
        guard let call = rejectedCall else { return }
        print("❌ Call rejected: \(call.sessionId ?? "")")
    }
    
    // Called when an incoming call is cancelled
    func onIncomingCallCancelled(canceledCall: CometChat.Call?, error: CometChat.CometChatException?) {
        guard let call = canceledCall else { return }
        print("📵 Call cancelled: \(call.sessionId ?? "")")
    }
    
    // Called when a call ends
    func onCallEnded(endedCall: CometChat.Call?, error: CometChat.CometChatException?) {
        guard let call = endedCall else { return }
        print("📴 Call ended: \(call.sessionId ?? "")")
    }
}

// Register in AppDelegate or SceneDelegate
// CallHandler.shared.registerForCalls()

Calling Components Reference

CometChatCallButtons

Renders voice and video call buttons.
let callButtons = CometChatCallButtons()
callButtons.user = user  // or callButtons.group = group

// Callbacks
callButtons.set(onVoiceCallClick: { user, group in })
callButtons.set(onVideoCallClick: { user, group in })
callButtons.set(onError: { error in })

CometChatIncomingCall

Displays incoming call screen with accept/reject options.
let incomingCall = CometChatIncomingCall()
incomingCall.set(call: call)

incomingCall.set(onAcceptClick: { call in })
incomingCall.set(onDeclineClick: { call in })

CometChatOutgoingCall

Displays outgoing call screen while waiting for answer.
let outgoingCall = CometChatOutgoingCall()
outgoingCall.set(call: call)

outgoingCall.set(onCancelClick: { call in })

CometChatCallLogs

Displays call history. See Call Logs for full documentation.
let callLogs = CometChatCallLogs()
callLogs.set(onItemClick: { callLog, indexPath in })

Troubleshooting

ProblemSolution
Call buttons not showingVerify CometChatCallsSDK is in Podfile and run pod install
”Permission denied” errorAdd camera/microphone permissions to Info.plist
Calls not connectingCheck network connection and CometChat credentials
No incoming call UIEnsure CometChatCallsSDK is initialized before login
Audio not workingCheck device is not on silent mode