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

# Migration Guide

> CometChat Calling SDK v5 - Migration Guide for iOS

This guide covers migrating from Calls SDK v4 to v5 for iOS.

## Drop-in Compatibility

Calls SDK v5 is a **drop-in replacement** for v4. All v4 APIs are preserved as deprecated methods that internally delegate to the new v5 implementations. You can update the dependency version and your existing code will compile and run without changes.

```ruby theme={null}
# CocoaPods — update your Podfile
-pod 'CometChatCallsSDK', '~> 4.0'
+pod 'CometChatCallsSDK', '~> 5.0'
```

```swift theme={null}
// Swift Package Manager — update your package dependency version to 5.x
```

<Info>
  If you're using CometChat UI Kits, simply updating the Calls SDK version is sufficient. The UI Kit will continue to work with v5 through the deprecated compatibility layer.
</Info>

## Why Migrate to v5 APIs?

While v4 APIs will continue to work, migrating to v5 APIs gives you:

* **Granular event listeners** — 5 focused listener protocols instead of one monolithic `CallsEventsDelegate`
* **`CallSession` singleton** for cleaner session control — all actions on a single object instead of scattered static methods
* **Dedicated `login()` method** — the Calls SDK now handles its own authentication instead of depending on the Chat SDK's auth token or REST APIs
* **Strongly-typed enums** — `AudioModeType`, `CallType`, `LayoutType`, `CameraFacing` instead of raw strings

***

## Initialization

No changes required. The `init` API is the same in v5.

```swift theme={null}
let callAppSettings = CallAppSettings()
callAppSettings.set(appId: "APP_ID")
callAppSettings.set(region: "REGION")

CometChatCalls(callsAppSettings: callAppSettings, onSuccess: { success in
    // Initialized
}, onError: { error in
    // Handle error
})
```

***

## Authentication

In v4, the Calls SDK had no dedicated authentication step. It relied on the Chat SDK's auth token (`CometChat.getUserAuthToken()`) or a REST API to obtain an auth token, which you then passed manually to `generateToken(authToken:sessionID:onSuccess:onError:)`.

v5 introduces its own `login()` method. After calling `login()`, the SDK caches the auth token internally, so you no longer need to pass it around to other API calls.

<Tabs>
  <Tab title="v4">
    ```swift theme={null}
    // No dedicated Calls SDK login — relied on Chat SDK for auth token
    let authToken = CometChat.getUserAuthToken()

    // Had to pass authToken manually
    CometChatCalls.generateToken(authToken: authToken as NSString?,
                                 sessionID: sessionID as NSString?,
                                 onSuccess: { token in
        // Use token to start session
    }, onError: { error in })
    ```
  </Tab>

  <Tab title="v5">
    ```swift theme={null}
    // Login to the Calls SDK once (after your Chat SDK login)
    CometChatCalls.login(authToken: authToken, onSuccess: { callsUser in
        // Calls SDK is now authenticated — auth token is cached internally
    }, onError: { error in })
    ```
  </Tab>
</Tabs>

<Note>
  Call `CometChatCalls.login()` once after your user authenticates (e.g., right after `CometChat.login()` succeeds). The SDK stores the auth token internally, so subsequent calls like `generateToken()` and `joinSession()` use it automatically without you having to pass it.
</Note>

***

## Session Settings

`CallSettingsBuilder` and `PresentationSettingsBuilder` are replaced by `SessionSettingsBuilder`. The builder methods have been renamed for clarity.

<Tabs>
  <Tab title="v4">
    ```swift theme={null}
    let callSettings = CometChatCalls.callSettingsBuilder
        .setIsAudioOnly(true)
        .setDefaultLayout(true)
        .setEndCallButtonDisable(false)
        .setMuteAudioButtonDisable(false)
        .setPauseVideoButtonDisable(false)
        .setSwitchCameraButtonDisable(false)
        .setAudioModeButtonDisable(false)
        .setShowRecordingButton(true)
        .setStartAudioMuted(false)
        .setStartVideoMuted(false)
        .setDefaultAudioMode("SPEAKER")
        .setMode("DEFAULT")
        .setStartRecordingOnCallStart(false)
        .setDelegate(self)
        .build()
    ```
  </Tab>

  <Tab title="v5">
    ```swift theme={null}
    let sessionSettings = CometChatCalls.sessionSettingsBuilder
        .setType(.audio)
        .startAudioMuted(false)
        .startVideoPaused(false)
        .setLayout(.tile)
        .setAudioMode(.speaker)
        .hideLeaveSessionButton(false)
        .hideToggleAudioButton(false)
        .hideToggleVideoButton(false)
        .hideSwitchCameraButton(false)
        .hideAudioModeButton(false)
        .hideRecordingButton(false)
        .hideControlPanel(false)
        .hideHeaderPanel(false)
        .enableAutoStartRecording(false)
        .build()
    ```
  </Tab>
