Skip to main content
Quick Reference for AI Agents & Developers
  • Initiate call: CometChat.initiateCall(call:onSuccess:onError:) — pass Call(receiverUid:callType:receiverType:)
  • Accept call: CometChat.acceptCall(sessionID:onSuccess:onError:)
  • Reject call: CometChat.rejectCall(sessionID:status:onSuccess:onError:) — status: .rejected, .busy
  • Cancel call: CometChat.rejectCall(sessionID:status:onSuccess:onError:) — status: .cancelled
  • Call listener: CometChat.addCallListener("UNIQUE_ID", self)
  • Related: Call Session · Call Logs · Calling Overview

Overview

This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as Default Calling.
After the call is accepted, you need to start the call session. See the Call Session guide for details on starting and managing the actual call.
Call Flow:
  1. Caller initiates a call using initiateCall()
  2. Receiver gets notified via onIncomingCallReceived() callback
  3. Receiver can either:
    • Accept the call using acceptCall()
    • Reject the call using rejectCall() with status .rejected
  4. Caller can cancel the call using rejectCall() with status .cancelled
  5. Once accepted, both participants call startSession() to join the call

Initiate Call

The initiateCall() method sends a call request to a user or a group. On success, the receiver gets an onIncomingCallReceived() callback.
let receiverID = "UID"
let receiverType: CometChat.ReceiverType = .user // or .group
let callType: CometChat.CallType = .video // or .audio

let newCall = Call(receiverId: receiverID, callType: callType, receiverType: receiverType)

CometChat.initiateCall(call: newCall, onSuccess: { call in
    // Call initiated, show outgoing call UI
    // Store call.sessionID for later use
    print("Call initiated successfully")
}) { error in
    print("Call initiation failed: \(error?.errorDescription ?? "")")
}
ParameterDescription
receiverIDThe UID or GUID of the recipient
receiverTypeThe type of the receiver: .user or .group
callTypeThe type of the call: .audio or .video
Request:
PropertyTypeValue
receiverIdString"Cometchat-uid-2"
receiverTypeReceiverType.user
callTypeCallType.audio
Success Response - Call Object:
PropertyTypeDescription
sessionIDString?Unique call session identifier
callStatusCallStatus.initiated
callTypeCallType.audio
receiverTypeReceiverType.user
callInitiatorUser?User object of the caller
callReceiverUser?User object receiving the call
initiatedAtDoubleTimestamp when call was initiated

Call Listeners

Register the CometChatCallDelegate to receive real-time call events.
extension ViewController: CometChatCallDelegate {
    
    func onIncomingCallReceived(incomingCall: Call?, error: CometChatException?) {
        // Show incoming call UI
    }
    
    func onOutgoingCallAccepted(acceptedCall: Call?, error: CometChatException?) {
        // Receiver accepted, start the call session
    }
    
    func onOutgoingCallRejected(rejectedCall: Call?, error: CometChatException?) {
        // Receiver rejected, dismiss outgoing call UI
    }
    
    func onIncomingCallCanceled(canceledCall: Call?, error: CometChatException?) {
        // Caller cancelled, dismiss incoming call UI
    }
    
    func onCallEndedMessageReceived(endedCall: Call?, error: CometChatException?) {
        // Call ended by remote participant
    }
}
Set your view controller as the CometChat call delegate in viewDidLoad(): CometChat.calldelegate = self

Events

EventDescription
onIncomingCallReceived(incomingCall: Call)Invoked when an incoming call is received. Display incoming call UI here.
onOutgoingCallAccepted(acceptedCall: Call)Invoked on the caller’s device when the receiver accepts. Start the call session here.
onOutgoingCallRejected(rejectedCall: Call)Invoked on the caller’s device when the receiver rejects. Dismiss outgoing call UI.
onIncomingCallCanceled(canceledCall: Call)Invoked on the receiver’s device when the caller cancels. Dismiss incoming call UI.
onCallEndedMessageReceived(endedCall: Call)Invoked when a call ends. Update call history here.
Method: onIncomingCallReceived(incomingCall: Call?, error: CometChatException?)Triggered on: Receiver’s device when someone initiates a call to themIncoming Call Object Properties:
PropertyTypeDescription
sessionIDString?Unique call session identifier
callTypeCallType.audio or .video
callStatusCallStatus.initiated
callInitiatorUser?User object of the caller
callReceiverAppEntity?User or Group object receiving the call
receiverTypeReceiverType.user or .group
initiatedAtDoubleTimestamp when call was initiated

Accept Call

When an incoming call is received via onIncomingCallReceived(), use acceptCall() to accept it. On success, start the call session.
let sessionID = incomingCall?.sessionID ?? ""

CometChat.acceptCall(sessionID: sessionID, onSuccess: { call in
    // Call accepted, now start the call session
    print("Call accepted successfully")
}) { error in
    print("Accept call failed: \(error?.errorDescription ?? "")")
}
Method: CometChat.acceptCall(sessionID:onSuccess:onError:)Parameters:
PropertyTypeDescription
sessionIDStringSession ID from incoming call

