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

# Call Session

> Guide to starting and managing call sessions using the CometChat iOS SDK including token generation and call UI.

<Info>
  **Quick Reference for AI Agents & Developers**

  * **Generate token:** `CometChat.generateToken(sessionID:onSuccess:onError:)`
  * **Start call:** `CometChatCalls.startSession(callToken:callSettings:onSuccess:onError:)`
  * **End call:** `CometChatCalls.endSession()`
  * **Call settings:** `CometChatCalls.callSettingsBuilder.setDefaultLayout(true).build()`
  * **Related:** [Ringing](/sdk/ios/default-calling) · [Call Logs](/sdk/ios/call-logs) · [Calling Overview](/sdk/ios/calling-overview)
</Info>

## Overview

This section demonstrates how to start a call session in an iOS application. Previously known as **Direct Calling**.

Before you begin, we strongly recommend you read the [calling setup guide](/sdk/ios/calling-setup).

<Note>
  If you want to implement a complete calling experience with ringing functionality (incoming/outgoing call UI), follow the [Ringing](/sdk/ios/default-calling) guide first. Once the call is accepted, return here to start the call session.
</Note>

## Generate Call Token

A call token is required for secure access to a call session. Each token is unique to a specific session and user combination, ensuring that only authorized users can join the call.

You can generate the token just before starting the call, or generate and store it ahead of time based on your use case.

Use the `generateToken()` method to create a call token:

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    var callToken: GenerateToken?

    let sessionId = "UNIQUE_SESSION_ID" // Random or from Call object in ringing flow
    let authToken = CometChat.getUserAuthToken() // Logged in user auth token

    CometChatCalls.generateToken(authToken: authToken as NSString, sessionID: sessionId) { token in
        self.callToken = token
    } onError: { error in
        print("Token generation failed: \(String(describing: error?.errorDescription))")
    }
    ```
  </Tab>
</Tabs>

| Parameter   | Description                                                                                                                            |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `sessionId` | The unique session ID. In ringing flow, this is available in the `Call` object. For standalone calls, generate a random unique string. |
| `authToken` | The logged-in user's auth token from `CometChat.getUserAuthToken()`.                                                                   |

<Accordion title="Sample Payloads - Generate Call Token">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChatCalls.generateToken(authToken:sessionID:onSuccess:onError:)`

      **Parameters:**

      | Property  | Type       | Description                      |
      | --------- | ---------- | -------------------------------- |
      | authToken | `NSString` | User's auth token from CometChat |
      | sessionID | `NSString` | Unique session identifier        |
    </Tab>

    <Tab title="Success Response">
      **GenerateToken Object:**

      | Property | Type     | Description                |
      | -------- | -------- | -------------------------- |
      | token    | `String` | JWT token for call session |
    </Tab>

    <Tab title="Error Response">
      **Object Type:** CometChatException

      | Property         | Type     | Description                     |
      | ---------------- | -------- | ------------------------------- |
      | errorCode        | `String` | `"ERR_TOKEN_GENERATION_FAILED"` |
      | errorDescription | `String` | Error message                   |
    </Tab>
  </Tabs>
</Accordion>

## Start Call Session

Use the `startSession()` method to join a call session. This method requires a call token (generated in the previous step) and a `CallSettings` object that configures the call UI and behavior.

The `CallSettings` class configures the call UI and behavior. Use `callSettingsBuilder` to create a `CallSettings` instance with the following required parameters:

