> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-ios-ui-kit-sdk-fixes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Ringing

> Guide to implementing voice and video calls with ringing functionality using the CometChat iOS SDK including call initiation, acceptance, and rejection.

<Info>
  **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](/sdk/ios/direct-calling) · [Call Logs](/sdk/ios/call-logs) · [Calling Overview](/sdk/ios/calling-overview)
</Info>

## 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**.

<Note>
  After the call is accepted, you need to start the call session. See the [Call Session](/sdk/ios/direct-calling#start-call-session) guide for details on starting and managing the actual call.
</Note>

**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.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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 ?? "")")
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    NSString *receiverID = @"UID";

    Call *newCall = [[Call alloc] initWithReceiverId:receiverID callType:CallTypeVideo receiverType:ReceiverTypeUser];

    [CometChat initiateCallWithCall:newCall onSuccess:^(Call *call) {
        // Call initiated, show outgoing call UI
        NSLog(@"Call initiated successfully");
    } onError:^(CometChatException *error) {
        NSLog(@"Call initiation failed: %@", [error errorDescription]);
    }];
    ```
  </Tab>
</Tabs>

| Parameter      | Description                                   |
| -------------- | --------------------------------------------- |
| `receiverID`   | The UID or GUID of the recipient              |
| `receiverType` | The type of the receiver: `.user` or `.group` |
| `callType`     | The type of the call: `.audio` or `.video`    |

<Accordion title="Sample Payloads - Initiate Call">
  <Tabs>
    <Tab title="User Audio Call">
      **Request:**

      | Property     | Type           | Value               |
      | ------------ | -------------- | ------------------- |
      | receiverId   | `String`       | `"Cometchat-uid-2"` |
      | receiverType | `ReceiverType` | `.user`             |
      | callType     | `CallType`     | `.audio`            |

      **Success Response - Call Object:**

      | Property      | Type           | Description                       |
      | ------------- | -------------- | --------------------------------- |
      | sessionID     | `String?`      | Unique call session identifier    |
      | callStatus    | `CallStatus`   | `.initiated`                      |
      | callType      | `CallType`     | `.audio`                          |
      | receiverType  | `ReceiverType` | `.user`                           |
      | callInitiator | `User?`        | User object of the caller         |
      | callReceiver  | `User?`        | User object receiving the call    |
      | initiatedAt   | `Double`       | Timestamp when call was initiated |
    </Tab>

    <Tab title="User Video Call">
      **Request:**

      | Property     | Type           | Value               |
      | ------------ | -------------- | ------------------- |
      | receiverId   | `String`       | `"Cometchat-uid-2"` |
      | receiverType | `ReceiverType` | `.user`             |
      | callType     | `CallType`     | `.video`            |

      **Success Response - Call Object:**

      | Property      | Type           | Description                       |
      | ------------- | -------------- | --------------------------------- |
      | sessionID     | `String?`      | Unique call session identifier    |
      | callStatus    | `CallStatus`   | `.initiated`                      |
      | callType      | `CallType`     | `.video`                          |
      | receiverType  | `ReceiverType` | `.user`                           |
      | callInitiator | `User?`        | User object of the caller         |
      | callReceiver  | `User?`        | User object receiving the call    |
      | initiatedAt   | `Double`       | Timestamp when call was initiated |
    </Tab>

    <Tab title="Group Audio Call">
      **Request:**

      | Property     | Type           | Value                |
      | ------------ | -------------- | -------------------- |
      | receiverId   | `String`       | `"Cometchat-guid-1"` |
      | receiverType | `ReceiverType` | `.group`             |
      | callType     | `CallType`     | `.audio`             |

      **Success Response - Call Object:**

      | Property      | Type           | Description                                                                     |
      | ------------- | -------------- | ------------------------------------------------------------------------------- |
      | sessionID     | `String?`      | Unique call session identifier (e.g., `"v1.in.279557705a948ad6.1772088258..."`) |
      | callStatus    | `CallStatus`   | `.initiated`                                                                    |
      | callType      | `CallType`     | `.audio`                                                                        |
      | receiverType  | `ReceiverType` | `.group`                                                                        |
      | callInitiator | `User?`        | User object of the caller                                                       |
      | callReceiver  | `Group?`       | Group object receiving the call                                                 |
      | initiatedAt   | `Double`       | Timestamp when call was initiated                                               |
      | sentAt        | `Double`       | Timestamp when call was sent                                                    |
    </Tab>

    <Tab title="Group Video Call">
      **Request:**

      | Property     | Type           | Value                |
      | ------------ | -------------- | -------------------- |
      | receiverId   | `String`       | `"Cometchat-guid-1"` |
      | receiverType | `ReceiverType` | `.group`             |
      | callType     | `CallType`     | `.video`             |

      **Success Response - Call Object:**

      | Property      | Type           | Description                       |
      | ------------- | -------------- | --------------------------------- |
      | sessionID     | `String?`      | Unique call session identifier    |
      | callStatus    | `CallStatus`   | `.initiated`                      |
      | callType      | `CallType`     | `.video`                          |
      | receiverType  | `ReceiverType` | `.group`                          |
      | callInitiator | `User?`        | User object of the caller         |
      | callReceiver  | `Group?`       | Group object receiving the call   |
      | initiatedAt   | `Double`       | Timestamp when call was initiated |
    </Tab>

    <Tab title="Error - Calling Self">
      **Object Type:** CometChatException

      | Property         | Type     | Value                                        |
      | ---------------- | -------- | -------------------------------------------- |
      | errorCode        | `String` | `"ERR_CALLING_SELF"`                         |
      | errorDescription | `String` | `"Initiator of a call cannot call himself."` |
    </Tab>

    <Tab title="Error - Call In Progress">
      **Object Type:** CometChatException

      | Property         | Type     | Value                                                                           |
      | ---------------- | -------- | ------------------------------------------------------------------------------- |
      | errorCode        | `String` | `"ERROR_CALL_IN_PROGRESS"`                                                      |
      | errorDescription | `String` | `"Call is in progress. Please end the previous call to perform this operation"` |
    </Tab>
  </Tabs>
</Accordion>

## Call Listeners

Register the `CometChatCallDelegate` to receive real-time call events.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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
        }
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    @interface ViewController () <CometChatCallDelegate>
    @end

    @implementation ViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        [CometChat setCalldelegate:self];
    }

    - (void)onIncomingCallReceivedWithIncomingCall:(Call *)incomingCall error:(CometChatException *)error {
        // Show incoming call UI
    }

    - (void)onOutgoingCallAcceptedWithAcceptedCall:(Call *)acceptedCall error:(CometChatException *)error {
        // Receiver accepted, start the call session
    }

    - (void)onOutgoingCallRejectedWithRejectedCall:(Call *)rejectedCall error:(CometChatException *)error {
        // Receiver rejected, dismiss outgoing call UI
    }

    - (void)onIncomingCallCanceledWithCanceledCall:(Call *)canceledCall error:(CometChatException *)error {
        // Caller cancelled, dismiss incoming call UI
    }

    @end
    ```
  </Tab>