Reject Call

Use rejectCall() to reject an incoming call. Set the status to .rejected.
let sessionID = incomingCall?.sessionID ?? ""
let status: CometChatConstants.callStatus = .rejected

CometChat.rejectCall(sessionID: sessionID, status: status, onSuccess: { call in
    // Call rejected, dismiss incoming call UI
    print("Call rejected successfully")
}) { error in
    print("Reject call failed: \(error?.errorDescription ?? "")")
}
Method: CometChat.rejectCall(sessionID:status:onSuccess:onError:)Parameters:
PropertyTypeDescription
sessionIDStringSession ID of call to reject
statuscallStatus.rejected

Cancel Call

The caller can cancel an outgoing call before it’s answered using rejectCall() with status .cancelled.
let sessionID = outgoingCall?.sessionID ?? ""
let status: CometChatConstants.callStatus = .cancelled

CometChat.rejectCall(sessionID: sessionID, status: status, onSuccess: { call in
    // Call cancelled, dismiss outgoing call UI
    print("Call cancelled successfully")
}) { error in
    print("Cancel call failed: \(error?.errorDescription ?? "")")
}
Method: CometChat.rejectCall(sessionID:status:onSuccess:onError:)Parameters:
PropertyTypeDescription
sessionIDStringSession ID of call to cancel
statuscallStatus.cancelled

Start Call Session

Once the call is accepted, both participants need to start the call session. Caller flow: In the onOutgoingCallAccepted() callback, generate a token and start the session. Receiver flow: In the acceptCall() success callback, generate a token and start the session.
let sessionId = call?.sessionID ?? ""
let authToken = CometChat.getUserAuthToken() ?? ""

// Step 1: Generate call token
CometChatCalls.generateToken(authToken: authToken as NSString, sessionID: sessionId) { token in
    
    // Step 2: Configure call settings
    let callSettings = CometChatCalls.callSettingsBuilder
        .setDefaultLayout(true)
        .setIsAudioOnly(false)
        .setDelegate(self)
        .build()
    
    // Step 3: Start the call session
    CometChatCalls.startSession(callToken: token, callSetting: callSettings, view: self.callView) { success in
        print("Call session started successfully")
    } onError: { error in
        print("Start session failed: \(String(describing: error?.errorDescription))")
    }
    
} onError: { error in
    print("Token generation failed: \(String(describing: error?.errorDescription))")
}
For more details on call settings and customization, see the Call Session guide.

End Call

To end an active call in the ringing flow, the process differs based on who ends the call. User who ends the call: When the user presses the end call button, the onCallEndButtonPressed() callback is triggered. Inside this callback, call CometChat.endCall(). On success, call CometChat.clearActiveCall() and CometChatCalls.endSession().
func onCallEndButtonPressed() {
    CometChat.endCall(sessionID: sessionId) { call in
        CometChat.clearActiveCall()
        CometChatCalls.endSession()
        // Close the calling screen
    } onError: { error in
        print("End call failed: \(String(describing: error?.errorDescription))")
    }
}
Remote participant (receives onCallEnded() callback):
func onCallEnded() {
    CometChat.clearActiveCall()
    CometChatCalls.endSession()
    // Close the calling screen
}
For more details, see the End Call Session guide.

Busy Call Handling

If the receiver is already on another call, you can reject the incoming call with .busy status.
let sessionID = incomingCall?.sessionID ?? ""
let status: CometChatConstants.callStatus = .busy

CometChat.rejectCall(sessionID: sessionID, status: status, onSuccess: { call in
    // Busy status sent to caller
    print("Busy rejection sent")
}) { error in
    print("Busy rejection failed: \(error?.errorDescription ?? "")")
}
Method: CometChat.rejectCall(sessionID:status:onSuccess:onError:)Parameters:
PropertyTypeDescription
sessionIDStringSession ID of incoming call
statuscallStatus.busy

Call Status Reference

StatusDescriptionUsage
.initiatedCall has been initiatedAfter initiateCall
.ongoingCall is in progressDuring active call
.unansweredCall was not answeredTimeout/no answer
.rejectedReceiver rejected the callrejectCall(.rejected)
.busyReceiver is on another callrejectCall(.busy)
.cancelledCaller cancelled before answerrejectCall(.cancelled)
.endedCall has endedAfter endCall

Call Type Reference

TypeDescription
.audioVoice call only
.videoVideo call with audio

Receiver Type Reference

TypeDescription
.userOne-to-one call with a user
.groupGroup call

Common Error Codes