| Parameter   | Description                                                          |
| ----------- | -------------------------------------------------------------------- |
| `view`      | A `UIView` where the calling UI will be rendered                     |
| `callToken` | The `GenerateToken` object received from `generateToken()` onSuccess |

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    guard let callToken = self.callToken else { return }

    let callSettings = CometChatCalls.callSettingsBuilder
        .setDefaultLayout(true)
        .setIsAudioOnly(false)
        .setDelegate(self)
        .build()

    CometChatCalls.startSession(callToken: callToken, callSetting: callSettings, view: callView) { success in
        print("Call session started successfully")
    } onError: { error in
        print("Start session failed: \(String(describing: error?.errorDescription))")
    }
    ```
  </Tab>
</Tabs>

<Accordion title="Sample Payloads - Start Call Session">
  <Tabs>
    <Tab title="Request">
      **Method:** `CometChatCalls.startSession(callToken:callSetting:view:onSuccess:onError:)`

      **Parameters:**

      | Property    | Type            | Description                       |
      | ----------- | --------------- | --------------------------------- |
      | callToken   | `GenerateToken` | Token from generateToken          |
      | callSetting | `CallSettings`  | Settings from callSettingsBuilder |
      | view        | `UIView`        | View to render call UI            |
    </Tab>

    <Tab title="Success Response">
      **Response:**

      | Property | Type     | Value           |
      | -------- | -------- | --------------- |
      | success  | `String` | Success message |
    </Tab>

    <Tab title="Error Response">
      **Object Type:** CometChatException

      | Property         | Type     | Description      |
      | ---------------- | -------- | ---------------- |
      | errorCode        | `String` | Error identifier |
      | errorDescription | `String` | Error message    |
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="Call Settings Options">
  **callSettingsBuilder Methods:**

  | Method                  | Type   | Description                               |
  | ----------------------- | ------ | ----------------------------------------- |
  | setDefaultLayout(\_:)   | `Bool` | Use default UI layout                     |
  | setIsAudioOnly(\_:)     | `Bool` | Audio-only mode (no video)                |
  | setDelegate(\_:)        | `Any`  | Delegate for call events                  |
  | setMode(\_:)            | `Mode` | Call mode (.default, .single, .spotlight) |
  | setStartAudioMuted(\_:) | `Bool` | Start with mic muted                      |
  | setStartVideoMuted(\_:) | `Bool` | Start with camera off                     |
</Accordion>

### Call Settings

Configure the call experience using the following `callSettingsBuilder` methods:

<Accordion title="Sample Payloads - Call Settings">
  **Layout & Mode Settings:**

  | Method                 | Type         | Default    | Description                         |
  | ---------------------- | ------------ | ---------- | ----------------------------------- |
  | `setDefaultLayout(_:)` | `Bool`       | `true`     | Enable default call UI              |
  | `setIsAudioOnly(_:)`   | `Bool`       | `false`    | Audio-only or audio-video           |
  | `setIsSingleMode(_:)`  | `Bool`       | `false`    | Single participant mode             |
  | `setMode(_:)`          | `Mode`       | `.default` | `.default`, `.single`, `.spotlight` |
  | `setAvatarMode(_:)`    | `AvatarMode` | `.circle`  | `.circle`, `.square`, `.fullscreen` |

  **Button Visibility Settings:**

  | Method                             | Type   | Default | Description                 |
  | ---------------------------------- | ------ | ------- | --------------------------- |
  | `setEndCallButtonDisable(_:)`      | `Bool` | `false` | Hide end call button        |
  | `setShowRecordingButton(_:)`       | `Bool` | `false` | Show recording button       |
  | `setSwitchCameraButtonDisable(_:)` | `Bool` | `false` | Hide switch camera button   |
  | `setMuteAudioButtonDisable(_:)`    | `Bool` | `false` | Hide mute audio button      |
  | `setPauseVideoButtonDisable(_:)`   | `Bool` | `false` | Hide pause video button     |
  | `setAudioModeButtonDisable(_:)`    | `Bool` | `false` | Hide audio mode button      |
  | `setShowSwitchToVideoCall(_:)`     | `Bool` | `false` | Show switch to video button |

  **Initial State Settings:**

  | Method                    | Type        | Default   | Description                        |
  | ------------------------- | ----------- | --------- | ---------------------------------- |
  | `setStartAudioMuted(_:)`  | `Bool`      | `false`   | Start with mic muted               |
  | `setStartVideoMuted(_:)`  | `Bool`      | `false`   | Start with camera off              |
  | `setDefaultAudioMode(_:)` | `AudioMode` | `SPEAKER` | `SPEAKER`, `EARPIECE`, `BLUETOOTH` |

  **Video Tile Settings:**

  | Method                            | Type   | Default | Description                   |
  | --------------------------------- | ------ | ------- | ----------------------------- |
  | `setEnableVideoTileClick(_:)`     | `Bool` | `true`  | Enable click on video tiles   |
  | `setEnableDraggableVideoTile(_:)` | `Bool` | `true`  | Enable drag in Spotlight mode |

  **Other Settings:**

  | Method                     | Type  | Default | Description             |
  | -------------------------- | ----- | ------- | ----------------------- |
  | `setIdleTimeoutPeriod(_:)` | `Int` | `180`   | Idle timeout in seconds |
  | `setDelegate(_:)`          | `Any` | `nil`   | CallsEventsDelegate     |
</Accordion>

| Method                               | Description                                                                                                             |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `setDefaultLayout(Bool)`             | Enables or disables the default call UI layout with built-in controls. `true` shows the default layout. Default: `true` |
| `setIsAudioOnly(Bool)`               | Sets whether the call is audio-only or audio-video. `true` for audio-only. Default: `false`                             |
| `setIsSingleMode(Bool)`              | Enables single participant mode.                                                                                        |
| `setShowSwitchToVideoCall(Bool)`     | Shows or hides the switch to video call button.                                                                         |
| `setEnableVideoTileClick(Bool)`      | Enables or disables click interactions on video tiles. Default: `true`                                                  |
| `setEnableDraggableVideoTile(Bool)`  | Enables or disables drag functionality for video tiles in Spotlight mode. Default: `true`                               |
| `setEndCallButtonDisable(Bool)`      | Shows or hides the end call button. Default: `false` (shown)                                                            |
| `setShowRecordingButton(Bool)`       | Shows or hides the recording button. Default: `false`                                                                   |
| `setSwitchCameraButtonDisable(Bool)` | Shows or hides the switch camera button. Default: `false` (shown)                                                       |
| `setMuteAudioButtonDisable(Bool)`    | Shows or hides the mute audio button. Default: `false` (shown)                                                          |
| `setPauseVideoButtonDisable(Bool)`   | Shows or hides the pause video button. Default: `false` (shown)                                                         |
| `setAudioModeButtonDisable(Bool)`    | Shows or hides the audio mode selection button. Default: `false` (shown)                                                |
| `setStartAudioMuted(Bool)`           | Starts the call with the microphone muted. Default: `false`                                                             |
| `setStartVideoMuted(Bool)`           | Starts the call with the camera turned off. Default: `false`                                                            |
| `setMode(DisplayModes)`              | Sets the call UI layout mode. Available: `.default`, `.single`, `.spotlight`. Default: `.default`                       |
| `setAvatarMode(AvatarMode)`          | Sets avatar display mode. Available: `.circle`, `.square`, `.fullscreen`. Default: `.circle`                            |
| `setDefaultAudioMode(AudioMode)`     | Sets the initial audio output device. Available: `SPEAKER`, `EARPIECE`, `BLUETOOTH`, `HEADPHONES`                       |
| `setIdleTimeoutPeriod(Int)`          | Sets idle timeout in seconds. Warning appears 60 seconds before auto-termination. Default: `180`. *v4.1.1+*             |
| `setDelegate(self)`                  | Sets the delegate to receive call events.                                                                               |

## Call Listeners

The `CallsEventsDelegate` protocol provides real-time callbacks for call session events, including participant changes, call state updates, and error conditions.

To receive call events, conform to `CallsEventsDelegate` and set the delegate in `callSettingsBuilder` using `setDelegate(self)`.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    extension ViewController: CallsEventsDelegate {
        
        func onCallEnded() {
            // Call ended, close the calling screen
        }
        
        func onSessionTimeout() {
            // Session timed out due to inactivity
        }
        
        func onCallEndButtonPressed() {
            // User pressed end call button
        }
        
        func onUserJoined(rtcUser: RTCUser) {
            // A participant joined the call
        }
        
        func onUserLeft(rtcUser: RTCUser) {
            // A participant left the call
        }
        
        func onUserListChanged(rtcUsers: [RTCUser]) {
            // Participant list updated
        }
        
        func onAudioModeChanged(mode: [AudioMode]) {
            // Available audio devices changed
        }
        
        func onCallSwitchedToVideo(callSwitchedInfo: CallSwitchRequestInfo) {
            // Call upgraded from audio to video
        }
        
        func onUserMuted(rtcMutedUser: RTCMutedUser) {
            // A participant's mute state changed
        }
        
        func onRecordingToggled(recordingInfo: RTCRecordingInfo) {
            // Recording started or stopped
        }
    }
    ```
  </Tab>
