> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Ringing

> CometChat Calling SDK v4 - Stable Release - Ringing for iOS

## 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](/calls/v4/ios/call-session#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
        guard let call = call else { return }
        // 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="Swift (with Timeout)">
    ```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)

    // Set a custom timeout of 30 seconds (default is 45 seconds)
    CometChat.initiateCall(call: newCall, timeout: 30, onSuccess: { call in
        guard let call = call else { return }
        // 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`                                                                                         |
| `timeout`      | Optional. The timeout duration in seconds for the call to be answered before it's automatically cancelled. Defaults to 45 seconds. |

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

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

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

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

## 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](/calls/v4/ios/call-session#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](/calls/v4/ios/call-session#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>