Error CodeDescriptionResolution
ERR_CALL_NOT_FOUNDCall session not foundVerify session ID
ERR_CALL_ALREADY_INITIATEDCall already in progressEnd current call first
ERR_CALL_ALREADY_JOINEDAlready joined the callCannot join twice
ERR_CALL_BUSYUser is on another callTry again later
ERR_CALL_CANCELLEDCall was cancelledInitiate new call
ERR_CALL_REJECTEDCall was rejectedUser declined
ERR_CALL_ENDEDCall has already endedInitiate new call
ERR_INVALID_SESSION_IDInvalid session IDCheck session ID
ERR_USER_NOT_LOGGED_INUser not authenticatedLogin first
ERR_CALLING_SELFCannot call yourselfUse different receiver
ERROR_CALL_IN_PROGRESSAnother call is activeEnd previous call first

Complete Calling Example

A complete implementation showing the full calling flow from initiation to end.
import CometChatSDK
import CometChatCallsSDK

class CallViewController: UIViewController, CometChatCallDelegate, CallsEventsDelegate {
    
    var callView: UIView!
    var currentSessionId: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        CometChat.calldelegate = self
        setupCallView()
    }
    
    func setupCallView() {
        callView = UIView(frame: view.bounds)
        view.addSubview(callView)
    }
    
    // MARK: - Initiate Call
    func initiateVideoCall(to receiverId: String) {
        let call = Call(receiverId: receiverId, callType: .video, receiverType: .user)
        
        CometChat.initiateCall(call: call) { [weak self] call in
            self?.currentSessionId = call?.sessionID
            // Show outgoing call UI
        } onError: { error in
            print("Error: \(error?.errorDescription ?? "")")
        }
    }
    
    // MARK: - CometChatCallDelegate
    func onIncomingCallReceived(incomingCall: Call?, error: CometChatException?) {
        guard let call = incomingCall else { return }
        currentSessionId = call.sessionID
        // Show incoming call UI with accept/reject buttons
    }
    
    func onOutgoingCallAccepted(acceptedCall: Call?, error: CometChatException?) {
        guard let sessionId = acceptedCall?.sessionID else { return }
        startCallSession(sessionID: sessionId)
    }
    
    func onOutgoingCallRejected(rejectedCall: Call?, error: CometChatException?) {
        // Dismiss outgoing call UI
    }
    
    func onIncomingCallCanceled(canceledCall: Call?, error: CometChatException?) {
        // Dismiss incoming call UI
    }
    
    func onCallEndedMessageReceived(endedCall: Call?, error: CometChatException?) {
        CometChat.clearActiveCall()
        CometChatCalls.endSession()
    }
    
    // MARK: - Accept/Reject Call
    func acceptCall() {
        guard let sessionId = currentSessionId else { return }
        
        CometChat.acceptCall(sessionID: sessionId) { [weak self] call in
            self?.startCallSession(sessionID: sessionId)
        } onError: { error in
            print("Error: \(error?.errorDescription ?? "")")
        }
    }
    
    func rejectCall() {
        guard let sessionId = currentSessionId else { return }
        
        CometChat.rejectCall(sessionID: sessionId, status: .rejected) { call in
            // Dismiss incoming call UI
        } onError: { error in
            print("Error: \(error?.errorDescription ?? "")")
        }
    }
    
    // MARK: - Start Call Session
    func startCallSession(sessionID: String) {
        guard let authToken = CometChat.getUserAuthToken() else { return }
        
        CometChatCalls.generateToken(
            authToken: authToken as NSString,
            sessionID: sessionID as NSString
        ) { [weak self] token in
            guard let self = self else { return }
            
            let tokenStr = (token as? [String: Any])?["token"] as? String ?? token as? String
            guard let callToken = tokenStr else { return }
            
            let callSettings = CometChatCalls.callSettingsBuilder
                .setDefaultLayout(true)
                .setIsAudioOnly(false)
                .setDelegate(self)
                .build()
            
            DispatchQueue.main.async {
                CometChatCalls.startSession(
                    callToken: callToken,
                    callSetting: callSettings,
                    view: self.callView
                ) { success in
                    print("Call session started")
                } onError: { error in
                    print("Error: \(error?.errorDescription ?? "")")
                }
            }
        } onError: { error in
            print("Token error: \(error?.errorDescription ?? "")")
        }
    }
    
    // MARK: - CallsEventsDelegate
    func onCallEndButtonPressed() {
        guard let sessionId = currentSessionId else { return }
        
        CometChat.endCall(sessionID: sessionId) { call in
            CometChat.clearActiveCall()
            CometChatCalls.endSession()
        } onError: { error in
            print("Error: \(error?.errorDescription ?? "")")
        }
    }
    
    func onCallEnded() {
        CometChat.clearActiveCall()
        CometChatCalls.endSession()
    }
    
    func onUserJoined(rtcUser: RTCUser) {
        print("User joined: \(rtcUser.name ?? "")")
    }
    
    func onUserLeft(rtcUser: RTCUser) {
        print("User left: \(rtcUser.name ?? "")")
    }
}