</Tabs>

### Events

| Event                                                            | Description                                                                                                                                             |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onCallEnded()`                                                  | Invoked when the call session terminates for a 1:1 call. Both participants receive this callback.                                                       |
| `onSessionTimeout()`                                             | Invoked when the call is auto-terminated due to inactivity (default: 180 seconds). *v4.1.1+*                                                            |
| `onCallEndButtonPressed()`                                       | Invoked when the local user taps the end call button. For ringing flow, call `CometChat.endCall()`. For standalone, call `CometChatCalls.endSession()`. |
| `onUserJoined(rtcUser: RTCUser)`                                 | Invoked when a remote participant joins.                                                                                                                |
| `onUserLeft(rtcUser: RTCUser)`                                   | Invoked when a remote participant leaves.                                                                                                               |
| `onUserListChanged(rtcUsers: [RTCUser])`                         | Invoked whenever the participant list changes.                                                                                                          |
| `onAudioModeChanged(mode: [AudioMode])`                          | Invoked when available audio devices change.                                                                                                            |
| `onCallSwitchedToVideo(callSwitchedInfo: CallSwitchRequestInfo)` | Invoked when an audio call is upgraded to video.                                                                                                        |
| `onUserMuted(rtcMutedUser: RTCMutedUser)`                        | Invoked when a participant's mute state changes.                                                                                                        |
| `onRecordingToggled(recordingInfo: RTCRecordingInfo)`            | Invoked when call recording starts or stops.                                                                                                            |

<Accordion title="Sample Payloads - CallsEventsDelegate">
  <Tabs>
    <Tab title="onCallEnded">
      **Method:** `onCallEnded()`

      **Triggered:** When call session terminates (1:1 call)

      **Payload:** None

      **Action:** Clean up and close calling screen
    </Tab>

    <Tab title="onSessionTimeout">
      **Method:** `onSessionTimeout()`

      **Triggered:** When call auto-terminates due to inactivity

      **Payload:** None

      **Default timeout:** 180 seconds (configurable via `setIdleTimeoutPeriod`)
    </Tab>

    <Tab title="onCallEndButtonPressed">
      **Method:** `onCallEndButtonPressed()`

      **Triggered:** When local user taps end call button

      **Payload:** None

      **Action:** End call (different for Ringing vs Session Only flow)
    </Tab>

    <Tab title="onUserJoined">
      **Method:** `onUserJoined(rtcUser: RTCUser)`

      **Triggered:** When a remote participant joins the call

      **RTCUser Object:**

      | Property | Type      | Description              |
      | -------- | --------- | ------------------------ |
      | uid      | `String?` | User's unique identifier |
      | name     | `String?` | User's display name      |
      | avatar   | `String?` | User's avatar URL        |
    </Tab>

    <Tab title="onUserLeft">
      **Method:** `onUserLeft(rtcUser: RTCUser)`

      **Triggered:** When a remote participant leaves the call

      **RTCUser Object:**

      | Property | Type      | Description              |
      | -------- | --------- | ------------------------ |
      | uid      | `String?` | User's unique identifier |
      | name     | `String?` | User's display name      |
      | avatar   | `String?` | User's avatar URL        |
    </Tab>

    <Tab title="onUserListChanged">
      **Method:** `onUserListChanged(rtcUsers: [RTCUser])`

      **Triggered:** Whenever the participant list changes

      **Payload:** Array of all current participants

      **RTCUser Object:**

      | Property | Type      | Description              |
      | -------- | --------- | ------------------------ |
      | uid      | `String?` | User's unique identifier |
      | name     | `String?` | User's display name      |
      | avatar   | `String?` | User's avatar URL        |
    </Tab>

    <Tab title="onAudioModeChanged">
      **Method:** `onAudioModeChanged(mode: [AudioMode])`

      **Triggered:** When available audio devices change

      **Payload:** Array of available AudioMode options

      **AudioMode Values:**

      | Mode         | Description                |
      | ------------ | -------------------------- |
      | `SPEAKER`    | Phone speaker              |
      | `EARPIECE`   | Phone earpiece             |
      | `BLUETOOTH`  | Connected Bluetooth device |
      | `HEADPHONES` | Wired headphones           |
    </Tab>

    <Tab title="onCallSwitchedToVideo">
      **Method:** `onCallSwitchedToVideo(callSwitchedInfo: CallSwitchRequestInfo)`

      **Triggered:** When an audio call is upgraded to video

      **CallSwitchRequestInfo Object:**

      | Property    | Type       | Description                   |
      | ----------- | ---------- | ----------------------------- |
      | requestedBy | `RTCUser?` | User who requested the switch |
    </Tab>

    <Tab title="onUserMuted">
      **Method:** `onUserMuted(rtcMutedUser: RTCMutedUser)`

      **Triggered:** When a participant's mute state changes

      **RTCMutedUser Object:**

      | Property   | Type      | Description              |
      | ---------- | --------- | ------------------------ |
      | uid        | `String?` | User's unique identifier |
      | name       | `String?` | User's display name      |
      | audioMuted | `Bool?`   | Is audio muted           |
      | videoMuted | `Bool?`   | Is video muted           |
    </Tab>

    <Tab title="onRecordingToggled">
      **Method:** `onRecordingToggled(recordingInfo: RTCRecordingInfo)`

      **Triggered:** When call recording starts or stops

      **RTCRecordingInfo Object:**

      | Property    | Type       | Description                |
      | ----------- | ---------- | -------------------------- |
      | isRecording | `Bool?`    | Is recording active        |
      | startedBy   | `RTCUser?` | User who started recording |
    </Tab>
  </Tabs>
</Accordion>

## End Call Session

Ending a call session properly is essential to release media resources (camera, microphone, network connections) and update call state across all participants.

<Accordion title="Sample Payloads - End Call">
  <Tabs>
    <Tab title="Ringing Flow">
      **When using the Ringing flow, coordinate between Chat SDK and Calls SDK.**

      **User who initiates end call (presses end button):**

      | Step | Method                          | SDK       | Purpose                  |
      | ---- | ------------------------------- | --------- | ------------------------ |
      | 1    | `CometChat.endCall(sessionID:)` | Chat SDK  | Notify server call ended |
      | 2    | `CometChat.clearActiveCall()`   | Chat SDK  | Clear active call state  |
      | 3    | `CometChatCalls.endSession()`   | Calls SDK | Release media resources  |

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

      | Step | Method                        | SDK       | Purpose                 |
      | ---- | ----------------------------- | --------- | ----------------------- |
      | 1    | `CometChat.clearActiveCall()` | Chat SDK  | Clear active call state |
      | 2    | `CometChatCalls.endSession()` | Calls SDK | Release media resources |
    </Tab>

    <Tab title="Session Only Flow">
      **When using Session Only flow (without ringing), only call the Calls SDK method.**

      | Step | Method                        | SDK       | Purpose                 |
      | ---- | ----------------------------- | --------- | ----------------------- |
      | 1    | `CometChatCalls.endSession()` | Calls SDK | Release media resources |
    </Tab>

    <Tab title="endCall Response">
      **Method:** `CometChat.endCall(sessionID:onSuccess:onError:)`

      **Request Parameters:**

      | Property  | Type     | Description               |
      | --------- | -------- | ------------------------- |
      | sessionID | `String` | Session ID of active call |

      **Success Response - Call Object:**

      | Property               | Type         | Description               |
      | ---------------------- | ------------ | ------------------------- |
      | sessionID              | `String?`    | Session ID of ended call  |
      | callStatus             | `CallStatus` | `.ended`                  |
      | endedAt                | `Double?`    | Timestamp when call ended |
      | totalDurationInMinutes | `Double`     | Total call duration       |
    </Tab>
  </Tabs>
</Accordion>

<Accordion title="Cleanup Methods">
  | Method                        | Description                           |
  | ----------------------------- | ------------------------------------- |
  | `CometChat.clearActiveCall()` | Clears the active call from CometChat |
  | `CometChatCalls.endSession()` | Ends the call session in CallsSDK     |
</Accordion>

### Ringing Flow

When using the [Ringing](/sdk/ios/default-calling) flow, you must coordinate between the CometChat Chat SDK and the Calls SDK to properly terminate the call.

<Note>
  The Ringing flow requires calling methods from both the Chat SDK (`CometChat.endCall()`) and the Calls SDK (`CometChatCalls.endSession()`) to ensure proper call termination.
</Note>

**User who initiates the end 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>

### Session Only Flow

When using the Session Only flow (without ringing), you only need to call the Calls SDK method to end the session.

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

## Methods

These methods are available for performing custom actions during an active call session. Use them to build custom UI controls or implement specific behaviors based on your use case.

<Note>
  These methods can only be called when a call session is active.
</Note>

### Switch Camera

Toggles between the front and rear camera during a video call.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.switchCamera()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls switchCamera];
    ```
  </Tab>