</Tabs>

<Note>
  Set your view controller as the CometChat call delegate in `viewDidLoad()`: `CometChat.calldelegate = self`
</Note>

### Events

| Event                                         | Description                                                                            |
| --------------------------------------------- | -------------------------------------------------------------------------------------- |
| `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.                                    |

<Accordion title="Sample Payloads - Call Listener Events">
  <Tabs>
    <Tab title="onIncomingCallReceived">
      **Method:** `onIncomingCallReceived(incomingCall: Call?, error: CometChatException?)`

      **Triggered on:** Receiver's device when someone initiates a call to them

      **Incoming Call Object Properties:**

      | Property      | Type           | Description                             |
      | ------------- | -------------- | --------------------------------------- |
      | sessionID     | `String?`      | Unique call session identifier          |
      | callType      | `CallType`     | `.audio` or `.video`                    |
      | callStatus    | `CallStatus`   | `.initiated`                            |
      | callInitiator | `User?`        | User object of the caller               |
      | callReceiver  | `AppEntity?`   | User or Group object receiving the call |
      | receiverType  | `ReceiverType` | `.user` or `.group`                     |
      | initiatedAt   | `Double`       | Timestamp when call was initiated       |
    </Tab>

    <Tab title="onOutgoingCallAccepted">
      **Method:** `onOutgoingCallAccepted(acceptedCall: Call?, error: CometChatException?)`

      **Triggered on:** Caller's device when the receiver accepts the call

      **Accepted Call Object Properties:**

      | Property      | Type         | Description                        |
      | ------------- | ------------ | ---------------------------------- |
      | sessionID     | `String?`    | Session ID to use for startSession |
      | callStatus    | `CallStatus` | `.ongoing`                         |
      | callType      | `CallType`   | `.audio` or `.video`               |
      | callInitiator | `User?`      | User object of the caller          |
      | callReceiver  | `AppEntity?` | User or Group that accepted        |
      | joinedAt      | `Double`     | Timestamp when call was accepted   |
    </Tab>

    <Tab title="onOutgoingCallRejected">
      **Method:** `onOutgoingCallRejected(rejectedCall: Call?, error: CometChatException?)`

      **Triggered on:** Caller's device when the receiver rejects the call

      **Rejected Call Object Properties:**

      | Property      | Type         | Description                 |
      | ------------- | ------------ | --------------------------- |
      | sessionID     | `String?`    | Session ID of rejected call |
      | callStatus    | `CallStatus` | `.rejected` or `.busy`      |
      | callType      | `CallType`   | `.audio` or `.video`        |
      | callInitiator | `User?`      | User object of the caller   |
      | callReceiver  | `AppEntity?` | User or Group that rejected |
    </Tab>

    <Tab title="onIncomingCallCanceled">
      **Method:** `onIncomingCallCanceled(canceledCall: Call?, error: CometChatException?)`

      **Triggered on:** Receiver's device when the caller cancels before they answer

      **Cancelled Call Object Properties:**

      | Property      | Type         | Description                         |
      | ------------- | ------------ | ----------------------------------- |
      | sessionID     | `String?`    | Session ID of cancelled call        |
      | callStatus    | `CallStatus` | `.cancelled`                        |
      | callType      | `CallType`   | `.audio` or `.video`                |
      | callInitiator | `User?`      | User who cancelled the call         |
      | callReceiver  | `AppEntity?` | User or Group that was being called |
    </Tab>

    <Tab title="onCallEndedMessageReceived">
      **Method:** `onCallEndedMessageReceived(endedCall: Call?, error: CometChatException?)`

      **Triggered on:** Both caller and receiver when the call ends

      **Ended Call Object Properties:**

      | Property               | Type         | Description                    |
      | ---------------------- | ------------ | ------------------------------ |
      | sessionID              | `String?`    | Session ID of ended call       |
      | callStatus             | `CallStatus` | `.ended`                       |
      | callType               | `CallType`   | `.audio` or `.video`           |
      | callInitiator          | `User?`      | User who initiated the call    |
      | callReceiver           | `AppEntity?` | User or Group that was called  |
      | endedAt                | `Double?`    | Timestamp when call ended      |
      | totalDurationInMinutes | `Double`     | Total call duration in minutes |
    </Tab>
  </Tabs>
</Accordion>

## Accept Call

When an incoming call is received via `onIncomingCallReceived()`, use `acceptCall()` to accept it. On success, start the call session.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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 ?? "")")
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChat acceptCallWithSessionID:incomingCall.sessionID onSuccess:^(Call *call) {
        // Call accepted, now start the call session
        NSLog(@"Call accepted successfully");
    } onError:^(CometChatException *error) {
        NSLog(@"Accept call failed: %@", [error errorDescription]);
    }];
    ```
  </Tab>