</Tabs>

### Builder Method Mapping

| v4 Method                            | v5 Method                                            | Notes                                  |
| ------------------------------------ | ---------------------------------------------------- | -------------------------------------- |
| `setIsAudioOnly(true)`               | `setType(.audio)`                                    | Use `.video` for video calls           |
| `setDefaultLayout(bool)`             | `hideControlPanel(!bool)` + `hideHeaderPanel(!bool)` | Inverted logic                         |
| `setEndCallButtonDisable(bool)`      | `hideLeaveSessionButton(bool)`                       | Same logic                             |
| `setMuteAudioButtonDisable(bool)`    | `hideToggleAudioButton(bool)`                        | Same logic                             |
| `setPauseVideoButtonDisable(bool)`   | `hideToggleVideoButton(bool)`                        | Same logic                             |
| `setSwitchCameraButtonDisable(bool)` | `hideSwitchCameraButton(bool)`                       | Same logic                             |
| `setAudioModeButtonDisable(bool)`    | `hideAudioModeButton(bool)`                          | Same logic                             |
| `setShowRecordingButton(bool)`       | `hideRecordingButton(!bool)`                         | Inverted logic                         |
| `setStartAudioMuted(bool)`           | `startAudioMuted(bool)`                              | Same logic                             |
| `setStartVideoMuted(bool)`           | `startVideoPaused(bool)`                             | Same logic                             |
| `setDefaultAudioMode("SPEAKER")`     | `setAudioMode(.speaker)`                             | Enum instead of string                 |
| `setMode("DEFAULT")`                 | `setLayout(.tile)`                                   | Enum instead of string                 |
| `setStartRecordingOnCallStart(bool)` | `enableAutoStartRecording(bool)`                     | Same logic                             |
| `setAvatarMode(string)`              | *Removed*                                            | No longer applicable                   |
| `setEnableVideoTileClick(bool)`      | *Removed*                                            | No longer applicable                   |
| `setEnableDraggableVideoTile(bool)`  | *Removed*                                            | No longer applicable                   |
| `setDelegate(delegate)`              | Use `CallSession` listeners                          | See [Events](#event-listeners) section |

***

## Joining a Session

`startSession()` is replaced by `joinSession()`.

<Tabs>
  <Tab title="v4">
    ```swift theme={null}
    // Step 1: Generate token with auth token
    CometChatCalls.generateToken(authToken: authToken as NSString?,
                                 sessionID: sessionID as NSString?,
                                 onSuccess: { token in
        // Step 2: Start session with token string
        CometChatCalls.startSession(callToken: token ?? "",
                                    callSetting: callSettings,
                                    view: callView,
                                    onSuccess: { message in
            // Session started
        }, onError: { error in })
    }, onError: { error in })
    ```
  </Tab>

  <Tab title="v5">
    ```swift theme={null}
    // Option A: Two-step with token
    CometChatCalls.generateToken(sessionID: sessionID, onSuccess: { token in
        CometChatCalls.joinSession(callToken: token ?? "",
                                   callSetting: sessionSettings,
                                   container: callView,
                                   onSuccess: { message in
            // Session joined
        }, onError: { error in })
    }, onError: { error in })

    // Option B: One-step with session ID (recommended)
    CometChatCalls.joinSession(sessionID: sessionID,
                               callSetting: sessionSettings,
                               container: callView,
                               onSuccess: { message in
        // Token generation + join handled internally
    }, onError: { error in })
    ```
  </Tab>
</Tabs>

Key differences:

* v5 `generateToken()` no longer requires the `authToken` parameter (uses cached token from `login()`)
* v5 offers a convenience overload that takes `sessionID` directly and handles token generation internally
* Parameter name changed from `view` to `container`

***

## Session Control (Actions)

In v4, session actions were static methods on `CometChatCalls`. In v5, they're instance methods on `CallSession.shared`.

<Tabs>
  <Tab title="v4">
    ```swift theme={null}
    CometChatCalls.endSession()
    CometChatCalls.switchCamera()
    CometChatCalls.audioMuted(true)
    CometChatCalls.videoPaused(true)
    CometChatCalls.setAudioMode(mode: "SPEAKER")
    CometChatCalls.enterPIPMode()
    CometChatCalls.exitPIPMode()
    CometChatCalls.startRecording()
    CometChatCalls.stopRecording()
    ```
  </Tab>

  <Tab title="v5">
    ```swift theme={null}
    let callSession = CallSession.shared

    callSession.leaveSession()
    callSession.switchCamera()
    callSession.muteAudio()       // or callSession.unmuteAudio()
    callSession.pauseVideo()      // or callSession.resumeVideo()
    callSession.setAudioMode("SPEAKER")
    callSession.enablePictureInPictureLayout()
    callSession.disablePictureInPictureLayout()
    callSession.startRecording()
    callSession.stopRecording()
    ```
  </Tab>
</Tabs>

### Action Method Mapping

| v4 Static Method                     | v5 CallSession Method                         |
| ------------------------------------ | --------------------------------------------- |
| `CometChatCalls.endSession()`        | `callSession.leaveSession()`                  |
| `CometChatCalls.switchCamera()`      | `callSession.switchCamera()`                  |
| `CometChatCalls.audioMuted(true)`    | `callSession.muteAudio()`                     |
| `CometChatCalls.audioMuted(false)`   | `callSession.unmuteAudio()`                   |
| `CometChatCalls.videoPaused(true)`   | `callSession.pauseVideo()`                    |
| `CometChatCalls.videoPaused(false)`  | `callSession.resumeVideo()`                   |
| `CometChatCalls.setAudioMode(mode:)` | `callSession.setAudioMode(_:)`                |
| `CometChatCalls.enterPIPMode()`      | `callSession.enablePictureInPictureLayout()`  |
| `CometChatCalls.exitPIPMode()`       | `callSession.disablePictureInPictureLayout()` |
| `CometChatCalls.startRecording()`    | `callSession.startRecording()`                |
| `CometChatCalls.stopRecording()`     | `callSession.stopRecording()`                 |
| `CometChatCalls.switchToVideoCall()` | *Removed*                                     |

***

## Event Listeners

This is the biggest improvement in v5. The single `CallsEventsDelegate` protocol is replaced by 5 focused listener protocols.

### v4: Single Monolithic Delegate

```swift theme={null}
class MyViewController: UIViewController, CallsEventsDelegate {

    func setupCallEvents() {
        // Set via builder
        CometChatCalls.callSettingsBuilder.setDelegate(self).build()
    }

    func onCallEnded() { }
    func onCallEndButtonPressed() { }
    func onSessionTimeout() { }
    func onUserJoined(rtcUser: RTCUser) { }
    func onUserLeft(rtcUser: RTCUser) { }
    func onUserListChanged(rtcUsers: [RTCUser]) { }
    func onAudioModeChanged(mode: [CallAudioMode]) { }
    func onCallSwitchedToVideo(callSwitchedInfo: CallSwitchRequestInfo) { }
    func onUserMuted(rtcMutedUser: RTCMutedUser) { }
    func onRecordingToggled(recordingInfo: RTCRecordingInfo) { }
}
```

### v5: Focused Listener Protocols

```swift theme={null}
let callSession = CallSession.shared

// Session lifecycle events
class MySessionListener: SessionStatusListener {
    func onSessionJoined() { }
    func onSessionLeft() { }
    func onSessionTimedOut() { }
    func onConnectionLost() { }
    func onConnectionRestored() { }
    func onConnectionClosed() { }
}
callSession.addSessionStatusListener(mySessionListener)

// Participant events (replaces onUserJoined, onUserLeft, onUserListChanged, onUserMuted)
class MyParticipantListener: ParticipantEventListener {
    func onParticipantJoined(participant: Participant) { }
    func onParticipantLeft(participant: Participant) { }
    func onParticipantListChanged(participants: [Participant]) { }
    func onParticipantAudioMuted(participant: Participant) { }
    func onParticipantAudioUnmuted(participant: Participant) { }
    func onParticipantVideoPaused(participant: Participant) { }
    func onParticipantVideoResumed(participant: Participant) { }
    func onParticipantHandRaised(participant: Participant) { }
    func onParticipantHandLowered(participant: Participant) { }
    func onParticipantStartedRecording(participant: Participant) { }
    func onParticipantStoppedRecording(participant: Participant) { }
    func onDominantSpeakerChanged(participant: Participant) { }
}
callSession.addParticipantEventListener(myParticipantListener)

// Media state events (replaces onAudioModeChanged, onRecordingToggled)
class MyMediaListener: MediaEventsListener {
    func onAudioMuted() { }
    func onAudioUnMuted() { }
    func onVideoPaused() { }
    func onVideoResumed() { }
    func onRecordingStarted() { }
    func onRecordingStopped() { }
    func onAudioModeChanged(audioModeType: AudioModeType) { }
    func onCameraFacingChanged(cameraFacing: CameraFacing) { }
}
callSession.addMediaEventsListener(myMediaListener)

// Button click events (replaces onCallEndButtonPressed)
class MyButtonListener: ButtonClickListener {
    func onLeaveSessionButtonClicked() { }
    func onToggleAudioButtonClicked() { }
    func onToggleVideoButtonClicked() { }
    func onSwitchCameraButtonClicked() { }
    func onRaiseHandButtonClicked() { }
    func onRecordingToggleButtonClicked() { }
}
callSession.addButtonClickListener(myButtonListener)

// Layout events
class MyLayoutListener: LayoutListener {
    func onCallLayoutChanged(layoutType: LayoutType) { }
    func onPictureInPictureLayoutEnabled() { }
    func onPictureInPictureLayoutDisabled() { }
}
callSession.addLayoutListener(myLayoutListener)
```

<Note>
  v5 listeners use weak references internally, so they are automatically cleaned up when the listener object is deallocated. You can also explicitly remove listeners using `removeSessionStatusListener(_:)`, `removeParticipantEventListener(_:)`, etc.
</Note>

### Event Mapping

| v4 Event                                   | v5 Listener                | v5 Event                                        |
| ------------------------------------------ | -------------------------- | ----------------------------------------------- |
| `onCallEnded()`                            | `SessionStatusListener`    | `onSessionLeft()`                               |
| `onCallEndButtonPressed()`                 | `ButtonClickListener`      | `onLeaveSessionButtonClicked()`                 |
| `onSessionTimeout()`                       | `SessionStatusListener`    | `onSessionTimedOut()`                           |
| `onUserJoined(rtcUser:)`                   | `ParticipantEventListener` | `onParticipantJoined(participant:)`             |
| `onUserLeft(rtcUser:)`                     | `ParticipantEventListener` | `onParticipantLeft(participant:)`               |
| `onUserListChanged(rtcUsers:)`             | `ParticipantEventListener` | `onParticipantListChanged(participants:)`       |
| `onAudioModeChanged(mode:)`                | `MediaEventsListener`      | `onAudioModeChanged(audioModeType:)`            |
| `onCallSwitchedToVideo(callSwitchedInfo:)` | *Removed*                  | —                                               |
| `onUserMuted(rtcMutedUser:)`               | `ParticipantEventListener` | `onParticipantAudioMuted(participant:)`         |
| `onRecordingToggled(recordingInfo:)`       | `MediaEventsListener`      | `onRecordingStarted()` / `onRecordingStopped()` |

### New Events in v5

These events are only available with the v5 listener APIs:

| Listener                   | Event                                        | Description                      |
| -------------------------- | -------------------------------------------- | -------------------------------- |
| `SessionStatusListener`    | `onConnectionLost()`                         | Network interrupted              |
| `SessionStatusListener`    | `onConnectionRestored()`                     | Network restored                 |
| `SessionStatusListener`    | `onConnectionClosed()`                       | Connection permanently closed    |
| `ParticipantEventListener` | `onParticipantVideoResumed()`                | Participant turned camera on     |
| `ParticipantEventListener` | `onParticipantVideoPaused()`                 | Participant turned camera off    |
| `ParticipantEventListener` | `onParticipantHandRaised()`                  | Participant raised hand          |
| `ParticipantEventListener` | `onParticipantHandLowered()`                 | Participant lowered hand         |
| `ParticipantEventListener` | `onParticipantStartedScreenShare()`          | Participant started screen share |
| `ParticipantEventListener` | `onParticipantStoppedScreenShare()`          | Participant stopped screen share |
| `ParticipantEventListener` | `onDominantSpeakerChanged()`                 | Active speaker changed           |
| `MediaEventsListener`      | `onCameraFacingChanged()`                    | Camera switched front/back       |
| `ButtonClickListener`      | Various button events                        | Individual button click tracking |
| `LayoutListener`           | `onCallLayoutChanged()`                      | Layout type changed              |
| `LayoutListener`           | `onPictureInPictureLayoutEnabled/Disabled()` | PiP state changed                |

***

## Call Logs

The `CallLogsRequest` API is unchanged. The only difference is that auth is now handled by `CometChatCalls.login()` instead of passing the auth token manually.

***

## Deprecated Classes Summary

These classes still exist in v5 for backward compatibility but are deprecated:

| Deprecated Class              | Replacement                              |
| ----------------------------- | ---------------------------------------- |
| `CallSettings`                | `SessionSettingsBuilder.SessionSettings` |
| `CallSettingsBuilder`         | `SessionSettingsBuilder`                 |
| `PresentationSettings`        | `SessionSettingsBuilder.SessionSettings` |
| `PresentationSettingsBuilder` | `SessionSettingsBuilder`                 |
| `CallsEventsDelegate`         | 5 focused listeners on `CallSession`     |
| `RTCRecordingInfo`            | `MediaEventsListener` callbacks          |
| `CallSwitchRequestInfo`       | *Removed (no replacement)*               |