</Tabs>

### Mute Audio

Controls the local audio stream transmission.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    // Mute audio
    CometChatCalls.audioMuted(true)

    // Unmute audio
    CometChatCalls.audioMuted(false)
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    // Mute audio
    [CometChatCalls audioMuted:YES];

    // Unmute audio
    [CometChatCalls audioMuted:NO];
    ```
  </Tab>
</Tabs>

### Pause Video

Controls the local video stream transmission.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    // Pause video
    CometChatCalls.videoPaused(true)

    // Resume video
    CometChatCalls.videoPaused(false)
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    // Pause video
    [CometChatCalls videoPaused:YES];

    // Resume video
    [CometChatCalls videoPaused:NO];
    ```
  </Tab>
</Tabs>

### Set Audio Mode

Routes the audio output to a specific device.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.setAudioMode(mode: "SPEAKER")
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls setAudioModeWithMode:@"SPEAKER"];
    ```
  </Tab>
</Tabs>

### Enter PIP Mode

Enters Picture-in-Picture mode.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.enterPIPMode()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls enterPIPMode];
    ```
  </Tab>
</Tabs>

### Exit PIP Mode

Exits Picture-in-Picture mode.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.exitPIPMode()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls exitPIPMode];
    ```
  </Tab>
</Tabs>

### Switch To Video Call

Upgrades an ongoing audio call to a video call.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.switchToVideoCall()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls switchToVideoCall];
    ```
  </Tab>