</Tabs>

<Accordion title="Sample Payloads - Accept Call">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChat.acceptCall(sessionID:onSuccess:onError:)`

      **Parameters:**

      | Property  | Type     | Description                   |
      | --------- | -------- | ----------------------------- |
      | sessionID | `String` | Session ID from incoming call |
    </Tab>

    <Tab title="Success Response">
      **Call Object Properties:**

      | Property   | Type         | Description                        |
      | ---------- | ------------ | ---------------------------------- |
      | sessionID  | `String?`    | Session ID to use for startSession |
      | callStatus | `CallStatus` | `.accepted`                        |
      | joinedAt   | `Double`     | Timestamp when call was accepted   |
    </Tab>
  </Tabs>
</Accordion>

## Reject Call

Use `rejectCall()` to reject an incoming call. Set the status to `.rejected`.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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 ?? "")")
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChat rejectCallWithSessionID:incomingCall.sessionID status:callStatusRejected onSuccess:^(Call *call) {
        // Call rejected, dismiss incoming call UI
        NSLog(@"Call rejected successfully");
    } onError:^(CometChatException *error) {
        NSLog(@"Reject call failed: %@", [error errorDescription]);
    }];
    ```
  </Tab>
</Tabs>

<Accordion title="Sample Payloads - Reject Call">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChat.rejectCall(sessionID:status:onSuccess:onError:)`

      **Parameters:**

      | Property  | Type         | Description                  |
      | --------- | ------------ | ---------------------------- |
      | sessionID | `String`     | Session ID of call to reject |
      | status    | `callStatus` | `.rejected`                  |
    </Tab>

    <Tab title="Success Response">
      **Call Object Properties:**

      | Property   | Type         | Description                 |
      | ---------- | ------------ | --------------------------- |
      | sessionID  | `String?`    | Session ID of rejected call |
      | callStatus | `CallStatus` | `.rejected`                 |
    </Tab>
  </Tabs>
</Accordion>

## Cancel Call

The caller can cancel an outgoing call before it's answered using `rejectCall()` with status `.cancelled`.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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 ?? "")")
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChat rejectCallWithSessionID:outgoingCall.sessionID status:callStatusCancelled onSuccess:^(Call *call) {
        // Call cancelled, dismiss outgoing call UI
        NSLog(@"Call cancelled successfully");
    } onError:^(CometChatException *error) {
        NSLog(@"Cancel call failed: %@", [error errorDescription]);
    }];
    ```
  </Tab>
</Tabs>