</Tabs>

### Start Recording

Starts recording the call session.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.startRecording()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls startRecording];
    ```
  </Tab>
</Tabs>

### Stop Recording

Stops an ongoing call recording.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.stopRecording()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls stopRecording];
    ```
  </Tab>
</Tabs>

### End Call

Terminates the current call session and releases all media resources.

<Tabs>
  <Tab title="Swift">
    ```swift theme={null}
    CometChatCalls.endSession()
    ```
  </Tab>

  <Tab title="Objective-C">
    ```objc theme={null}
    [CometChatCalls endSession];
    ```
  </Tab>
</Tabs>

***

## In-Call Methods Reference

| Method                | Parameters | Description              |
| --------------------- | ---------- | ------------------------ |
| `switchCamera()`      | None       | Toggle front/rear camera |
| `audioMuted(_:)`      | `Bool`     | Mute/unmute microphone   |
| `videoPaused(_:)`     | `Bool`     | Pause/resume camera      |
| `setAudioMode(mode:)` | `String`   | Set audio output device  |
| `enterPIPMode()`      | None       | Enter Picture-in-Picture |
| `exitPIPMode()`       | None       | Exit Picture-in-Picture  |
| `switchToVideoCall()` | None       | Upgrade audio to video   |
| `startRecording()`    | None       | Start call recording     |
| `stopRecording()`     | None       | Stop call recording      |
| `endSession()`        | None       | End call session         |