<Accordion title="Sample Payloads - Cancel Call">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChat.rejectCall(sessionID:status:onSuccess:onError:)`

      **Parameters:**

      | Property  | Type         | Description                  |
      | --------- | ------------ | ---------------------------- |
      | sessionID | `String`     | Session ID of call to cancel |
      | status    | `callStatus` | `.cancelled`                 |
    </Tab>

    <Tab title="Success Response">
      **Call Object Properties:**

      | Property   | Type         | Description                  |
      | ---------- | ------------ | ---------------------------- |
      | sessionID  | `String?`    | Session ID of cancelled call |
      | callStatus | `CallStatus` | `.cancelled`                 |
    </Tab>
  </Tabs>
</Accordion>

## 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.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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))")
    }
    ```
  </Tab>
</Tabs>

For more details on call settings and customization, see the [Call Session](/sdk/ios/direct-calling#start-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()`.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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))")
        }
    }
    ```
  </Tab>
</Tabs>

**Remote participant** (receives `onCallEnded()` callback):

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    func onCallEnded() {
        CometChat.clearActiveCall()
        CometChatCalls.endSession()
        // Close the calling screen
    }
    ```
  </Tab>
</Tabs>

For more details, see the [End Call Session](/sdk/ios/direct-calling#end-call-session) guide.

## Busy Call Handling

If the receiver is already on another call, you can reject the incoming call with `.busy` status.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    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 ?? "")")
    }
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChat rejectCallWithSessionID:incomingCall.sessionID status:callStatusBusy onSuccess:^(Call *call) {
        // Busy status sent to caller
        NSLog(@"Busy rejection sent");
    } onError:^(CometChatException *error) {
        NSLog(@"Busy rejection failed: %@", [error errorDescription]);
    }];
    ```
  </Tab>
</Tabs>

<Accordion title="Sample Payloads - Busy Call">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChat.rejectCall(sessionID:status:onSuccess:onError:)`

      **Parameters:**

      | Property  | Type         | Description                 |
      | --------- | ------------ | --------------------------- |
      | sessionID | `String`     | Session ID of incoming call |
      | status    | `callStatus` | `.busy`                     |
    </Tab>

    <Tab title="Success Response">
      **Call Object Properties:**

      | Property   | Type         | Description        |
      | ---------- | ------------ | ------------------ |
      | sessionID  | `String?`    | Session ID of call |
      | callStatus | `CallStatus` | `.busy`            |
    </Tab>
  </Tabs>
</Accordion>

***

## Call Status Reference

| Status        | Description                    | Usage                  |
| ------------- | ------------------------------ | ---------------------- |
| `.initiated`  | Call has been initiated        | After initiateCall     |
| `.ongoing`    | Call is in progress            | During active call     |
| `.unanswered` | Call was not answered          | Timeout/no answer      |
| `.rejected`   | Receiver rejected the call     | rejectCall(.rejected)  |
| `.busy`       | Receiver is on another call    | rejectCall(.busy)      |
| `.cancelled`  | Caller cancelled before answer | rejectCall(.cancelled) |
| `.ended`      | Call has ended                 | After endCall          |

***

## Call Type Reference

| Type     | Description           |
| -------- | --------------------- |
| `.audio` | Voice call only       |
| `.video` | Video call with audio |

***

## Receiver Type Reference

| Type     | Description                 |
| -------- | --------------------------- |
| `.user`  | One-to-one call with a user |
| `.group` | Group call                  |

***

## Common Error Codes

| Error Code                   | Description              | Resolution              |
| ---------------------------- | ------------------------ | ----------------------- |
| `ERR_CALL_NOT_FOUND`         | Call session not found   | Verify session ID       |
| `ERR_CALL_ALREADY_INITIATED` | Call already in progress | End current call first  |
| `ERR_CALL_ALREADY_JOINED`    | Already joined the call  | Cannot join twice       |
| `ERR_CALL_BUSY`              | User is on another call  | Try again later         |
| `ERR_CALL_CANCELLED`         | Call was cancelled       | Initiate new call       |
| `ERR_CALL_REJECTED`          | Call was rejected        | User declined           |
| `ERR_CALL_ENDED`             | Call has already ended   | Initiate new call       |
| `ERR_INVALID_SESSION_ID`     | Invalid session ID       | Check session ID        |
| `ERR_USER_NOT_LOGGED_IN`     | User not authenticated   | Login first             |
| `ERR_CALLING_SELF`           | Cannot call yourself     | Use different receiver  |
| `ERROR_CALL_IN_PROGRESS`     | Another call is active   | End previous call first |

***

## Complete Calling Example

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

<Accordion title="Complete Calling Implementation">
  <Tabs>
    <Tab title="Swift">
      ```swift theme={null}
      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 ?? "")")
          }
      }
      ```
    </Tab>
  </Tabs>
</Accordion>