***

## CallsEventsDelegate Callbacks

| Callback                                   | Payload                 | Description                            |
| ------------------------------------------ | ----------------------- | -------------------------------------- |
| `onCallEnded()`                            | None                    | Call session terminated                |
| `onSessionTimeout()`                       | None                    | Call auto-terminated due to inactivity |
| `onCallEndButtonPressed()`                 | None                    | User tapped end call button            |
| `onUserJoined(rtcUser:)`                   | `RTCUser`               | Remote participant joined              |
| `onUserLeft(rtcUser:)`                     | `RTCUser`               | Remote participant left                |
| `onUserListChanged(rtcUsers:)`             | `[RTCUser]`             | Participant list changed               |
| `onAudioModeChanged(mode:)`                | `[AudioMode]`           | Available audio devices changed        |
| `onCallSwitchedToVideo(callSwitchedInfo:)` | `CallSwitchRequestInfo` | Audio call upgraded to video           |
| `onUserMuted(rtcMutedUser:)`               | `RTCMutedUser`          | Participant mute state changed         |
| `onRecordingToggled(recordingInfo:)`       | `RTCRecordingInfo`      | Recording started/stopped              |

<Accordion title="Callback Object Properties">
  <Tabs>
    <Tab title="RTCUser">
      | Property | Type      | Description              |
      | -------- | --------- | ------------------------ |
      | uid      | `String?` | User's unique identifier |
      | name     | `String?` | User's display name      |
      | avatar   | `String?` | User's avatar URL        |
    </Tab>

    <Tab title="RTCMutedUser">
      | Property   | Type      | Description              |
      | ---------- | --------- | ------------------------ |
      | uid        | `String?` | User's unique identifier |
      | name       | `String?` | User's display name      |
      | audioMuted | `Bool?`   | Is audio muted           |
      | videoMuted | `Bool?`   | Is video muted           |
    </Tab>

    <Tab title="RTCRecordingInfo">
      | Property    | Type       | Description                |
      | ----------- | ---------- | -------------------------- |
      | isRecording | `Bool?`    | Is recording active        |
      | startedBy   | `RTCUser?` | User who started recording |
    </Tab>

    <Tab title="AudioMode">
      | Mode         | Description                |
      | ------------ | -------------------------- |
      | `SPEAKER`    | Phone speaker              |
      | `EARPIECE`   | Phone earpiece             |
      | `BLUETOOTH`  | Connected Bluetooth device |
      | `HEADPHONES` | Wired headphones           |
    </Tab>
  </Tabs>
</Accordion>
