Fallback emails sent only when a chat message stays unread beyond your configurable wait window.
+
+
+
+
+
+
+ Set up HTML templates for unread-message alerts (e.g., subject/body with sender and message preview).
+
+
+ Define how long to wait for the message to be read; if it is still unread, CometChat triggers the email to the intended recipient.
+
+
+ Honor user/channel preferences and regional rules before sending.
+
+
+
+
+
+
+
Get started
+
+
+
+
+
+
+
+
+
SMS Notifications
+
SMS fallbacks fire only when a chat message remains unread after your configurable wait window.
+
+
+
+
+
+
+ Connect credentials and map the “message unread past wait window” trigger.
+
+
+ Keep payloads concise (sender + message snippet, optional deep link to the conversation).
+
+
+ CometChat handles routing, retries, user preferences, and regional compliance.
+
+
+
+
+
+
+
Get started
+
+
+
+
+
+
+
+{/* Sample Apps Section */}
+
+
+ Push Notification Demos
+
+
+
+ Show working experiences just like Chat & Calling: clones, payloads, and end-to-end journeys.
+
diff --git a/notifications/android-push-notifications-legacy.mdx b/notifications/android-push-notifications-legacy.mdx
new file mode 100644
index 00000000..b81f35bd
--- /dev/null
+++ b/notifications/android-push-notifications-legacy.mdx
@@ -0,0 +1,524 @@
+---
+title: "Android"
+---
+
+The Push Notification extension allows you to send push notifications to mobile apps and desktop browsers. In this section, we will see how to send Push Notifications to your Android app using Firebase Cloud Messaging or FCM.
+
+
+Use Connection Service
+
+If you want to use the System's native call service to handle calls, please refer to our guide on [Android - Connection Service](/notifications/android-connection-service)
+
+
+
+
+ Android Push notifications sample app
+
+ View on Github
+
+
+## Firebase Project Setup
+
+Visit [Firebase Console](https://console.firebase.google.com) and login/signup using your Gmail ID.
+
+### Step 1: Create a new Firebase Project
+
+On your Firebase Console, create a new project.
+
+
+
+
+
+This is a simple 3 step process where:
+
+1. You give a name to your project
+2. Add Google Analytics to your project (Optional)
+3. Configure Google Analytics account (Optional)
+
+Click on Create and you are ready to go.
+
+### Step 2: Add Firebase to your Android App
+
+1. Click on the Android icon as shown on the screen below.
+
+
+
+
+
+2. Register your Android app by providing the following details:
+
+ 1. Android Package name
+ 2. App nickname (optional)
+ 3. Debug signing certificate SHA-1 (optional)
+
+
+
+
+
+3. Download the `google-services.json` file and place it in the required location in your project.
+
+
+
+
+
+4. Add Firebase SDK by copying and pasting the snippets in the Project-level `build.gradle` file.
+
+
+
+
+
+5. Add Firebase SDK by copying and pasting the snippets in the App-level `build.gradle` file.
+
+
+
+
+
+6. Click on 'Continue to Console' to finish the setup.
+
+### Step 3: Download the service account file
+
+
+
+
+
+## Extension settings
+
+### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications extension.
+3. Open the settings for this extension and save the following.
+
+
+
+
+
+### Step 2: Save your settings
+
+On the Settings page you need to enter the following:
+
+1. **Set extension version**
+
+* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
+* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
+* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+
+2. **Select the platforms that you want to support**
+
+* Select from Web, Android, Ionic, React Native, Flutter & iOS.
+
+3. **Notification payload settings**
+
+* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
+
+4. **Push payload message options**
+
+
+
+
+
+The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+
+* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+
+5. **Notification Triggers**
+
+
+
+
+
+* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+* These are pretty self-explanatory and you can toggle them as per your requirement.
+
+## Android App Setup
+
+In the Firebase Project setup, we did the following things:
+
+1. Added google-services.json file to the project.
+2. Added the required Firebase SDK snippets to the Project-level build.grade file.
+3. Added the required Firebase SDK snippets to the App-level build.gradle file.
+
+If you want more details, check the [Firebase Documentation](https://firebase.google.com/docs/cloud-messaging/android/client).
+
+### Step 1: Register the FCM Token on user login
+
+1. Initialize CometChat and then login your user.
+2. On successful login, you can register the obtained FCM Token using `CometChat.registerTokenForPushNotification()` function call. (You can see the process of getting the FCM Token in the next step)
+
+
+
+```java
+CometChat.registerTokenForPushNotification(MyFirebaseMessagingService.token, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(String s) {
+ Log.e( "onSuccessPN: ",s );
+ }
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("onErrorPN: ",e.getMessage() );
+ }
+});
+```
+
+
+
+
+```kotlin
+CometChat.registerTokenForPushNotification(MyFirebaseMessagingService.token, object : CallbackListener() {
+ override fun onSuccess(s: String?) {
+ Log.e("onSuccessPN: ", s)
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("onErrorPN: ", e.message)
+ }
+ })
+```
+
+
+
+
+
+To fetch the registered token you can use below Firebase method.
+
+
+
+```java
+FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(
+ new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (!task.isSuccessful()) {
+ return;
+ }
+ token = task.getResult().getToken();
+ //CometChat.registerTokenForPushNotification(token, CometChat.CallbackListener());
+ }
+});
+```
+
+
+
+
+```kotlin
+FirebaseInstanceId.getInstance().getInstanceId()
+ .addOnCompleteListener(object : OnCompleteListener() {
+ fun onComplete(task: com.google.android.gms.tasks.Task) {
+ if (!task.isSuccessful()) {
+ return
+ }
+ token = task.getResult().getToken()
+ //CometChat.registerTokenForPushNotification(token,CometChat.CallbackListener())
+ }
+ })
+```
+
+
+
+
+
+### Step 2: Receive notifications
+
+1. The FCM Token can be received by overriding the `onNewToken()` method. This token is stored as a String variable. You can choose to store it in SharedPreferences as well.
+2. To receive messages, you need to override the onMessageReceived(RemoteMessage remoteMessage).
+3. [PushNotificationService.java](https://github.com/cometchat/cometchat-push-notification-app-android/blob/v4-push-notifications-extension/app/src/main/java/com/cometchat/pushnotificationsample/PushNotificationService.java) has the code that provides a way you can handle messages received from CometChat users and groups.
+4. CallNotificationAction.class is a BroadcastReceiver which is used to handle call events when your app is in the background state.
+5. Since Android O, there have been certain restrictions added for background tasks and users cannot launch intent directly from the service. More details [here](https://developer.android.com/guide/components/activities/background-starts).
+6. We suggest you to create notification channel inside your application class. After Android O, it is necessary to register notification channel to allow notifications of your apps.
+
+
+
+```java
+private void createNotificationChannel() {
+ // Create the NotificationChannel, but only on API 26+ because
+ // the NotificationChannel class is new and not in the support library
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ CharSequence name = getString(R.string.app_name);
+ String description = getString(R.string.channel_description);
+ int importance = NotificationManager.IMPORTANCE_HIGH;
+ NotificationChannel channel = new NotificationChannel("2", name, importance);
+ channel.setDescription(description);
+ channel.enableVibration(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+```
+
+
+
+
+
+* You also need to add both of the above-mentioned files in your AndroidManifest.xml to make Push notification work in the background as well.
+
+
+
+```xml
+
+
+
+
+
+
+```
+
+
+
+
+
+## Advanced
+
+### Sending Custom Notification body
+
+Push notification has 2 parts, namely, the notification title and notification body. The title can be: a. Name of the sender in case of one-on-one message. (E.g.: Nancy Grace) b. Name of the sender followed by group name for group messages (E.g.: Nancy Grace @ Hiking Group)
+
+The body of the message depends upon the type of message being sent. You can send a custom body by specifying the `pushNotification` key followed by some user-defined string for the notification body inside `metadata` while sending the message.
+
+The following code shows an example of a Custom body using a message of category=custom. This is however not just limited to a custom category of messages.
+
+
+
+```java
+String receiverId="cometchat-uid-1";
+JSONObject metaData=new JSONObject();
+JSONObject customData=new JSONObject();
+
+try {
+ metaData.put("pushNotification","Custom Notification body");
+ customData.put("yourkey","Your Value");
+ } catch (JSONException e) {
+ e.printStackTrace();
+}
+
+CustomMessage customMessage=new CustomMessage(receiverId,CometChatConstants.RECEIVER_TYPE_USER,customData);
+customMessage.setMetadata(metaData);
+
+CometChat.sendCustomMessage(customMessage, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(CustomMessage customMessage) {
+ Log.d(TAG, "onSuccess: "+customMessage.toString());
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.d(TAG, "onError: "+e.getMessage());
+ }
+ });
+```
+
+
+
+
+```kotlin
+var receiverId:String="cometchat-uid-1"
+var metaData:JSONObject=JSONObject()
+var customData:JSONObject= JSONObject()
+
+ try {
+ metaData.put("pushNotification","Custom Notification Body")
+ customData.put("yourkey","Your Value")
+ } catch (e:JSONException) {
+ e.printStackTrace()
+ }
+var customMessage = CustomMessage(receiverId,CometChatConstants.RECEIVER_TYPE_USER,customData)
+customMessage.metadata = metaData;
+
+CometChat.sendCustomMessage(customMessage, object :CometChat.CallbackListener() {
+ override fun onSuccess(p0: CustomMessage?) {
+ Log.d(TAG,"onSuccess ${p0?.toString()}")
+ }
+
+ override fun onError(p0: CometChatException?) {
+ Log.d(TAG,"onError ${p0?.message}")
+ }
+})
+```
+
+
+
+
+
+### Converting Push Notification Payloads to Message Objects
+
+CometChat provides a method `CometChatHelper.processMessage()` to convert the message JSON to the corresponding object of `TextMessage`, `MediaMessage`, `CustomMessage`, `Action` or `Call`.
+
+This code needs to be added to the `onMessageReceived()` method of the `FirebaseMessagingService` class.
+
+
+
+```java
+CometChatHelper.processMessage(new JSONObject(remoteMessage.getData().get("message"));
+```
+
+
+
+
+
+
+
+Type of Attachment can be of the following the type\
+`CometChatConstants.MESSAGE_TYPE_IMAGE`\
+`CometChatConstants.MESSAGE_TYPE_VIDEO`\
+`CometChatConstants.MESSAGE_TYPE_AUDIO`\
+`CometChatConstants.MESSAGE_TYPE_FILE`
+
+
+
+Push Notification Payload sample for text and media messages-
+
+
+
+```json
+{
+ "alert": "Nancy Grace: Text Message",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "text": "Text Message"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "142",
+ "sentAt": 1555668711,
+ "category": "message",
+ "type": "text"
+ }
+}
+```
+
+
+
+
+```json
+{
+ "alert": "Nancy Grace: has sent an image",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "attachments": [
+ {
+ "extension": "png",
+ "size": 14327,
+ "name": "extension_leftpanel.png",
+ "mimeType": "image_png",
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ }
+ ],
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "145",
+ "sentAt": 1555671238,
+ "category": "message",
+ "type": "image"
+ }
+}
+```
+
+
+
+
+
+### Handle Push Notification Actions.
+
+
+
+
+
+**Step 1. Process push notification payload and grab BaseMessage object**
+
+To open a chat view, firstly you will need a BaseMessage object. You can grab this from the push notification payload received in `onMessageReceived(RemoteMessage message)`. You need to call `CometChat.processMessage()` method to process push notification payload.
+
+
+
+```java
+@Override
+public void onMessageReceived(RemoteMessage remoteMessage) {
+ try {
+ JSONObject messageData = new JSONObject(remoteMessage.getData().get("message"));
+ BaseMessage baseMessage = CometChatHelper.processMessage(messageData);
+
+ //Process BaseMessage and show Notification
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+}
+```
+
+
+
+
+
+**Step 2 . Handle Notification Actions**
+
+You can launch the chat view after you tap on the Message Notification by creating PendingIntent and set it with NotificationBuilder object.
+
+
+
+CometChatMessageListActivity is part of UI Kit Library. You can replace CometChatMessageListActivity with your required class.
+
+
diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx
index b81f35bd..8a8c40f5 100644
--- a/notifications/android-push-notifications.mdx
+++ b/notifications/android-push-notifications.mdx
@@ -1,524 +1,644 @@
---
-title: "Android"
+title: "Android Push Notifications"
+description: "Setup FCM and CometChat for message and call push notifications on Android."
---
-
-The Push Notification extension allows you to send push notifications to mobile apps and desktop browsers. In this section, we will see how to send Push Notifications to your Android app using Firebase Cloud Messaging or FCM.
-
-
-Use Connection Service
-
-If you want to use the System's native call service to handle calls, please refer to our guide on [Android - Connection Service](/notifications/android-connection-service)
-
-
-
+
- Android Push notifications sample app
-
- View on Github
+ Reference implementation of Kotlin UI Kit, FCM and Push Notification Setup.
-## Firebase Project Setup
-
-Visit [Firebase Console](https://console.firebase.google.com) and login/signup using your Gmail ID.
-
-### Step 1: Create a new Firebase Project
+## What this guide covers
-On your Firebase Console, create a new project.
-
-
-
-
-
-This is a simple 3 step process where:
-
-1. You give a name to your project
-2. Add Google Analytics to your project (Optional)
-3. Configure Google Analytics account (Optional)
-
-Click on Create and you are ready to go.
-
-### Step 2: Add Firebase to your Android App
-
-1. Click on the Android icon as shown on the screen below.
-
-
-
-
+- FCM setup and CometChat provider wiring (credentials + Gradle + manifest).
+- Token registration/unregistration so CometChat routes pushes correctly.
+- Handling message pushes with grouped notifications and inline reply.
+- Handling call pushes with `ConnectionService` for native telecom UI.
+- Deep links/navigation from notifications and payload customization.
-2. Register your Android app by providing the following details:
+{/* ## What you need first
- 1. Android Package name
- 2. App nickname (optional)
- 3. Debug signing certificate SHA-1 (optional)
+- Firebase project with an Android app added, `google-services.json` downloaded, and Cloud Messaging enabled.
+- CometChat App ID, Region, Auth Key; **Push Notifications** enabled with an **FCM Android provider** and its Provider ID.
+- Android device with Play Services (sample uses `minSdk 26` because of `ConnectionService`).
+- Latest CometChat UI Kit + Calls SDK dependencies (see Gradle tabs below). */}
-
-
-
+## How FCM and CometChat fit together
-3. Download the `google-services.json` file and place it in the required location in your project.
+- **Why FCM?** Google issues device tokens and delivers raw push payloads to Android. You must add `google-services.json`, the Messaging SDK, and a service receiver (`FCMService`) so the device can receive pushes.
+- **Why a CometChat provider?** The Provider ID tells CometChat which FCM credentials to use when sending to your app. Without registering tokens against this ID, CometChat cannot target your device.
+- **Token registration bridge:** The app retrieves the FCM token and calls `CometChatNotifications.registerPushToken(pushToken, PushPlatforms.FCM_ANDROID, providerId, …)`. That binds the token to your logged-in user so CometChat can route message/call pushes to FCM on your behalf.
+- **Payload handling:** When FCM delivers a push, your `FCMService`/`FCMMessageBroadcastReceiver` parses CometChat’s payload, shows notifications (grouped, inline reply), and forwards intents to your activities. For calls, `CometChatVoIPConnectionService` surfaces a telecom-grade UI and uses the same payload to accept/reject server-side.
+- **Dashboard ↔ app contract:** The Provider ID in `AppConstants.FCMConstants.PROVIDER_ID` must match the dashboard provider you created. The package name in Firebase and the `applicationId` in Gradle must match, or FCM will reject the token.
-
-
-
+## 1. Prepare Firebase and CometChat
-4. Add Firebase SDK by copying and pasting the snippets in the Project-level `build.gradle` file.
+1. **Firebase Console**: Add your Android package, download `google-services.json` into the app module, and enable Cloud Messaging.
-
+
-5. Add Firebase SDK by copying and pasting the snippets in the App-level `build.gradle` file.
+2. **CometChat dashboard**: Go to **Notifications → Settings**, enable **Push Notifications**, click **Add Credentials** (FCM), upload the Firebase service account JSON (Project settings → Service accounts → Generate new private key), and copy the Provider ID.
-
+
-6. Click on 'Continue to Console' to finish the setup.
-
-### Step 3: Download the service account file
+From the same screen, click **Add Provider** to upload the Firebase service account JSON. This is how you can Generate a new private key from Firebase:
-
+
-## Extension settings
-
-### Step 1: Enable the extension
-
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open the settings for this extension and save the following.
-
-
-
-
+3. **App constants**:
+Note down your CometChat App ID, Auth Key, and Region from the CometChat dashboard and keep them available to be added to your project's AppCrendentials.kt.
+Similarly, note the FCM Provider ID generated in the CometChat dashboard and add the same in your AppConstants.kt; this will be when registering the FCM token with CometChat.
-### Step 2: Save your settings
+## 2. Add dependencies (Gradle)
-On the Settings page you need to enter the following:
+Use a version catalog and aliases (Update `applicationId`, package names, icons, and app name.). Also, if you are new to CometChat, please review the Maven repositories and related setup requirements before proceeding.
-1. **Set extension version**
+
+
+
+```toml lines
+[versions]
+minSdk = "26"
+compileSdk = "35"
+targetSdk = "35"
+agp = "8.7.0"
+kotlin = "2.0.0"
+googleServices = "4.4.2"
+cometChatUikit = "5.2.6"
+cometChatSdk = "4.1.8"
+cometChatCalls = "4.3.2"
+firebaseBom = "33.7.0"
+coreKtx = "1.13.1"
+appcompat = "1.7.0"
+material = "1.12.0"
+gson = "2.11.0"
+glide = "4.16.0"
+
+[libraries]
+cometchat-uikit = { group = "com.cometchat", name = "chat-uikit", version.ref = "cometChatUikit" }
+cometchat-sdk = { group = "com.cometchat", name = "chat-sdk-android", version.ref = "cometChatSdk" }
+cometchat-calls = { group = "com.cometchat", name = "calls-sdk-android", version.ref = "cometChatCalls" }
+firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
+firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" }
+firebase-auth = { group = "com.google.firebase", name = "firebase-auth" }
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
+glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
+```
-* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
-* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
-* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+This TOML file defines versions and aliases for the required dependencies.
+
+
-2. **Select the platforms that you want to support**
+```gradle lines
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.google.services)
+}
-* Select from Web, Android, Ionic, React Native, Flutter & iOS.
+android {
+ compileSdk 35
+ defaultConfig {
+ applicationId "your.package.name"
+ minSdk 26
+ targetSdk 35
+ }
+ kotlinOptions { jvmTarget = "11" }
+}
-3. **Notification payload settings**
+dependencies {
+ // CometChat
+ implementation(libs.cometchat.uikit)
+ implementation(libs.cometchat.sdk)
+ implementation(libs.cometchat.calls)
+
+ // Firebase
+ implementation(platform(libs.firebase.bom))
+ implementation(libs.firebase.messaging)
+ implementation(libs.firebase.auth)
+
+ // UI + utilities
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.gson)
+ implementation(libs.glide)
+}
+```
-* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
+
+
-4. **Push payload message options**
+- Apply the `google-services` plugin and place `google-services.json` in the same module; keep `viewBinding` enabled if you copy UI Kit screens directly from the sample.
+- Update `applicationId`, package names, icons, and app name as needed.
+
+## 3. Manifest permissions and services
+
+Start from the sample [`AndroidManifest.xml`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/AndroidManifest.xml):
+
+```xml lines highlight={15, 19, 26, 29}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
-
-
-
+- Permissions cover notifications + telecom; services/receiver wire Firebase delivery (`FCMService`), notification actions (`FCMMessageBroadcastReceiver`), and telecom UI (`CometChatVoIPConnectionService`). Point `android:name` to your `MyApplication`.
-The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+- Set `android:name` on `` to your `MyApplication` subclass.
+- Keep runtime permission prompts for notifications, mic, camera, and media access (see `AppUtils.kt` / `HomeActivity.kt` in the sample).
-* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+## 4. Application wiring, sample code, and callbacks
-5. **Notification Triggers**
+- Clone/open the [reference repo](https://github.com/cometchat/cometchat-uikit-android/tree/v5/sample-app-kotlin%2Bpush-notification).
+- Copy into your app module (keep structure):
+ - [`fcm/fcm`](https://github.com/cometchat/cometchat-uikit-android/tree/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/fcm) for services/DTOs/notification utils/broadcast receiver.
+ - [`fcm/voip`](https://github.com/cometchat/cometchat-uikit-android/tree/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/voip) for ConnectionService + VoIP helpers.
+ - `fcm/utils` for `MyApplication`, `AppUtils`, `AppConstants`, `AppCredentials`.
+ - Copy String values from `res/values/strings.xml`.
+ - BuildConfig file `build.gradle`.
+- Update packages to your namespace; set `AppCredentials` (App ID/Auth Key/Region) and `AppConstants.FCMConstants.PROVIDER_ID` to your dashboard provider. Point `` and services/receivers to your package; update app name/icons as needed.
+- Keep notification constants from [`AppConstants.kt`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/utils/AppConstants.kt); rename channels/keys consistently if you change them.
-
-
-
+**What the core pieces do**
-* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+- `FCMService` – receives FCM data/notification messages, parses CometChat payload, and hands off to `FCMMessageBroadcastReceiver`.
+- `FCMMessageBroadcastReceiver` – builds grouped notifications, inline reply actions, and routes taps/deeplinks to your `HomeActivity`.
+- `Repository.registerFCMToken` – fetches the FCM token and registers it with CometChat using `AppConstants.FCMConstants.PROVIDER_ID`; call after login.
+- `Repository.acceptCall/rejectCall/rejectCallWithBusyStatus` – performs server-side call actions so the caller sees the correct state even if your UI is backgrounded.
+- `MyApplication` – initializes UIKit, manages websocket connect/disconnect, tracks foreground state, and shows/dismisses incoming call overlays.
+- `CometChatVoIPConnectionService` – handles Android telecom integration so call pushes display a system-grade incoming call UI and cleanly end/busy on reject.
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
+**Splash/entry deep link handler** (adapt activity targets):
-* These are pretty self-explanatory and you can toggle them as per your requirement.
+```kotlin lines
+// In your Splash/entry activity (e.g., SplashActivity)
+override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ handleDeepLinking()
+}
-## Android App Setup
+private fun handleDeepLinking() {
+ NotificationManagerCompat.from(this)
+ .cancel(AppConstants.FCMConstants.NOTIFICATION_GROUP_SUMMARY_ID)
-In the Firebase Project setup, we did the following things:
+ val notificationType = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_TYPE)
+ val notificationPayload = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_PAYLOAD)
-1. Added google-services.json file to the project.
-2. Added the required Firebase SDK snippets to the Project-level build.grade file.
-3. Added the required Firebase SDK snippets to the App-level build.gradle file.
+ startActivity(
+ Intent(this, HomeActivity::class.java).apply {
+ putExtra(AppConstants.FCMConstants.NOTIFICATION_TYPE, notificationType)
+ putExtra(AppConstants.FCMConstants.NOTIFICATION_PAYLOAD, notificationPayload)
+ }
+ )
+ finish()
+}
+```
-If you want more details, check the [Firebase Documentation](https://firebase.google.com/docs/cloud-messaging/android/client).
+This reads the push extras, clears the summary notification, and forwards the payload to `HomeActivity` so taps or deep links land in the right screen.
-### Step 1: Register the FCM Token on user login
+**SplashViewModel (init UIKit + login check)**
-1. Initialize CometChat and then login your user.
-2. On successful login, you can register the obtained FCM Token using `CometChat.registerTokenForPushNotification()` function call. (You can see the process of getting the FCM Token in the next step)
+```kotlin lines
+class SplashViewModel : ViewModel() {
+ private val loginStatus = MutableLiveData()
-
-
-```java
-CometChat.registerTokenForPushNotification(MyFirebaseMessagingService.token, new CometChat.CallbackListener() {
- @Override
- public void onSuccess(String s) {
- Log.e( "onSuccessPN: ",s );
- }
- @Override
- public void onError(CometChatException e) {
- Log.e("onErrorPN: ",e.getMessage() );
- }
-});
-```
+ fun initUIKit(context: Context) {
+ val appId = AppUtils.getDataFromSharedPref(context, String::class.java, R.string.app_cred_id, AppCredentials.APP_ID)
+ val region = AppUtils.getDataFromSharedPref(context, String::class.java, R.string.app_cred_region, AppCredentials.REGION)
+ val authKey = AppUtils.getDataFromSharedPref(context, String::class.java, R.string.app_cred_auth, AppCredentials.AUTH_KEY)
-
+ val uiKitSettings = UIKitSettings.UIKitSettingsBuilder()
+ .setAutoEstablishSocketConnection(false)
+ .setAppId(appId)
+ .setRegion(region)
+ .setAuthKey(authKey)
+ .subscribePresenceForAllUsers()
+ .build()
-
-```kotlin
-CometChat.registerTokenForPushNotification(MyFirebaseMessagingService.token, object : CallbackListener() {
- override fun onSuccess(s: String?) {
- Log.e("onSuccessPN: ", s)
+ CometChatUIKit.init(context, uiKitSettings, object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String) {
+ CometChat.setDemoMetaInfo(getAppMetadata(context))
+ checkUserIsNotLoggedIn()
}
-
override fun onError(e: CometChatException) {
- Log.e("onErrorPN: ", e.message)
+ Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}
})
+ }
+
+ private fun getAppMetadata(context: Context): JSONObject {
+ val jsonObject = JSONObject()
+ jsonObject.put("name", context.getString(R.string.app_name))
+ jsonObject.put("bundle", BuildConfig.APPLICATION_ID)
+ jsonObject.put("version", BuildConfig.VERSION_NAME)
+ jsonObject.put("platform", "android")
+ return jsonObject
+ }
+
+ fun checkUserIsNotLoggedIn() {
+ loginStatus.value = CometChatUIKit.getLoggedInUser() != null
+ }
+
+ fun getLoginStatus(): LiveData = loginStatus
+}
```
-
+Loads credentials from shared prefs, builds `UIKitSettings`, initializes CometChat UIKit (without auto socket), sets sample metadata, and exposes `loginStatus` so the splash can route to login vs home.
-
+**Repository (push token + call helpers)**
-To fetch the registered token you can use below Firebase method.
+```kotlin lines
+object Repository {
+ fun registerFCMToken(listener: CometChat.CallbackListener) { /* fetch FCM token and call registerPushToken */ }
+ fun unregisterFCMToken(listener: CometChat.CallbackListener) { /* call unregisterPushToken */ }
-
-
-```java
-FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(
- new OnCompleteListener() {
- @Override
- public void onComplete(@NonNull Task task) {
- if (!task.isSuccessful()) {
- return;
- }
- token = task.getResult().getToken();
- //CometChat.registerTokenForPushNotification(token, CometChat.CallbackListener());
- }
-});
-```
+ fun rejectCallWithBusyStatus(
+ call: Call,
+ callbackListener: CometChat.CallbackListener? = null
+ ) { /* reject with CALL_STATUS_BUSY and notify UIKit */ }
-
+ fun acceptCall(
+ call: Call,
+ callbackListener: CometChat.CallbackListener
+ ) { /* acceptCall and notify UIKit */ }
-
-```kotlin
-FirebaseInstanceId.getInstance().getInstanceId()
- .addOnCompleteListener(object : OnCompleteListener() {
- fun onComplete(task: com.google.android.gms.tasks.Task) {
- if (!task.isSuccessful()) {
- return
- }
- token = task.getResult().getToken()
- //CometChat.registerTokenForPushNotification(token,CometChat.CallbackListener())
- }
- })
+ fun rejectCall(
+ call: Call,
+ callbackListener: CometChat.CallbackListener
+ ) { /* rejectCall with CALL_STATUS_REJECTED and notify UIKit */ }
+}
```
-
+Thin wrappers that register/unregister FCM tokens with your Provider ID and perform server-side call actions (accept/reject/busy) so the caller sees the correct state even if your UI is backgrounded.
-
+**MyApplication (push/call lifecycle essentials)**
-### Step 2: Receive notifications
+```kotlin lines
+class MyApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
-1. The FCM Token can be received by overriding the `onNewToken()` method. This token is stored as a String variable. You can choose to store it in SharedPreferences as well.
-2. To receive messages, you need to override the onMessageReceived(RemoteMessage remoteMessage).
-3. [PushNotificationService.java](https://github.com/cometchat/cometchat-push-notification-app-android/blob/v4-push-notifications-extension/app/src/main/java/com/cometchat/pushnotificationsample/PushNotificationService.java) has the code that provides a way you can handle messages received from CometChat users and groups.
-4. CallNotificationAction.class is a BroadcastReceiver which is used to handle call events when your app is in the background state.
-5. Since Android O, there have been certain restrictions added for background tasks and users cannot launch intent directly from the service. More details [here](https://developer.android.com/guide/components/activities/background-starts).
-6. We suggest you to create notification channel inside your application class. After Android O, it is necessary to register notification channel to allow notifications of your apps.
+ if (!CometChatUIKit.isSDKInitialized()) {
+ SplashViewModel().initUIKit(this)
+ }
-
-
-```java
-private void createNotificationChannel() {
- // Create the NotificationChannel, but only on API 26+ because
- // the NotificationChannel class is new and not in the support library
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- CharSequence name = getString(R.string.app_name);
- String description = getString(R.string.channel_description);
- int importance = NotificationManager.IMPORTANCE_HIGH;
- NotificationChannel channel = new NotificationChannel("2", name, importance);
- channel.setDescription(description);
- channel.enableVibration(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
- // Register the channel with the system; you can't change the importance
- // or other notification behaviors after this
- NotificationManager notificationManager = getSystemService(NotificationManager.class);
- notificationManager.createNotificationChannel(channel);
- }
+ FirebaseApp.initializeApp(this)
+ addCallListener()
+ registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { currentActivity = activity }
+ override fun onActivityStarted(activity: Activity) {
+ if (activity !is SplashActivity &&
+ CometChatUIKit.isSDKInitialized() &&
+ isConnectedToWebSockets.compareAndSet(false, true)
+ ) {
+ CometChat.connect(object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String?) { isConnectedToWebSockets.set(true) }
+ override fun onError(e: CometChatException) { isConnectedToWebSockets.set(false) }
+ })
+ }
+ currentActivity = activity
+ if (++activityReferences == 1 && !isActivityChangingConfigurations) {
+ isAppInForeground = true
+ }
+ }
+ override fun onActivityResumed(activity: Activity) { currentActivity = activity }
+ override fun onActivityPaused(activity: Activity) {}
+ override fun onActivityStopped(activity: Activity) {
+ if (activity !is SplashActivity) {
+ isActivityChangingConfigurations = activity.isChangingConfigurations
+ if (--activityReferences == 0 && !isActivityChangingConfigurations) {
+ isAppInForeground = false
+ if (CometChatUIKit.isSDKInitialized()) {
+ CometChat.disconnect(object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String?) { isConnectedToWebSockets.set(false) }
+ override fun onError(e: CometChatException) {}
+ })
+ }
+ }
+ }
+ }
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
+ override fun onActivityDestroyed(activity: Activity) { if (currentActivity === activity) currentActivity = null }
+ })
}
-```
-
-
-
+ private fun addCallListener() {
+ CometChat.addCallListener(LISTENER_ID, object : CometChat.CallListener() {
+ override fun onIncomingCallReceived(call: Call) { /* handle call UI or banner */ }
+ override fun onOutgoingCallAccepted(call: Call) {}
+ override fun onOutgoingCallRejected(call: Call) {}
+ override fun onIncomingCallCancelled(call: Call) {}
+ override fun onCallEndedMessageReceived(call: Call) {}
+ })
+ }
-* You also need to add both of the above-mentioned files in your AndroidManifest.xml to make Push notification work in the background as well.
+ companion object {
+ var currentOpenChatId: String? = null
+ var currentActivity: Activity? = null
+ private var isAppInForeground = false
+ private val isConnectedToWebSockets = AtomicBoolean(false)
+ private var activityReferences = 0
+ private var isActivityChangingConfigurations = false
+ private var LISTENER_ID: String = System.currentTimeMillis().toString()
+ private var tempCall: Call? = null
+
+ fun getTempCall(): Call? = tempCall
+ fun setTempCall(call: Call?) {
+ tempCall = call
+ if (call == null && soundManager != null) {
+ soundManager?.pauseSilently()
+ }
+ }
-
-
-```xml
-
-
-
-
-
-
+ fun isAppInForeground(): Boolean = isAppInForeground
+ var soundManager: CometChatSoundManager? = null
+ }
+}
```
-
+Initializes UIKit/Firebase, adds call listeners, manages websocket connect/disconnect tied to app foreground, tracks the current activity, and caches temp call state so banners can reappear after resume.
-
-
-## Advanced
+State to set at runtime:
-### Sending Custom Notification body
+- `isAppInForeground`/`currentActivity` inside lifecycle callbacks.
+- `currentOpenChatId` when a chat screen opens; clear on exit to suppress notifications only for the active chat.
+- `tempCall` via `setTempCall(...)` when an incoming call arrives; clear on dismiss/end. `getTempCall()` is read on resume to re-show the banner.
-Push notification has 2 parts, namely, the notification title and notification body. The title can be: a. Name of the sender in case of one-on-one message. (E.g.: Nancy Grace) b. Name of the sender followed by group name for group messages (E.g.: Nancy Grace @ Hiking Group)
+## 5. Application wiring and permissions
-The body of the message depends upon the type of message being sent. You can send a custom body by specifying the `pushNotification` key followed by some user-defined string for the notification body inside `metadata` while sending the message.
+- [`AppUtils.kt`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/utils/AppUtils.kt) + your entry screen (e.g., `HomeActivity`): request notification/mic/camera/storage permissions early.
+- In `HomeActivity`, keep the VoIP permission chain and phone-account enablement so call pushes can render the native UI:
-The following code shows an example of a Custom body using a message of category=custom. This is however not just limited to a custom category of messages.
-
-
-
-```java
-String receiverId="cometchat-uid-1";
-JSONObject metaData=new JSONObject();
-JSONObject customData=new JSONObject();
-
-try {
- metaData.put("pushNotification","Custom Notification body");
- customData.put("yourkey","Your Value");
- } catch (JSONException e) {
- e.printStackTrace();
+```kotlin lines
+override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ AppUtils.requestNotificationPermission(this)
+ configureVoIP()
+ handleDeepLinking() // open chats based on NOTIFICATION_TYPE/NOTIFICATION_PAYLOAD
}
-CustomMessage customMessage=new CustomMessage(receiverId,CometChatConstants.RECEIVER_TYPE_USER,customData);
-customMessage.setMetadata(metaData);
+private fun configureVoIP() {
+ CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString())
+ launchVoIP()
+}
-CometChat.sendCustomMessage(customMessage, new CometChat.CallbackListener() {
- @Override
- public void onSuccess(CustomMessage customMessage) {
- Log.d(TAG, "onSuccess: "+customMessage.toString());
- }
+private fun launchVoIP() {
+ if (!CometChatVoIP.hasReadPhoneStatePermission(this)) {
+ CometChatVoIP.requestReadPhoneStatePermission(this, CometChatVoIPConstant.PermissionCode.READ_PHONE_STATE)
+ return
+ }
+ if (!CometChatVoIP.hasManageOwnCallsPermission(this)) {
+ CometChatVoIP.requestManageOwnCallsPermission(this, CometChatVoIPConstant.PermissionCode.MANAGE_OWN_CALLS)
+ return
+ }
+ if (!CometChatVoIP.hasAnswerPhoneCallsPermission(this)) {
+ CometChatVoIP.requestAnswerPhoneCallsPermission(this, CometChatVoIPConstant.PermissionCode.ANSWER_PHONE_CALLS)
+ return
+ }
+ CometChatVoIP.hasEnabledPhoneAccountForVoIP(this, object : VoIPPermissionListener {
+ override fun onPermissionsGranted() { /* ready for call pushes */ }
+ override fun onPermissionsDenied(error: CometChatVoIPError?) {
+ CometChatVoIP.alertDialogForVoIP(this@HomeActivity)
+ }
+ })
+}
- @Override
- public void onError(CometChatException e) {
- Log.d(TAG, "onError: "+e.getMessage());
+override fun onRequestPermissionsResult(reqCode: Int, permissions: Array, results: IntArray) {
+ super.onRequestPermissionsResult(reqCode, permissions, results)
+ when (reqCode) {
+ AppUtils.PushNotificationPermissionCode -> if (granted(results)) {
+ CometChatVoIP.requestPhoneStatePermissions(this, CometChatVoIPConstant.PermissionCode.READ_PHONE_STATE)
+ }
+ CometChatVoIPConstant.PermissionCode.READ_PHONE_STATE -> if (granted(results)) {
+ if (CometChatVoIP.hasManageOwnCallsPermission(this)) {
+ CometChatVoIP.requestAnswerPhoneCallsPermissions(this, CometChatVoIPConstant.PermissionCode.ANSWER_PHONE_CALLS)
+ } else {
+ CometChatVoIP.requestManageOwnCallsPermissions(this, CometChatVoIPConstant.PermissionCode.MANAGE_OWN_CALLS)
}
- });
-```
-
-
-
-
-```kotlin
-var receiverId:String="cometchat-uid-1"
-var metaData:JSONObject=JSONObject()
-var customData:JSONObject= JSONObject()
-
- try {
- metaData.put("pushNotification","Custom Notification Body")
- customData.put("yourkey","Your Value")
- } catch (e:JSONException) {
- e.printStackTrace()
- }
-var customMessage = CustomMessage(receiverId,CometChatConstants.RECEIVER_TYPE_USER,customData)
-customMessage.metadata = metaData;
-
-CometChat.sendCustomMessage(customMessage, object :CometChat.CallbackListener() {
- override fun onSuccess(p0: CustomMessage?) {
- Log.d(TAG,"onSuccess ${p0?.toString()}")
}
+ CometChatVoIPConstant.PermissionCode.MANAGE_OWN_CALLS -> if (granted(results)) {
+ CometChatVoIP.requestAnswerPhoneCallsPermissions(this, CometChatVoIPConstant.PermissionCode.ANSWER_PHONE_CALLS)
+ }
+ CometChatVoIPConstant.PermissionCode.ANSWER_PHONE_CALLS -> if (granted(results)) {
+ launchVoIP()
+ }
+ }
+}
- override fun onError(p0: CometChatException?) {
- Log.d(TAG,"onError ${p0?.message}")
- }
-})
+private fun granted(results: IntArray) =
+ results.isNotEmpty() && results[0] == PackageManager.PERMISSION_GRANTED
+
+// Deep link from notification payload to Chats
+private fun handleDeepLinking() {
+ val type = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_TYPE)
+ val payload = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_PAYLOAD) ?: return
+ if (type == AppConstants.FCMConstants.NOTIFICATION_TYPE_MESSAGE) {
+ val fcmMessageDTO = Gson().fromJson(payload, FCMMessageDTO::class.java)
+ // Set currentOpenChatId to suppress notifications for the open chat
+ MyApplication.currentOpenChatId = if (fcmMessageDTO.receiverType == "user") {
+ fcmMessageDTO.sender
+ } else fcmMessageDTO.receiver
+ }
+}
```
-
-
-
-
-### Converting Push Notification Payloads to Message Objects
+Requests notification + telecom permissions in sequence, initializes the VoIP phone account, and maps notification payload extras to set `currentOpenChatId` so you don’t alert for the chat currently open.
-CometChat provides a method `CometChatHelper.processMessage()` to convert the message JSON to the corresponding object of `TextMessage`, `MediaMessage`, `CustomMessage`, `Action` or `Call`.
+## 6. Register the FCM token after login
-This code needs to be added to the `onMessageReceived()` method of the `FirebaseMessagingService` class.
+Call registration right after `CometChatUIKit.login()` succeeds:
-
-
-```java
-CometChatHelper.processMessage(new JSONObject(remoteMessage.getData().get("message"));
+```kotlin lines
+FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
+ if (task.isSuccessful) {
+ val token = task.result
+ CometChatNotifications.registerPushToken(
+ token,
+ PushPlatforms.FCM_ANDROID,
+ AppConstants.FCMConstants.PROVIDER_ID,
+ object : CometChat.CallbackListener() {
+ override fun onSuccess(uid: String?) { /* token registered */ }
+ override fun onError(e: CometChatException) { /* handle failure */ }
+ }
+ )
+ }
+}
```
-
+Registers the current device token with CometChat under your Provider ID after login so the backend can target this user via FCM; retry on failure and rerun when the token rotates.
-
+Re-register on token refresh. Keep the provider ID aligned to the FCM provider you created for this app.
-
+Handle FCM refresh tokens too:
-Type of Attachment can be of the following the type\
-`CometChatConstants.MESSAGE_TYPE_IMAGE`\
-`CometChatConstants.MESSAGE_TYPE_VIDEO`\
-`CometChatConstants.MESSAGE_TYPE_AUDIO`\
-`CometChatConstants.MESSAGE_TYPE_FILE`
+```kotlin lines
+// In FCMService
+override fun onNewToken(token: String) {
+ super.onNewToken(token)
+ // Re-register with CometChat using your provider ID
+ CometChatNotifications.registerPushToken(
+ token,
+ PushPlatforms.FCM_ANDROID,
+ AppConstants.FCMConstants.PROVIDER_ID,
+ object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String?) { /* token registered */ }
+ override fun onError(e: CometChatException) { /* handle failure */ }
+ }
+ )
+}
+```
-
+Ensures a rotated FCM token is re-bound to the logged-in user; without this, pushes will stop after Firebase refreshes the token.
-Push Notification Payload sample for text and media messages-
+## 7. Unregister the token on logout
-
-
-```json
-{
- "alert": "Nancy Grace: Text Message",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
- }
- },
- "text": "Text Message"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "142",
- "sentAt": 1555668711,
- "category": "message",
- "type": "text"
- }
-}
+```kotlin lines
+CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String?) { /* success */ }
+ override fun onError(e: CometChatException) { /* handle error */ }
+})
+// Then call CometChatUIKit.logout()
```
-
+{/* ## 8. What arrives in the push payload
+
+Payload keys delivered to `onMessageReceived` (adapted from the shared push integration):
-
-```json
+```json lines
{
- "alert": "Nancy Grace: has sent an image",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "attachments": [
- {
- "extension": "png",
- "size": 14327,
- "name": "extension_leftpanel.png",
- "mimeType": "image_png",
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- }
- ],
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
- }
- },
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "145",
- "sentAt": 1555671238,
- "category": "message",
- "type": "image"
- }
+ "title": "Andrew Joseph",
+ "body": "Hello!",
+ "sender": "cometchat-uid-1",
+ "senderName": "Andrew Joseph",
+ "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
+ "receiver": "cometchat-uid-2",
+ "receiverName": "George Alan",
+ "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp",
+ "receiverType": "user",
+ "tag": "123",
+ "conversationId": "cometchat-uid-1_user_cometchat-uid-2",
+ "type": "chat", // or "call"
+ "callAction": "initiated", // "initiated" | "cancelled" | "unanswered" | "ongoing" | "rejected" | "ended" | "busy"
+ "sessionId": "v1.123.aik2",
+ "callType": "audio",
+ "sentAt": "1741847453000",
+ "message": { }, // CometChat Message Object if included
+ "custom": { } // Custom JSON if configured
}
```
-
+Use `message` for deep links (`CometChatHelper.processMessage`) and `type/callAction` to branch chat vs call flows. */}
-
+## 8. Handle message pushes
+
+- [`FCMService.onMessageReceived`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/fcm/FCMService.kt) checks `message.data["type"]`.
+- For `type == "chat"`: mark delivered (`CometChat.markAsDelivered`), skip notifying if the chat is already open (`MyApplication.currentOpenChatId`), and build grouped notifications (avatars + BigText) via [`FCMMessageNotificationUtils`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/fcm/FCMMessageNotificationUtils.kt) with inline reply actions.
+- [`FCMMessageBroadcastReceiver`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/fcm/FCMMessageBroadcastReceiver.kt) handles inline replies, initializes the SDK headlessly, sends the reply, and refreshes the notification.
+- In your messaging service (e.g., `FCMService`), set the notification tap intent to your splash/entry activity (e.g., `SplashActivity`), and keep the `currentOpenChatId` check to suppress notifications for the open chat.
-### Handle Push Notification Actions.
+## 9. Handle call pushes (ConnectionService)
-
-
-
+- For `type == "call"`, `FCMService.handleCallFlow` parses [`FCMCallDto`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/fcm/FCMCallDto.kt) and routes to the `voip` package.
+- [`CometChatVoIP`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/voip/CometChatVoIP.kt) registers a `PhoneAccount` and triggers `TelecomManager.addNewIncomingCall` for native full-screen UI with Accept/Decline.
+- Busy logic: if already on a call, reject with busy (`Repository.rejectCallWithBusyStatus`). Cancel/timeout pushes end the active telecom call when IDs match.
+- Runtime VoIP checks: before handling call pushes, request `READ_PHONE_STATE`, `MANAGE_OWN_CALLS`, and `ANSWER_PHONE_CALLS` at runtime and ensure the phone account is enabled (`CometChatVoIP.hasEnabledPhoneAccountForVoIP`).
+- Foreground suppression: the sample ignores VoIP banners if `MyApplication.isAppInForeground()` is true; keep or remove based on your UX.
+- Cancel/unanswered handling: on `callAction` of `cancelled`/`unanswered`, end the active telecom call if the session IDs match.
-**Step 1. Process push notification payload and grab BaseMessage object**
+## 10. Customize notification text or parse payloads
-To open a chat view, firstly you will need a BaseMessage object. You can grab this from the push notification payload received in `onMessageReceived(RemoteMessage message)`. You need to call `CometChat.processMessage()` method to process push notification payload.
+Parse the push into a `BaseMessage` for deep links:
-
-
-```java
-@Override
-public void onMessageReceived(RemoteMessage remoteMessage) {
- try {
- JSONObject messageData = new JSONObject(remoteMessage.getData().get("message"));
- BaseMessage baseMessage = CometChatHelper.processMessage(messageData);
-
- //Process BaseMessage and show Notification
- } catch (JSONException e) {
- e.printStackTrace();
- }
+```kotlin
+override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ val messageJson = remoteMessage.data["message"] ?: return
+ val baseMessage = CometChatHelper.processMessage(JSONObject(messageJson))
+ // open the right chat/thread using baseMessage
}
```
-
-
-
+Parses the CometChat message JSON shipped in the payload into a `BaseMessage` so you can navigate to the right conversation/thread without extra API calls.
-**Step 2 . Handle Notification Actions**
+Override the push body before sending:
-You can launch the chat view after you tap on the Message Notification by creating PendingIntent and set it with NotificationBuilder object.
-
-
-
-CometChatMessageListActivity is part of UI Kit Library. You can replace CometChatMessageListActivity with your required class.
+```kotlin
+val meta = JSONObject().put("pushNotification", "Custom notification body")
+customMessage.metadata = meta
+CometChat.sendCustomMessage(customMessage, object : CallbackListener() {})
+```
-
+Adds a `pushNotification` field in metadata so CometChat uses your custom text as the push body for that message.
+## 11. Navigation from notifications
+Notification taps launch [`SplashActivity`](https://github.com/cometchat/cometchat-uikit-android/blob/v5/sample-app-kotlin%2Bpush-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/ui/activity/SplashActivity.kt); it reads `NOTIFICATION_PAYLOAD` extras and opens the correct user or group in `MessagesActivity`. Keep `launchMode` settings that allow the intent extras to arrive.
+## 12. Testing checklist
+
+1. Install on a physical device and grant notification + mic permissions (Android 13+ needs `POST_NOTIFICATIONS`).
+2. Log in and ensure token registration succeeds (check Logcat).
+3. Send a message from another user:
+ - Foreground: grouped notification shows unless you are already in that chat.
+ - Background/terminated: tap opens the correct conversation.
+4. Inline reply from the shade delivers the message and updates the notification.
+5. Trigger an incoming call push:
+ - Native full-screen call UI appears with caller info.
+ - Accept/Decline work; cancel/timeout dismisses the telecom call.
+6. Reinstall or clear app data to confirm token re-registration works.
+
+## Troubleshooting
+
+| Symptom | Quick checks |
+| --- | --- |
+| No notifications | Package name matches Firebase app, `google-services.json` is present, notification permission granted, Provider ID correct, Push Notifications enabled. |
+| Token registration fails | Run registration after login, confirm `AppConstants.FCMConstants.PROVIDER_ID`, and verify the Firebase project matches the app ID. |
+| Notification tap does nothing | Ensure `SplashActivity` reads `NOTIFICATION_PAYLOAD` and activity launch modes do not drop extras. |
+| Call UI never shows | All telecom permissions declared + granted; `CometChatVoIPConnectionService` in manifest; device supports `MANAGE_OWN_CALLS`. |
+| Inline reply crashes | Keep `FCMMessageBroadcastReceiver` registered; do not strip FCM or `RemoteInput` classes in ProGuard/R8. |
diff --git a/notifications/constraints-and-limits.mdx b/notifications/constraints-and-limits.mdx
index 200b2547..603ce754 100644
--- a/notifications/constraints-and-limits.mdx
+++ b/notifications/constraints-and-limits.mdx
@@ -2,14 +2,16 @@
title: "Constraints And Limits"
---
-## Constraints
+Use this page to confirm supported SDK/UI Kit versions, platform/browser coverage, hard limits, and known notification caveats before you ship.
-To implement Notification features, the app must implement certain versions of UI Kits and SDKs. (Note: If you download a UI Kit, it has a corresponding Chat and Call SDK integrated into the same and does not have to be separately downloaded).
+## What you need (SDKs/UI Kits)
-* All UI Kits above version 4 are supported. (For Flutter, `Calls UI Kit v4.3.0 & above` are supported)
-* All Calls SDK versions are supported. (For Flutter, `Calls SDK v4.0.9 & above` are supported)
-* Notifications are supported in iOS Chat SDK v4.0.51 and above, Android Chat SDK v4.0.9 and above, Flutter Chat SDK v4.0.15 and above, React Native Chat SDK v4.0.10 and above, JavaScript Chat SDK v4.0.8 and above and Ionic (Cordova) SDK v4.0.8 and above.
-* Chat Widgets are not compatible with Push Notifications.
+To implement Notifications, use the minimum versions below (UI Kits already bundle the matching Chat and Call SDKs):
+
+- **UI Kits**: v4+ (Flutter Calls UI Kit v4.3.0+)
+- **Calls SDK**: all versions (Flutter Calls SDK v4.0.9+)
+- **Chat SDK**: iOS v4.0.51+, Android v4.0.9+, Flutter v4.0.15+, React Native v4.0.10+, JavaScript v4.0.8+, Ionic (Cordova) v4.0.8+
+- Chat Widgets are not compatible with push notifications.
@@ -17,25 +19,25 @@ It is possible to use Notification features without using UI Kits with an entire
-### Supported Web Browsers & Versions
+## Supported web browsers
-* Chrome version 50 and above.
-* Firefox version 44 and above.
-* Edge version 17 and above.
-* Opera version 42 and above.
+- Chrome 50+
+- Firefox 44+
+- Edge 17+
+- Opera 42+
-### Supported Platforms & Versions
+## Supported platforms
-* iOS: v11 and above.
-* Android: v21 and above.
-* Flutter: v2.17 and above.
-* React Native: v0.73 and previous versions.
-* React: v18 and previous versions.
-* Angular: v17 and previous versions.
-* Vue: v3 and previous versions.
-* Capacitor: v6 and previous versions.
+- iOS v11+
+- Android v21+
+- Flutter v2.17+
+- React Native v0.73 and earlier
+- React v18 and earlier
+- Angular v17 and earlier
+- Vue v3 and earlier
+- Capacitor v6 and earlier
-## Limits
+## Limits (delivery and content)
| Entity / Parameter | Limit |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
@@ -47,12 +49,10 @@ It is possible to use Notification features without using UI Kits with an entire
| Minimum interval between two emails (in minutes) | 1 minute. |
| Minimum interval between two SMS (in minutes) | 1 minute. |
-## Limitations
+## Platform-specific limitations
-* iOS platform:
- * Calling notifications work when using APNS (VoIP). Calling notifications are not supported to work with FCM. (Applicable for native iOS, React Native iOS, Flutter iOS)
-* Android platform:
- * Push notifications using FCM work on devices that have Google Mobile Services. Push notifications don’t work on Huawei phones.
-* Flutter (Android) with FCM:
- * When the app is in the foreground state, notifications for edited and deleted messages don’t work. The original text is displayed in the notification even though the corresponding message has been edited/deleted.
-* Browser notifications are not supported on mobile devices.
+- **iOS**: Calling notifications work only with APNs (VoIP). FCM calling notifications are not supported (native iOS, React Native iOS, Flutter iOS).
+- **Android**: FCM push works on devices with Google Mobile Services; it does not work on Huawei devices.
+- **Flutter (Android) with FCM**: In the foreground, edited/deleted message notifications may still show the original text.
+- **Browser notifications** are not supported on mobile devices.
+- **Calling Notifications** are not supported in web browsers.
\ No newline at end of file
diff --git a/notifications/custom-providers.mdx b/notifications/custom-providers.mdx
new file mode 100644
index 00000000..36ce0ee5
--- /dev/null
+++ b/notifications/custom-providers.mdx
@@ -0,0 +1,313 @@
+---
+title: "Custom Providers"
+---
+
+Custom providers let you route push payloads to your own webhook instead of FCM/APNs. Use this page to set up credentials, understand payload shapes, and wire a simple receiver.
+
+**When to use a custom provider**
+- You already have a push gateway (or a third-party) and want CometChat to hand off payloads.
+- You want to enrich or filter notification payloads before delivery.
+- You need to fan out to multiple channels from one webhook.
+
+**How delivery works**
+- CometChat POSTs one payload per recipient: once for one-on-one, once per member in a group.
+- Your webhook must return `200 OK` within ~2 seconds; you handle the actual push delivery.
+- Keep payloads small; respect any rate limits on your infrastructure.
+
+### Add custom provider credentials
+
+Custom providers use a webhook URL that receives everything needed to trigger push notifications through your own system.
+
+#### Prerequisites
+
+1. An `https://` webhook that is publicly reachable and accepts `POST` JSON.
+2. Respond with `200 OK` within 2 seconds; do any heavy work asynchronously.
+3. Secure the endpoint (recommended): Basic Auth or a verified signature. With Basic Auth, send `Authorization: Basic `.
+4. Your client should register push tokens with your backend on login and unregister on logout.
+5. For multi-device, map push tokens to the user's auth tokens so each session can be targeted.
+
+#### Add credentials
+
+1. Click on the "+ Add Credentials" button.
+2. Enable the provider.
+3. Enter the publicly accessible webhook URL.
+4. (Recommended) Enable Basic Authentication.
+5. Enter the username and password.
+6. Save the credentials.
+
+
+
+
+
+#### How does it work?
+
+For one-on-one events, the custom provider fires once per recipient. For group events, it fires once per member so you receive one HTTP request per user who should be notified.
+
+Example: if a group has 100 members, your webhook receives 100 POSTs—one for each member.
+
+#### Payload reference
+
+Every request includes `trigger: "push-notification-payload-generated"` and `webhook: "custom"`. The notification content lives in `data.notificationDetails`; use that to decide how you deliver the push.
+
+Below are sample payloads for different events:
+
+
+
+```json
+{
+ "trigger": "push-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-2"
+ },
+ "notificationDetails": {
+ // Notification details
+ "title": "Andrew Joseph", // The title of the notification to be displayed
+ "body": "Hello!", // The body of the notification to be displayed
+
+ // Sender's details
+ "sender": "cometchat-uid-1", // UID of the user who sent the message.
+ "senderName": "Andrew Joseph", // Name of the user who sent the message.
+ "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp", // Avatar URL of the user.
+
+ // Receiver's details
+ "receiver": "cometchat-uid-2", // UID or GUID of the receiver.
+ "receiverName": "George Alan", // Name of the user or group.
+ "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp", // Avatar URL of the receiver.
+ "receiverType": "user", // Values can be "user" or "group"
+
+ // Message details
+ "tag": "123", // The ID of the message that can be used as the ID of the notification to be displayed.
+ "conversationId": "cometchat-uid-1_user_cometchat-uid-2", // The ID of the conversation that the message belongs to.
+ "type": "chat",
+ "sentAt": "1741847453000",
+ "message": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited or deleted.
+ "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
+ }
+ },
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+```json
+{
+ "trigger": "push-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-2"
+ },
+ "notificationDetails": {
+ // Notification details
+ "title": "Caller", // The title of the notification to be displayed
+ "body": "AUDIO CALL", // "AUDIO CALL" or "VIDEO CALL"
+
+ // Sender's details
+ "sender": "cometchat-uid-1", // UID of the user who sent the message.
+ "senderName": "Andrew Joseph", // Name of the user who sent the message.
+ "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp", // Avatar URL of the user.
+
+ // Receiver's details
+ "receiver": "cometchat-uid-2", // UID or GUID of the receiver.
+ "receiverName": "George Alan", // Name of the user or group.
+ "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp", // Avatar URL of the receiver.
+ "receiverType": "user", // "user" or "group"
+
+ // Message details
+ "tag": "123", // The ID of the message that can be used as the ID of the notification to be displayed.
+ "conversationId": "cometchat-uid-1_user_cometchat-uid-2", // The ID of the conversation that the call belongs to.
+ "type": "call",
+
+ // Call details
+ "callAction": "initiated", // "initiated" or "cancelled" or "unanswered" or "ongoing" or "rejected" or "ended" or "busy"
+ "sessionId": "v1.123.aik2", // The unique sessionId of the call that can be used as an identifier in CallKit or ConnectionService.
+ "callType": "audio", // "audio" or "video"
+ "sentAt": "1741847453000",
+ "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
+ }
+ },
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+```json
+{
+ "trigger": "push-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-2"
+ },
+ "notificationDetails": {
+ // Notification details
+ "title": "Andrew Joseph",
+ "body": "Reacted to your message: 😎",
+
+ // Sender's details
+ "sender": "cometchat-uid-1",
+ "senderName": "Andrew Joseph",
+ "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
+
+ // Receiver's details
+ "receiver": "cometchat-uid-1",
+ "receiverName": "Andrew Joseph",
+ "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
+ "receiverType": "user",
+
+ // Message details
+ "tag": "58",
+ "conversationId": "cometchat-uid-1_user_cometchat-uid-2",
+ "type": "chat",
+ "sentAt": "1741847453000",
+ "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
+ }
+ },
+ "appId": "app123",
+ "region": "us",
+ "webhook": "custom"
+}
+```
+
+
+
+
+```json
+{
+ "trigger": "push-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "g-messages-none"
+ },
+ "notificationDetails": {
+ // Notification details
+ "title": "Hiking group",
+ "body": "Andrew Joseph has left", // Similarly for joined, kicked, banned, unbanned, added events
+
+ // Sender details
+ "sender": "cometchat-uid-1",
+ "senderName": "Andrew Joseph",
+ "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
+
+ // Receiver details
+ "receiver": "cometchat-guid-1",
+ "receiverName": "Hiking group",
+ "receiverType": "group",
+
+ // Message details
+ "tag": "cometchat-guid-1",
+ "conversationId": "group_cometchat-guid-1",
+ "type": "chat",
+ "sentAt": "1741847453000",
+ "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
+ }
+ },
+ "appId": "app123",
+ "region": "us",
+ "webhook": "custom"
+}
+```
+
+
+
+
+
+#### Sample server-side code
+
+Example Express webhook that checks Basic Auth, validates the trigger, and forwards the payload to your push gateway.
+
+```js
+const express = require('express');
+const app = express();
+const PORT = process.env.PORT || 3000;
+
+app.use(express.json());
+
+// Optional: Basic authentication middleware
+const basicAuth = (req, res, next) => {
+ const expected =
+ 'Basic ' +
+ Buffer.from(
+ `${process.env.WEBHOOK_USER}:${process.env.WEBHOOK_PASS}`
+ ).toString('base64');
+
+ if (expected && req.headers.authorization !== expected) {
+ return res.status(401).json({ message: 'Unauthorized' });
+ }
+ next();
+};
+
+// Look up the push token for the recipient in your store.
+const fetchPushToken = async (uid) => {
+ // TODO: Implement fetch logic.
+ return 'device-push-token';
+};
+
+// Send to APNs/FCM/other provider with the payload you received.
+const sendPushNotification = async (token, title, body, notificationDetails) => {
+ // TODO: Implement delivery.
+};
+
+const triggerPushNotification = async (to, notificationDetails) => {
+ const { uid } = to || {};
+ const { type, title, body, callAction, sessionId, callType } =
+ notificationDetails || {};
+
+ if (!uid) {
+ throw new Error('Missing recipient UID');
+ }
+
+ if (type === 'call') {
+ console.log('Push notification for calling event', {
+ callAction,
+ sessionId,
+ callType,
+ });
+ }
+
+ if (type === 'chat') {
+ console.log('Push notification for messaging event');
+ }
+
+ const token = await fetchPushToken(uid);
+ await sendPushNotification(token, title, body, notificationDetails);
+};
+
+app.post('/webhook', basicAuth, (req, res) => {
+ const { trigger, data, appId, webhook } = req.body || {};
+ const { to, notificationDetails } = data || {};
+
+ if (
+ trigger !== 'push-notification-payload-generated' ||
+ webhook !== 'custom' ||
+ !to ||
+ !notificationDetails
+ ) {
+ return res.status(400).json({ message: 'Invalid trigger or webhook type' });
+ }
+
+ console.log('Received webhook', { appId, trigger, webhook, to: to.uid });
+
+ triggerPushNotification(to, notificationDetails).catch((error) => {
+ console.error(
+ 'Error while triggering push notification for',
+ appId,
+ to.uid,
+ error.message
+ );
+ });
+
+ res.status(200).json({ message: 'Webhook received successfully' });
+});
+
+app.listen(PORT, () => {
+ console.log(`Server is running on port ${PORT}`);
+});
+```
diff --git a/notifications/email-custom-providers.mdx b/notifications/email-custom-providers.mdx
new file mode 100644
index 00000000..76beb699
--- /dev/null
+++ b/notifications/email-custom-providers.mdx
@@ -0,0 +1,205 @@
+---
+title: "Custom Providers"
+---
+
+Use a custom provider to send CometChat email notifications through any gateway via webhook. SendGrid is built-in; choose custom when you want another service or your own SMTP bridge.
+
+## Prerequisites
+
+1. Public `https://` webhook that accepts `POST` JSON.
+2. Return `200 OK` within ~2 seconds (do heavy work async).
+3. Secure the endpoint (recommended): Basic Auth or a verified signature. With Basic Auth, the request includes:
+
+```html
+Authorization: Basic
+```
+
+## Add credentials
+
+1. Click on the "+ Add Credentials" button.
+2. Enable the provider.
+3. Enter the publicly accessible webhook URL.
+4. (Recommended) Enable Basic Authentication and set username/password.
+5. Decide if you want to **Trigger only if email address is stored with CometChat** (via [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details)); when off, the webhook fires regardless.
+6. Save the credentials.
+
+
+
+
+
+## How delivery works
+
+For one-on-one events, CometChat calls your webhook once per recipient. For group events, it calls once per member—one HTTP request per user to notify.
+
+Example: if a group has 100 members, your webhook receives 100 POSTs.
+
+
+
+```json
+{
+ "trigger": "email-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-1",
+ "email": "andrew-joseph@example.com", // Optional
+ "name": "Andrew Joseph"
+ },
+ "messages": [
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "Are we meeting on this weekend?",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ },
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "📷 Has shared an image",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ }
+ ],
+ "senderDetails": {
+ "uid": "cometchat-uid-4",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
+ },
+ "subject": "New messages from Susan Marie"
+ },
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+```json
+{
+ "trigger": "email-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-1",
+ "email": "andrew-joseph@example.com", // Optional
+ "name": "Andrew Joseph"
+ },
+ "messages": [
+ {
+ "sender": {
+ "uid": "cometchat-uid-5",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-5.webp",
+ "name": "John Paul"
+ },
+ "message": "Hello all! What's up?",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ },
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "This is the place I was thinking about",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ }
+ ],
+ "groupDetails": {
+ "guid": "cometchat-guid-1",
+ "name": "Hiking Group",
+ "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
+ },
+ "subject": "New messages in Hiking Group"
+},
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+
+#### Sample server-side code
+
+```javascript
+const express = require('express');
+const app = express();
+const PORT = process.env.PORT || 3000;
+
+app.use(express.json());
+
+// Optional: Basic authentication middleware
+const basicAuth = (req, res, next) => {
+ const authHeader = req.headers['authorization'];
+ if (!authHeader || !authHeader.startsWith('Basic ')) {
+ return res.status(401).json({ message: 'Unauthorized' });
+ }
+ next();
+};
+
+const triggerEmailNotification = async (to, data) => {
+ let { name, uid, email } = to;
+ let { groupDetails, senderDetails, subject } = data;
+
+ if (groupDetails) {
+ console.log('Received webhook for group email notification');
+ }
+
+ if (senderDetails) {
+ console.log('Received webhook for one-on-one email notification');
+ }
+
+ if (email == null) {
+ // Your implementation to fetch Email ID
+ email = await fetchEmailIDFor(uid);
+ }
+
+ // Your implementation for sending the email notification
+ await sendEmail(email, subject, data.messages);
+};
+
+app.post('/webhook', basicAuth, (req, res) => {
+ const { trigger, data, appId, region, webhook } = req.body || {};
+ const { to } = data || {};
+
+ if (
+ trigger !== 'email-notification-payload-generated' ||
+ webhook !== 'custom'
+ ) {
+ return res.status(400).json({ message: 'Invalid trigger or webhook type' });
+ }
+
+ console.log('Received Webhook:', JSON.stringify(req.body, null, 2));
+
+ triggerEmailNotification(to, data)
+ .then((result) => {
+ console.log(
+ 'Successfully triggered email notification for',
+ appId,
+ to.uid,
+ result
+ );
+ })
+ .catch((error) => {
+ console.error(
+ 'Something went wrong while triggering email notification for',
+ appId,
+ to.uid,
+ error.message
+ );
+ });
+
+ res.status(200).json({ message: 'Webhook received successfully' });
+});
+
+app.listen(PORT, () => {
+ console.log(`Server is running on port ${PORT}`);
+});
+```
diff --git a/notifications/email-customization.mdx b/notifications/email-customization.mdx
deleted file mode 100644
index fac1ed46..00000000
--- a/notifications/email-customization.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: "Customizations"
----
-
-Customizations allow controlling notifications for message events, group events, and call events. Users can set preferences for incoming notifications based on notification schedules, Do Not Disturb (DND) mode, and the mute status of specific conversations.
-
-Additional customizations include modifications to notification templates and sounds. These options also ensure that user privacy is maintained while displaying notifications on the device.
-
-For more information, refer to [Preferences, Templates & Sounds](/notifications/preferences-templates-sounds) documentation.
diff --git a/notifications/email-integration.mdx b/notifications/email-integration.mdx
index 5666c65f..16e4f444 100644
--- a/notifications/email-integration.mdx
+++ b/notifications/email-integration.mdx
@@ -2,13 +2,18 @@
title: "Integration"
---
-Email notifications integration is possible using SendGrid as a provider or a custom provider. Custom provider allows integration with providers other than SendGrid.
+Connect CometChat email notifications to SendGrid or your own provider, wire the required webhooks, and keep timezones in sync for scheduling.
-## SendGrid
+## Provider options
+
+- **SendGrid (recommended)**: Native integration with templates, unsubscribe groups, and replies.
+- **Custom provider**: Bring your own email gateway/webhook. See [Custom Provider](/notifications/email-custom-providers) for payload and endpoint details.
+
+## SendGrid setup
We have partnered with SendGrid for sending Email Notifications and hence you need to set up an account on [SendGrid](https://www.sendgrid.com/) before you start using the extension.
-### Get your SendGrid API Key
+### 1. Get your SendGrid API key
1. Log in to your SendGrid account.
2. In the left navigation pane, go to Settings and select API Keys.
@@ -16,14 +21,14 @@ We have partnered with SendGrid for sending Email Notifications and hence you ne
4. Give a name to your API Key and select Full Access to get started.
5. Make a note of the **API key** for later use.
-### Create an email template
+### 2. Create an email template
1. Log in to your SendGrid account.
2. In the left navigation pane, go to Email API and select Dynamic Templates.
-3. Click on "Create a Dynamic Template" and give a name to your template.
-4. In the Template listing, expand your template and click on "Add Version".
-5. Under the "Your Email Designs" tab, select Blank Template.
-6. As we have the following HTML template ready for you, select the "Code Editor" option.
+3. Click on **"Create a Dynamic Template"** and give a name to your template.
+4. In the Template listing, expand your template and click on **"Add Version"**.
+5. Under the **"Your Email Designs"** tab, select Blank Template.
+6. As we have the following HTML template ready for you, select the **"Code Editor"** option.
7. Paste the code for the email template. You should be able to see the Preview in the Right pane.
8. Click on Settings on the Left to expand the Settings drawer.
9. Enter the Version name and the value for Subject as `{{subject}}` and hit "Save".
@@ -33,7 +38,7 @@ We have partnered with SendGrid for sending Email Notifications and hence you ne
-```json
+```json lines
{
"to": {
"uid": "cometchat-uid-1",
@@ -72,7 +77,7 @@ We have partnered with SendGrid for sending Email Notifications and hence you ne
-```json
+```json lines
{
"to": {
"uid": "cometchat-uid-1",
@@ -112,9 +117,9 @@ We have partnered with SendGrid for sending Email Notifications and hence you ne
-13. The email template recommended by us is as follows. Replace "[https://www.YOURSITE.com](https://www.YOURSITE.com)" with your website's URL in the below template.
+13. (Optional) Use our starter template below. Replace `https://www.YOURSITE.com` with your site URL.
-```html
+```html lines highlight={185}
@@ -349,29 +354,29 @@ We have partnered with SendGrid for sending Email Notifications and hence you ne
```
-### Add an unsubscribe group
+### 3. Add an unsubscribe group
An unsubscribe group will allow your users to unsubscribe to only chat email notifications and will allow you to continue to send other emails to that user via SendGrid.
1. In the left pane, go to Suppressions and select Unsubscribe Groups.
-2. Click on "Create New Group" and give it a name and proper description.
+2. Click on **"Create New Group"** and give it a name and proper description.
3. Save your new group and make a note of the **Unsubscribe Group ID** for later use.
-### Store contact details
+### 4. Store contact details
Store the Email IDs of your users by using our [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details).
-### Enable Email notifications
+### 5. Enable Email notifications
1. Login to [CometChat](https://app.cometchat.com/login) dashboard and select your app.
-2. Navigate to **Notifications** > **Notifications** in the left-hand menu.
+2. Navigate to **Notifications** > **Settings** in the left-hand menu.
3. Enable Email notifications feature.
-### Save the SendGrid credentials
+### 6. Save the SendGrid credentials
@@ -391,7 +396,7 @@ The domain used in Sender's Email needs to be Authenticated. Refer to SendGrid's
-### Save user's timezone
+### 7. Save user timezones
A user's timezone is required to allow them to set a schedule for receiving notifications. In case the timezone is not registered, the default timezone for
@@ -414,7 +419,7 @@ This functionality is available in the following SDK versions:
-### Receive notifications
+### 8. Receive notifications
@@ -422,7 +427,7 @@ This functionality is available in the following SDK versions:
Send a message to any user and keep the conversation unread for the designated amount of time to receive an email notification.
-### Configure email replies
+### 9. Configure email replies (optional)
In the SendGrid provider settings, enable the email replies. Optionally, you can set a different sender's email address. Only ensure that the it doesn't contain any "+" symbol in it.
@@ -435,212 +440,21 @@ Before saving the Inbound Host and URL:
Once this setup is successful, users will be able to reply to an email notification and send messages in a particular conversation on CometChat. The parsing of the replies is heavily dependent on the Email client used and the content of the reply.
-## Custom Email provider
-
-Custom provider allows you to make use of providers apart from SendGrid for triggering Email notifications. This is implemented using webhook URL which gets all the required details that can be used to trigger Email notifications.
-
-#### Pre-requisite
-
-1. Your webhook endpoint must be accessible over `HTTPS`. This is essential to ensure the security and integrity of data transmission.
-2. This URL should be publicly accessible from the internet.
-3. Ensure that your endpoint supports the `HTTP POST` method. Event payloads will be delivered via `HTTP POST` requests in `JSON` format.
-4. Configure your endpoint to respond immediately to the CometChat server with a 200 OK response. The response should be sent within 2 seconds of receiving the request.
-5. For security, it is recommended to set up Basic Authentication that is usually used for server-to-server calls. This requires you to configure a username and password. Whenever your webhook URL is triggered, the HTTP Header will contain:
-
-```html
-Authorization: Basic
-```
-
-#### Add credentials
-
-1. Click on the "+ Add Credentials" button.
-2. Enable the provider.
-3. Enter the publically accessible Webhook URL.
-4. It is recommended to enable Basic Authentication.
-5. Enter the username and password.
-6. Enabling the "Trigger only if email address is stored with CometChat" setting requires users' email addresses to be stored with CometChat using the [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details). When enabled, the webhook is triggered only for those users. If this setting is disabled, the webhook triggers regardless of whether users' email addresses are stored with CometChat.
-7. Save the credentials.
-
-
-
-
-
-#### How does it work?
-
-The Custom provider is triggered once for an event in one-on-one conversation. In case of notifying the members of a group, the custom provider is triggered once for each user present in that group.
-
-For example, if there are 100 members in the group, your webhook will receive 100 HTTP requests. Once for each member of the group.
-
-
-
-```json
-{
- "trigger": "email-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-1",
- "email": "andrew-joseph@example.com", // Optional
- "name": "Andrew Joseph"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Are we meeting on this weekend?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "📷 Has shared an image",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "senderDetails": {
- "uid": "cometchat-uid-4",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
- },
- "subject": "New messages from Susan Marie"
- },
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-```json
-{
- "trigger": "email-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-1",
- "email": "andrew-joseph@example.com", // Optional
- "name": "Andrew Joseph"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-5",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-5.webp",
- "name": "John Paul"
- },
- "message": "Hello all! What's up?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "This is the place I was thinking about",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "groupDetails": {
- "guid": "cometchat-guid-1",
- "name": "Hiking Group",
- "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
- },
- "subject": "New messages in Hiking Group"
-},
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-
-#### Sample server-side code
-
-```javascript
-const express = require('express');
-const app = express();
-const PORT = process.env.PORT || 3000;
-
-app.use(express.json());
-
-// Optional: Basic authentication middleware
-const basicAuth = (req, res, next) => {
- const authHeader = req.headers['authorization'];
- if (!authHeader || !authHeader.startsWith('Basic ')) {
- return res.status(401).json({ message: 'Unauthorized' });
- }
- next();
-};
-
-const triggerEmailNotification = async (to, data) => {
- let { name, uid, email } = to;
- let { groupDetails, senderDetails, subject } = data;
-
- if (groupDetails) {
- console.log('Received webhook for group email notification');
- }
-
- if (senderDetails) {
- console.log('Received webhook for one-on-one email notification');
- }
-
- if (email == null) {
- // Your implementation to fetch Email ID
- email = await fetchEmailIDFor(uid);
- }
-
- // Your implementation for sending the email notification
- await sendEmail(email, subject, data.messages);
-};
-
-app.post('/webhook', basicAuth, (req, res) => {
- const { trigger, data, appId, region, webhook } = req.body;
-
- if (
- trigger !== 'email-notification-payload-generated' ||
- webhook !== 'custom'
- ) {
- return res.status(400).json({ message: 'Invalid trigger or webhook type' });
- }
-
- console.log('Received Webhook:', JSON.stringify(req.body, null, 2));
-
- triggerEmailNotification(to, data)
- .then((result) => {
- console.log(
- 'Successfully triggered email notification for',
- appId,
- to.uid,
- result
- );
- })
- .catch((error) => {
- console.error(
- 'Something went wrong while triggering email notification for',
- appId,
- to.uid,
- error.message
- );
- });
-
- res.status(200).json({ message: 'Webhook received successfully' });
-});
-
-app.listen(PORT, () => {
- console.log(`Server is running on port ${PORT}`);
-});
-```
-
-## Next steps
-
-Have a look at the available [preferences](/notifications/preferences-templates-sounds#email-notification-preferences) and [templates](/notifications/preferences-templates-sounds#email-notification-templates) for email notifications.
+### Quick checklist
+
+- **Provider**: SendGrid API key, Template ID, Unsubscribe Group ID saved in dashboard.
+- **Sender**: Authenticated domain; sender name/email set.
+- **Users**: Contact email stored via Update Contact details API; timezones synced via `updateTimezone`.
+- **Templates**: Email templates configured (subject/body) and wait windows set in preferences.
+
+
+
+ Control when and how users receive email notifications.
+
+
+ Customize email notification content.
+
+
+ Integrate email providers other than SendGrid.
+
+
diff --git a/notifications/email-notification-extension.mdx b/notifications/email-notification-extension.mdx
index d070cf6c..d4212ff8 100644
--- a/notifications/email-notification-extension.mdx
+++ b/notifications/email-notification-extension.mdx
@@ -1,5 +1,5 @@
---
-title: "Email Notification Extension (Legacy)"
+title: "Email Notifications (Legacy)"
---
diff --git a/notifications/email-overview.mdx b/notifications/email-overview.mdx
index 3a9cc163..1b8bee9b 100644
--- a/notifications/email-overview.mdx
+++ b/notifications/email-overview.mdx
@@ -1,33 +1,24 @@
---
title: "Overview"
+description: "How CometChat sends unread-message fallback emails and what to configure."
---
-## Introduction
+## Why use CometChat email notifications
-Email notifications are useful as a re-engagement tool, prompting users to return to the app after an extended absence. These are useful for providing updates on messages that are unread while the user was away. The email alerts or notifications are dispatched at predetermined intervals and not in real time.
+Email acts as a safety net when users miss push: if a message stays unread past your wait window, CometChat can send a branded email that pulls them back to the conversation.
-## Key features
+## How it works
-1. **Notify users at intervals**:
+- **Trigger**: Only fires when a chat message remains unread beyond the wait window you configure.
+- **Templates**: Customize subject/body (sender name, snippet, deep link) per message type; manage in Templates & Sounds.
+- **Preferences/DND**: Honors user/channel mutes, global DND, and quiet hours before sending.
+- **Scheduling + timezones**: Respect quiet hours and deliver in the recipient’s local timezone.
+- **Preferences**: Honors per-user/per-conversation mutes before sending.
+- **Observability**: Use logs to verify sends and diagnose drops.
- Users who have unread messages can be notified at the specified intervals. The email includes these messages and are triggered for every such conversation.
+## Key capabilities
-2. **Contacts management**
-
- Once the emails are verified and vetted on your end, they can be shared with the notifications system using APIs.
-
-3. **Preferences management**:
-
- Through CometChat's Notification Preferences, users and admins have the ability to customize the notification settings, that help provide pertinent alerts while avoiding notification fatigue.
-
-4. **Ability to set up a schedule**:
-
- CometChat's notifications service ensures that the notifications are delivered based on the specified daily timetable, adhering to the user's local time zone.
-
-5. **Ability to mute notifications**:
-
- Users have the option to completely mute notifications for the app (DND mode), or selectively mute them for specific users and groups, for a designated duration.
-
-6. **Ability to set up Templates**:
-
- CometChat offers developers a set of pre-defined templates that define the content shown in notifications. These templates act as a blueprint for customizing the payload content sent with notifications as per the needs and requirements.
+- Unread-based digests/transactional emails for 1:1 and group chats.
+- Per-template customization with dynamic variables for sender, preview text, and links.
+- Mute/quiet hours enforcement to avoid fatigue.
+- Works alongside push/SMS as layered fallbacks.
diff --git a/notifications/email-preferences.mdx b/notifications/email-preferences.mdx
new file mode 100644
index 00000000..fdb39c9d
--- /dev/null
+++ b/notifications/email-preferences.mdx
@@ -0,0 +1,25 @@
+---
+title: "Email Preferences"
+---
+
+Control when CometChat sends unread-message emails and what data is included in each payload.
+
+
+
+
+
+## Delivery preferences
+
+- **Notify for unread messages only** (default: on). When enabled, send only if the conversation has unread messages.
+- **Wait time before sending the next notification (per conversation, minutes)**: default 120; min 1; max 1440.
+- **Maximum emails per day**: default 20.
+- **Maximum emails per conversation per day**: default 2.
+- **Override**: when enabled, end users can change these settings in clients that expose preferences.
+
+## Email payload options
+
+All toggles default to off; enable per your privacy/performance needs.
+- **Include entire message object in payload**
+- **Include message metadata in payload**
+- **Include sender's metadata in payload**
+- **Include receiver's metadata in payload**
diff --git a/notifications/email-templates.mdx b/notifications/email-templates.mdx
new file mode 100644
index 00000000..664d38bd
--- /dev/null
+++ b/notifications/email-templates.mdx
@@ -0,0 +1,109 @@
+---
+title: "Email Templates"
+---
+
+Design the subject/body your users see in unread-message emails. No sounds apply to email. Providers like SendGrid can consume these fields—map them into Dynamic Template variables or assemble the subject/body in your service before sending.
+
+## What to customize
+
+- **Subjects and bodies** for 1:1 and group emails (default vs privacy-friendly).
+- **Preview content** from `messages[]` (unread list) and `senderDetails`/`groupDetails`.
+- **Deep links** back to the conversation (add in your provider template).
+
+## Payload shapes
+
+Field meanings:
+- `to`: recipient identifiers and optional email.
+- `messages[]`: the unread messages that triggered the notification (use for counts and previews).
+- `senderDetails`: most recent sender; map to subjects or hero text.
+- `groupDetails`: only present for group conversations.
+- `subject`: ready-to-use subject if you do not build it in the provider.
+
+### One-on-one
+```json
+{
+ "to": { "uid": "customer-123", "email": "andrew@example.com", "name": "Andrew Joseph" },
+ "messages": [
+ {
+ "sender": { "uid": "agent-42", "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp", "name": "Susan Marie" },
+ "message": "Are we meeting this weekend?",
+ "messageObject": { "category": "message", "type": "text" }
+ },
+ {
+ "sender": { "uid": "agent-42", "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp", "name": "Susan Marie" },
+ "message": "📷 Has shared an image"
+ }
+ ],
+ "senderDetails": { "uid": "agent-42", "name": "Susan Marie", "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp" },
+ "subject": "New messages from Susan Marie"
+}
+```
+
+### Group
+```json
+{
+ "to": { "uid": "customer-123", "name": "Andrew Joseph" },
+ "messages": [
+ { "sender": { "uid": "mod-5", "name": "John Paul" }, "message": "Hello all! What's up?" },
+ { "sender": { "uid": "agent-42", "name": "Susan Marie" }, "message": "This is the place I was thinking about" }
+ ],
+ "groupDetails": { "guid": "community-1", "name": "Hiking Group" },
+ "subject": "New messages in Hiking Group"
+}
+```
+
+## Subject examples
+
+You can use the provided `subject` field or build your own using `senderDetails.name` and `groupDetails.name`. Here are default and privacy-focused subject templates:
+
+
+
+
+
+| Use case | Default subject | Privacy subject | Example result (default) |
+| --- | --- | --- | --- |
+| One-on-one notification | New messages from `{{senderDetails.name}}` | New messages from `{{senderDetails.name}}` | New messages from Susan Marie |
+| Group notification | New messages in `{{groupDetails.name}}` | New messages in `{{groupDetails.name}}` | New messages in Hiking Group |
+
+## Tips
+
+- Keep privacy variants generic to avoid leaking message content.
+- Use `messages[0].message` for a short preview; use the length for unread counts.
+- If you use SendGrid Dynamic Templates, map the payload into `dynamic_template_data` (subject, recipient name, sender name, unread count, preview, and the `messages` array).
+
+{/* ## Example: using with SendGrid API
+
+Pass payload fields into a SendGrid Dynamic Template. The template can reference variables like `{{subject}}`, `{{recipientName}}`, `{{previewMessage}}`, and loop over `messages`.
+
+```js
+import sgMail from '@sendgrid/mail';
+sgMail.setApiKey(process.env.SENDGRID_API_KEY);
+
+// payload from CometChat
+const payload = {
+ to: { email: 'andrew@example.com', name: 'Andrew Joseph' },
+ senderDetails: { name: 'Susan Marie' },
+ messages: [
+ { message: 'Are we meeting this weekend?' },
+ { message: '📷 Has shared an image' }
+ ],
+ subject: 'New messages from Susan Marie'
+};
+
+// Map payload into template data
+const msg = {
+ to: payload.to.email,
+ from: 'no-reply@yourdomain.com',
+ templateId: 'YOUR_DYNAMIC_TEMPLATE_ID',
+ dynamic_template_data: {
+ subject: payload.subject,
+ recipientName: payload.to.name,
+ senderName: payload.senderDetails.name,
+ unreadCount: payload.messages.length,
+ previewMessage: payload.messages[0]?.message || '',
+ messages: payload.messages
+ }
+};
+
+await sgMail.send(msg);
+``` */}
diff --git a/notifications/flutter-push-notifications-android.mdx b/notifications/flutter-push-notifications-android.mdx
new file mode 100644
index 00000000..d55a908e
--- /dev/null
+++ b/notifications/flutter-push-notifications-android.mdx
@@ -0,0 +1,211 @@
+---
+title: "Flutter Push Notifications (Android)"
+description: "CometChat push notifications in Flutter apps on Android using Firebase Cloud Messaging (FCM)."
+---
+
+
+ Reference implementation of Flutter UI Kit, FCM and Push Notification Setup.
+
+
+## What this guide covers
+
+- CometChat dashboard setup (enable push, add FCM provider) with screenshots.
+- Firebase/FCM + Flutter wiring (credentials, pubspec, Firebase init).
+- Copying the sample notification stack and aligning package IDs/provider IDs.
+- Native Android glue (manifest, MethodChannel, lock-screen call activity/receiver).
+- Token registration, notification/call handling, navigation, testing, and troubleshooting.
+
+{/* ## What you need first
+
+- Firebase project with an Android app configured (package name matches your `applicationId`) and Cloud Messaging enabled; `google-services.json` inside `android/app`.
+- CometChat app credentials (App ID, Region, Auth Key) plus Push Notifications enabled with an **FCM provider** for Flutter Android.
+- Flutter 3.24+ / Dart 3+, the latest CometChat UI Kit (`cometchat_chat_uikit`) and Calls UI Kit (`cometchat_calls_uikit`) packages.
+- Physical Android device for testing—full-screen call notifications and background delivery are unreliable on emulators. */}
+
+## How FCM + CometChat work together
+
+- **FCM’s role:** Issues the Android registration token and delivers the push payload to the device.
+- **CometChat’s role:** The FCM provider you add in the CometChat dashboard stores your Firebase service account. When `PNRegistry.registerPNService(token, true, false)` runs after login, CometChat binds that token to the logged-in user and sends pushes to FCM on your behalf.
+- **Flow:** Permission (Android 13+ `POST_NOTIFICATIONS`) → Firebase returns FCM token → after `CometChatUIKit.login`, register with `PNRegistry` (uses `AppCredentials.fcmProviderId`) → CometChat sends to FCM → FCM delivers to the device → `NotificationLaunchHandler` / `VoipNotificationHandler` route taps and call actions.
+
+## 1. Enable push and add providers (CometChat Dashboard)
+
+1. Go to **Notifications → Settings** and enable **Push Notifications**.
+
+
+
+
+
+2. Click **Add Credentials**, choose **FCM**, upload the Firebase service account JSON (Firebase → Project settings → Service accounts → Generate new private key), and copy the Provider ID.
+
+
+
+
+
+Keep the provider ID—you’ll use it in `AppCredentials.fcmProviderId`.
+
+## 2. Prepare Firebase and credentials
+
+### 2.1 Firebase Console
+
+1. Register your Android package name (the same as `applicationId` in `android/app/build.gradle`) and download `google-services.json` into `android/app`.
+2. Enable Cloud Messaging and copy the Server key if you want to send test messages manually.
+
+
+
+
+
+### 2.3 Local configuration file
+
+Update [`lib/app_credentials.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/app_credentials.dart) so it exposes your credentials and provider IDs:
+
+```dart lines
+class AppCredentials {
+ static String _appId = "YOUR_APP_ID";
+ static String _authKey = "YOUR_AUTH_KEY";
+ static String _region = "YOUR_REGION";
+ static String _fcmProviderId = "FCM-PROVIDER-ID";
+}
+```
+
+The sample persists these values to `SharedPreferences`; `saveAppSettingsToNative()` passes them to Android so `CallActionReceiver` can reject calls even if Flutter is not running.
+
+## 3. Bring the notification stack into Flutter
+
+### 3.1 Copy [`lib/notifications`](https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications/lib/notifications)
+
+- Clone or download the sample once.
+- Copy the entire `lib/notifications` directory (models, Android/iOS services, helpers) into your app.
+- Update the import prefixes (for example replace `package:sample_app_push_notifications/...` with your own package name). Keeping the same folder names avoids manual refactors later.
+
+### 3.2 Wire the entry points
+
+**[`lib/main.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/main.dart)**
+
+- Initialize `SharedPreferencesClass`, `FlutterLocalNotificationsPlugin`, and Firebase before calling `runApp`.
+- Cache `NotificationLaunchHandler.pendingNotificationResponse` when the app is launched from a tapped notification while terminated.
+- Keep the `callMain` entrypoint; `CallActivity` uses it to render the ongoing-call UI over the lock screen.
+
+**[`lib/guard_screen.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/guard_screen.dart) / [`lib/dashboard.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/dashboard.dart) (or your first screen after login)**
+
+- Ensure `CometChatUIKit.init()` and `CometChatUIKit.login()` finish before rendering the dashboard.
+- On Android, instantiate `FirebaseService` and call `notificationService.init(context)` once; on iOS, keep `APNSService`.
+- Replay `NotificationLaunchHandler.pendingNotificationResponse` after the widget tree builds so taps from a killed app still navigate to `MessagesScreen`.
+- Forward lifecycle changes to `IncomingCallOverlay` / `BoolSingleton` to hide stale overlays when the app resumes.
+- `VoipNotificationHandler.handleNativeCallIntent(context)` runs after the first frame to act on accept/decline actions that were tapped from the Android notification before Flutter started.
+
+### 3.3 Align dependencies and configuration
+
+Mirror the sample `pubspec.yaml` versions (update as needed when newer releases ship):
+
+```yaml lines
+dependencies:
+ firebase_core: ^3.9.0
+ firebase_messaging: ^15.1.6
+ flutter_local_notifications: ^18.0.0
+ flutter_callkit_incoming:
+ path: ../sample_app_push_notifications/flutter_callkit_incoming
+ cometchat_chat_uikit: ^5.2.5
+ cometchat_calls_uikit: ^5.0.11
+ permission_handler: ^11.3.1
+ shared_preferences: ^2.2.1
+```
+
+Run `flutter pub get`, then `flutterfire configure` if you still need to generate `firebase_options.dart`.
+
+## 4. Configure the native Android layer
+
+### 4.1 Gradle + Firebase
+
+1. Add `google-services.json` to `android/app`.
+2. Ensure `android/app/build.gradle` applies the plugins used in the sample:
+
+```gradle lines
+plugins {
+ id "com.android.application"
+ id "com.google.gms.google-services"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+```
+
+Set `applicationId` to your package name and keep `minSdk 24` or higher. `compileSdk 36` / `targetSdk 35` match the sample but can be raised if your project already targets a newer API.
+
+### 4.2 Manifest permissions and components
+
+Use the sample [`AndroidManifest.xml`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/android/app/src/main/AndroidManifest.xml) as a baseline:
+
+- Permissions for notifications, audio/video, and lock-screen call UI: `POST_NOTIFICATIONS`, `RECORD_AUDIO`, `CAMERA`, `FOREGROUND_SERVICE`, `USE_FULL_SCREEN_INTENT`, `WAKE_LOCK`, `SHOW_WHEN_LOCKED`, `TURN_SCREEN_ON`, and `SYSTEM_ALERT_WINDOW`.
+- `MainActivity` uses `launchMode="singleTask"` with `android:showWhenLocked="true"` / `android:turnScreenOn="true"` so incoming calls can wake the screen.
+- `CallActivity` is a dedicated entrypoint (uses `callMain`) to render the ongoing call over the lock screen and is excluded from recents.
+- `CallActionReceiver` listens to `flutter_callkit_incoming` actions (and mirrored app-specific actions) so Accept/Decline from the native notification reach Flutter.
+- Set `default_notification_icon` meta-data to your icon if you change the launcher asset.
+
+### 4.3 Kotlin bridge for call intents
+
+- [`MainActivity.kt`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/android/app/src/main/kotlin/com/cometchat/sampleapp/flutter/android/MainActivity.kt) exposes a `MethodChannel("com.cometchat.sampleapp")` that supports:
+ - `get_initial_call_intent` – read and clear any call intent extras so `VoipNotificationHandler.handleNativeCallIntent` in Dart can react after Flutter launches.
+ - `setupLockScreenForCall` / `restoreLockScreenAfterCall` – temporarily bypass and then restore the lock screen when a call is accepted.
+ - `saveAppSettings` – stores your App ID and Region for the broadcast receiver.
+- [`CallActionReceiver.kt`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/android/app/src/main/kotlin/com/cometchat/sampleapp/flutter/android/CallActionReceiver.kt) wakes the app for Accept/Decline actions. On decline, it can initialize the CometChat SDK headlessly (using the saved App ID/Region) to reject the call as busy even if Flutter is not running.
+- [`CallActivity.kt`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/android/app/src/main/kotlin/com/cometchat/sampleapp/flutter/android/CallActivity.kt) overrides `getDartEntrypointFunctionName` to `callMain`, letting the ongoing-call UI render in its own activity with lock-screen flags.
+
+If you change the MethodChannel name in Kotlin, update `voipPlatformChannel` inside [`lib/notifications/services/save_settings_to_native.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/notifications/services/save_settings_to_native.dart) to match.
+
+## 5. Token registration and runtime events
+
+### 5.1 FCM tokens
+
+`FirebaseService.init` requests notification permission, sets the background handler (`firebaseMessagingBackgroundHandler`), and registers tokens:
+
+```dart lines
+final token = await FirebaseMessaging.instance.getToken();
+if (token != null) {
+ PNRegistry.registerPNService(token, true, false); // platform: FCM_FLUTTER_ANDROID
+}
+FirebaseMessaging.instance.onTokenRefresh.listen(
+ (token) => PNRegistry.registerPNService(token, true, false),
+);
+```
+
+`PNRegistry` pulls the provider ID from `AppCredentials.fcmProviderId`. Call this only after `CometChatUIKit.login` succeeds.
+
+### 5.2 Local notifications and navigation
+
+- `LocalNotificationService.showNotification` renders a high-priority local notification when the incoming CometChat message does not belong to the currently open conversation.
+- `NotificationLaunchHandler.pendingNotificationResponse` caches taps triggered while the app is terminated; `dashboard.dart` replays it after navigation is ready.
+- `LocalNotificationService.handleNotificationTap` fetches the user/group and pushes `MessagesScreen` when a notification is tapped from foreground, background, or terminated states.
+
+### 5.3 Call events (VoIP-like pushes)
+
+- The top-level `firebaseMessagingBackgroundHandler` shows the incoming-call UI by calling `VoipNotificationHandler.displayIncomingCall`, which uses `flutter_callkit_incoming` to render a full-screen notification.
+- `FirebaseService.initializeCallKitListeners` binds `FlutterCallkitIncoming.onEvent` so Accept/Decline/Timeout actions map to `VoipNotificationHandler.acceptVoipCall`, `declineVoipCall`, or `endCall`.
+- `VoipNotificationHandler.handleNativeCallIntent` reads Accept/Decline extras passed from `CallActionReceiver` via the MethodChannel if the user acted before Flutter started.
+- `saveAppSettingsToNative()` runs during `FirebaseService.init` to persist App ID/Region for the native receiver; keep it in place or `CallActionReceiver` cannot initialize CometChat when rejecting a call from the lock screen.
+
+## 6. Testing checklist
+
+1. Run on a physical Android device. Grant notification, microphone, and camera permissions when prompted (Android 13+ requires `POST_NOTIFICATIONS`).
+2. Send a message from another user:
+ - Foreground: a local notification banner shows (unless you are in that chat).
+ - Background: FCM notification appears; tapping opens the right conversation.
+3. Force-quit the app, send another message push, tap it, and confirm `NotificationLaunchHandler` launches `MessagesScreen`.
+4. Trigger an incoming CometChat call. Ensure:
+ - The full-screen call UI shows caller name/type with Accept/Decline.
+ - Accepting on the lock screen notifies Flutter (`handleNativeCallIntent`), starts the call session, and dismisses the native UI when the call ends.
+ - Declining from the notification triggers `CallActionReceiver` to reject the call server-side.
+5. Rotate through Wi-Fi/cellular and reinstall the app to confirm token registration works after refresh events.
+
+## 7. Troubleshooting tips
+
+| Symptom | Quick checks |
+| --- | --- |
+| No notifications received | Confirm `google-services.json` is in `android/app`, the package name matches Firebase, and notification permission is granted (Android 13+). |
+| Token registration errors | Double-check `AppCredentials.fcmProviderId` and that `PNRegistry.registerPNService` runs after login. |
+| Call actions never reach Flutter | Ensure `CallActionReceiver` is declared in the manifest, MethodChannel names match `voipPlatformChannel`, and `VoipNotificationHandler.handleNativeCallIntent` is called from `dashboard.dart`. |
+| Full-screen call UI not showing | Verify `USE_FULL_SCREEN_INTENT`, `WAKE_LOCK`, and `SHOW_WHEN_LOCKED` permissions plus `android:showWhenLocked="true"` / `android:turnScreenOn="true"` on `MainActivity` and `CallActivity`. |
+| Tapping notification from killed app does nothing | Keep the `NotificationLaunchHandler` logic in `main.dart` and replay it after the navigator key is ready (post-frame callback). |
diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx
new file mode 100644
index 00000000..bd262cde
--- /dev/null
+++ b/notifications/flutter-push-notifications-ios.mdx
@@ -0,0 +1,235 @@
+---
+title: "Flutter Push Notifications (iOS)"
+description: "CometChat push notifications in Flutter apps on iOS using Apple Push Notification service (APNs)."
+---
+
+
+ Reference implementation of Flutter UI Kit, APNs and Push Notification Setup.
+
+
+## What this guide covers
+
+- CometChat dashboard setup (enable push, create APNs + optional VoIP/FCM providers) with screenshots.
+- Apple + Firebase setup (entitlements, APNs key, `GoogleService-Info.plist`).
+- Copying the sample notification stack and aligning package IDs/provider IDs.
+- Token registration, navigation from pushes, testing, and troubleshooting.
+
+{/* ## What you need first
+
+- Apple Developer account with Push Notifications, Background Modes, and VoIP entitlements for your bundle ID.
+- Firebase project with an iOS app configured (`GoogleService-Info.plist` inside the Runner target) and Cloud Messaging enabled.
+- CometChat app credentials (App ID, Region, Auth Key) plus Push Notification extension enabled with at least an **APNs provider** (add VoIP and FCM providers if you use them).
+- Flutter 3.24+ / Dart 3+, the latest CometChat UI Kit (`cometchat_chat_uikit`) and Calls UI Kit (`cometchat_calls_uikit`) packages.
+- Physical iPhone or iPad for testing—simulators cannot receive VoIP pushes or present CallKit UI. */}
+
+## How FCM + CometChat work together
+
+- **APNs is primary on iOS:** FCM can be used only as a bridge. Firebase hands the payload to APNs using the APNs key you uploaded in Firebase.
+- **CometChat providers:** The APNs/VoIP/FCM providers you create in the CometChat dashboard hold your Apple/FCM credentials. `CometChatPushRegistry.register(token, isFcm: false, isVoip: ...)` binds the APNs/VoIP tokens; `isFcm: true` uses the FCM provider if you decide to register FCM tokens too.
+- **Flow:** Permission prompt → APNs (and/or FCM) token issued → after `CometChatUIKit.login` succeeds, register tokens with `CometChatPushRegistry` using the matching provider IDs → CometChat sends to FCM/APNs → APNs delivers to the device → Flutter handles taps via `NotificationLaunchHandler` and `APNSService`.
+
+## 1. Enable push and add providers (CometChat Dashboard)
+
+1. Go to **Notifications → Settings** and enable **Push Notifications**.
+
+
+
+
+
+2. Click **Add Credentials**, choose **APNs** (and **APNs VoIP** if you want in-call pushes), upload your `.p8` key or certificate, and copy each Provider ID.
+
+
+
+
+
+3. (Optional) Add an **FCM** provider if you plan to register FCM tokens on iOS.
+
+
+
+
+
+Keep the provider IDs—you’ll set them in `CometChatConfig`.
+
+## 2. Prepare Apple + Firebase credentials
+
+### 2.1 Apple Developer portal
+
+1. Generate an APNs Auth Key (`.p8`) and note the **Key ID** and **Team ID**.
+2. Enable Push Notifications plus Background Modes → *Remote notifications* and *Voice over IP* on the bundle ID.
+3. Create a VoIP Services certificate/key if you want separate credentials.
+
+### 2.2 Firebase Console
+
+1. Register the same bundle ID and download `GoogleService-Info.plist` into `ios/Runner`.
+2. Enable Cloud Messaging and upload the APNs key under *Project Settings → Cloud Messaging*.
+
+
+
+
+
+## 3. Local configuration file
+
+Update [`lib/cometchat_config.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/cometchat_config.dart) (or your own config file) so it exposes:
+
+```dart lines
+class CometChatConfig {
+ static const appId = "YOUR_APP_ID";
+ static const region = "YOUR_APP_REGION";
+ static const authKey = "YOUR_AUTH_KEY";
+ static const fcmProviderId = "FCM-PROVIDER-ID";
+ static const apnProviderId = "APNS-PROVIDER-ID";
+ static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; // optional but recommended
+}
+```
+
+## 4. Bring the notification stack into Flutter
+
+### 4.1 Copy [`lib/notifications`](https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications/lib/notifications)
+
+- Clone or download the sample once.
+- Copy the entire [`lib/notifications`](https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications/lib/notifications) directory (models, Android/iOS services, helpers) into your app.
+- Update the import prefixes (for example replace `package:flutter_application_demo/...` with your own package name). Keeping the same folder names avoids manual refactors later.
+
+### 4.2 Wire the entry points
+
+**[`lib/main.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/main.dart)**
+
+- Initialize `SharedPreferencesClass`, Firebase, and `FlutterLocalNotificationsPlugin` before calling `runApp`.
+- Store `NotificationLaunchHandler.pendingNotificationResponse` when the app is opened by tapping a notification while terminated.
+- On iOS, call `APNSService.setupNativeCallListener(context)` from `initState` so Flutter reacts when the native CallKit UI changes state.
+
+**[`lib/guard_screen.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/guard_screen.dart) / [`lib/dashboard.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/dashboard.dart) (or your first screen after login)**
+
+- Ensure `CometChatUIKit.init()` and `CometChatUIKit.login()` finish before rendering the dashboard.
+- Instantiate `APNSService` (iOS only) and call `apnsService.init(context)` inside `initState`.
+- Register CometChat UI + Calls listeners (`CometChatUIEventListener`, `CometChatCallEventListener`, and `CallListener`) exactly once per session; the sample stores the listener IDs inside `APNSService`.
+- Replay `NotificationLaunchHandler.pendingNotificationResponse` after the widget tree builds so taps from a killed app still navigate to `MessagesScreen`.
+- Forward lifecycle changes to `IncomingCallOverlay` / `BoolSingleton` to hide stale overlays when the app resumes.
+
+### 4.3 Align dependencies and configuration
+
+Mirror the sample `pubspec.yaml` versions (update as needed when newer releases ship):
+
+```yaml lines
+dependencies:
+ firebase_core: ^3.0.0
+ firebase_messaging: ^15.0.0
+ flutter_apns_x: ^2.1.1
+ flutter_callkit_incoming: ^2.0.3+3
+ flutter_local_notifications: ^16.0.0
+ cometchat_chat_uikit: ^5.0.0
+ cometchat_calls_uikit: ^5.0.0
+ permission_handler: ^11.3.0
+```
+
+Run `flutter pub get`, then `flutterfire configure` if you still need to generate `firebase_options.dart`.
+
+## 5. Configure the native iOS layer
+
+### 5.1 Capabilities and Info.plist
+
+1. Open `ios/Runner.xcworkspace` in Xcode.
+2. Under *Signing & Capabilities*, enable **Push Notifications** and **Background Modes** (Remote notifications + Voice over IP).
+3. Add microphone, camera, bluetooth, and notification permission strings to `Info.plist`.
+4. Set the development team that has access to the APNs/VoIP keys you generated earlier.
+
+
+
+
+
+### 5.2 `AppDelegate.swift` bridge
+
+Start from the sample [`ios/Runner/AppDelegate.swift`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/ios/Runner/AppDelegate.swift) and keep these pieces intact:
+
+- **MethodChannel handshake** – create a channel that both Flutter and Swift know:
+
+```swift lines
+let appInfoChannel = FlutterMethodChannel(
+ name: "com.flutter_application_demo/ios",
+ binaryMessenger: controller.binaryMessenger
+)
+```
+
+Handle at least `getAppInfo`, `endCall`, `onCallAcceptedFromNative`, and `onCallEndedFromNative`, mirroring the Dart side (`APNSService.setupNativeCallListener`).
+
+- **Firebase + plugin registration** – call `FirebaseApp.configure()` before `GeneratedPluginRegistrant.register(with: self)`.
+- **PushKit** – instantiate `PKPushRegistry`, set `desiredPushTypes = [.voIP]`, and forward the token inside `pushRegistry(_:didUpdate:for:)` via `SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(tokenHex)`.
+- **CallKit** – configure `CXProviderConfiguration`, keep a `CXCallController`, and implement `provider(_:perform: CXAnswerCallAction)` / `provider(_:perform: CXEndCallAction)` so native actions propagate to Flutter.
+- **Incoming push handler** – inside `pushRegistry(_:didReceiveIncomingPushWith:)`, convert the CometChat payload into `flutter_callkit_incoming.Data`, set `extra` with the raw payload, and call `showCallkitIncoming(..., fromPushKit: true)`.
+- **UUID helper** – reuse `createUUID(sessionid:)` to produce valid `UUID`s from long CometChat session IDs; this lets CallKit correlate calls even if the payload string exceeds 32 characters.
+
+If you change the MethodChannel name in Swift, remember to update `APNSService.platform` in Dart to match.
+
+## 6. Token registration and runtime events
+
+### 6.1 Standard APNs tokens
+
+`APNSService` hooks into `FirebaseMessaging.instance.getAPNSToken()` (and `onTokenRefresh`) before calling:
+
+```dart lines
+await CometChatPushRegistry.register(
+ token: token,
+ isFcm: false,
+ isVoip: false,
+);
+```
+
+This registers the device token against the APNs provider selected in `CometChatConfig.apnProviderId`.
+
+### 6.2 VoIP tokens
+
+- Capture the PushKit token in `AppDelegate.pushRegistry(_:didUpdate:for:)`.
+- Forward it to Flutter via the MethodChannel or register it directly from Swift by invoking `CometChatPushRegistry` through `SwiftFlutterCallkitIncomingPlugin`.
+- If you keep the Dart implementation, emit a MethodChannel call named `onVoipToken` and handle it in `APNSService` by calling `CometChatPushRegistry.register(token: token, isFcm: false, isVoip: true);`.
+
+### 6.3 Local notifications and navigation
+
+- `APNSService._showNotification` displays a local notification when the incoming CometChat message does not belong to the currently open conversation.
+- `LocalNotificationService.handleNotificationTap` parses the payload, fetches the relevant user/group, and pushes `MessagesScreen`.
+- `NotificationLaunchHandler.pendingNotificationResponse` caches taps triggered while the app is terminated; replay it on the dashboard once the UI is ready.
+
+### 6.4 Call events
+
+- `FlutterCallkitIncoming.onEvent` is already wired inside `APNSService` to accept or end calls initiated by CallKit.
+- When native CallKit UI accepts/ends a call, Swift invokes `onCallAcceptedFromNative` / `onCallEndedFromNative` on the MethodChannel; `APNSService` then calls `FlutterCallkitIncoming.setCallConnected` or `CometChat.endCall()` to keep both stacks synchronized.
+
+## 7. Testing checklist
+
+1. Run the app on a physical device in debug first. Grant notification, microphone, camera, and Bluetooth permissions when prompted.
+2. Send a message from another user:
+ - Foreground: a local notification banner shows (unless you are in that chat).
+ - Background: APNs notification appears, tapping opens the right conversation.
+3. Force-quit the app, send another message push, tap it, and confirm `NotificationLaunchHandler` launches `MessagesScreen`.
+4. Trigger an incoming CometChat call. Ensure:
+ - CallKit UI shows contact name, call type, and Accept/Decline.
+ - Accepting on the lock screen notifies Flutter (`setupNativeCallListener`), starts the call session, and dismisses the native UI when the call ends.
+5. Decline the call and confirm both CallKit and Flutter clean up (`BoolSingleton` resets, overlays dismissed).
+6. Rotate through Wi-Fi/cellular and reinstall the app to confirm token registration works after refresh events.
+
+## 8. Troubleshooting tips
+
+| Symptom | Quick checks |
+| --- | --- |
+| No VoIP pushes | Entitlements missing? Ensure Push Notifications + Background Modes (VoIP) are enabled and the bundle ID matches the CometChat provider. |
+| CallKit UI never dismisses | Make sure `endCall(callUUID:)` reports to `CXProvider`, runs a `CXEndCallAction`, **and** calls `SwiftFlutterCallkitIncomingPlugin.sharedInstance?.endCall`. |
+| Flutter never receives native call events | Confirm the MethodChannel names match between Swift and Dart, and `APNSService.setupNativeCallListener` runs inside `initState`. |
+| Token registration errors | Double-check `CometChatConfig` provider IDs, and verify you call `registerPushToken` after `CometChatUIKit.login` succeeds. |
+| Notification taps ignored | Ensure you replay `NotificationLaunchHandler.pendingNotificationResponse` **after** the navigator key is ready (typically `WidgetsBinding.instance.addPostFrameCallback`). |
+
+{/* ## Additional resources
+
+
+
+
+
+Push notifications sample app for Flutter
+
+View on Github
+
+
+
+ */}
diff --git a/notifications/ios-apns-push-notifications-legacy.mdx b/notifications/ios-apns-push-notifications-legacy.mdx
new file mode 100644
index 00000000..853ba2a1
--- /dev/null
+++ b/notifications/ios-apns-push-notifications-legacy.mdx
@@ -0,0 +1,685 @@
+---
+title: "iOS APNs"
+---
+
+Apple Push Notification service or APNs is used to send notifications to iOS devices. With this, you can also use Apple's CallKit for showing the call screen.
+
+
+ iOS Push notifications sample app
+
+ View on Github
+
+
+## Get APNS Credentials
+
+The following steps in this section are written on the assumption that you already have an app ID assigned to your client app.
+
+### Step 1: Create a Certificate Signing Request
+
+To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
+
+1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
+
+
+
+
+
+2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button.
+
+
+
+
+
+3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
+
+### Step 2: Create an SSL certificate
+
+1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
+2. Go to Certificates, Identifiers & Profiles.
+
+
+
+
+
+3. Create new Certificate by clicking on the + icon.
+
+
+
+
+
+4. Under Services, select - Apple Push Notification services SSL (Sandbox & Production)
+
+
+
+
+
+5. Select your App ID from the dropdown.
+
+
+
+
+
+6. Upload CSR file., upload the CSR file you created through the **Choose File** button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
+
+
+
+
+
+
+
+
+
+### Step 3: Export and update .p8 certificate
+
+1. To generate a .p8 key file, go to [Apple Developer Account](https://developer.apple.com/account/), then select Certificates, IDs & Profiles.
+2. Select Keys and click on the "+" button to add a new key.
+3. In the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click "Continue" and click "Register".
+4. Then proceed to download the key file by clicking Download.
+5. Make note of the `Key ID`, `Team ID` and your `Bundle ID` for saving in the Extension's settings.
+
+**If you wish to use the .p12 certificate instead, do the following:**
+
+1. Type a name for the .p12 file and save it to your Mac.
+2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
+3. DO NOT provide an export password when prompted.
+4. The .p12 file will be required in the next step for uploading in the CometChat Dashboard.
+
+## Extension settings
+
+### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications extension.
+3. Open the settings for this extension and save the following.
+
+### Step 2: Save your settings
+
+
+
+
+
+
+
+
+
+On the Settings page you need to enter the following:
+
+1. **Set extension version**
+
+ 1. The extension version has to be set to 'V2' or 'V1 & V2' in order to use APNs as the provider.
+
+2. **Select Platforms**
+
+ 1. You can select the platforms on which you wish to receive Push Notifications.
+
+3. **APNs Settings**
+
+ 1. You can turn off the Production mode when you create a development build of your application.
+ 2. Upload the .p8 or .p12 certificate exported in the previous step.
+
+4. **Push payload message options**
+
+
+
+
+
+The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+
+* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+
+5. **Notification Triggers**
+
+ 1. Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+ 2. These are pretty self-explanatory and you can toggle them as per your requirement.
+
+## iOS App Setup
+
+### Initial Setup
+
+1. Call `CometChat.init()` method to initialize CometChat in your application. This needs to be called only once.
+2. The user has to be logged in using `CometChat.login()` method. On the success callback, register the token with the extension. Two tokens need to be registered, out of which one is APNs token and other is CallKit token: a. `CometChat.registerTokenForPushNotification(token: apnsToken, settings: ["voip":false])`\
+ b. `CometChat.registerTokenForPushNotification(token: voipToken, settings: ["voip":true])`
+
+
+
+```swift
+let authKey = "XXXX XXXX XXXXX"
+
+CometChat.login(UID: UID, authKey: authKey, onSuccess: { (current_user) in
+ DispatchQueue.main.async {
+ if let apnsToken = UserDefaults.standard.value(forKey: "apnsToken") as? String {
+ print("APNS token is: \(apnsToken)")
+ CometChat.registerTokenForPushNotification(token: apnsToken, settings: ["voip":false]) { (success) in
+ print("onSuccess to registerTokenForPushNotification: \(success)")
+ DispatchQueue.main.async {self.activityIndicator.stopAnimating()
+ print("login success with : \(current_user.stringValue())")
+ self.performSegue(withIdentifier: "presentPushNotification", sender: nil)
+ }
+ } onError: { (error) in
+ print("error to registerTokenForPushNotification")
+ }
+ }
+ if let voipToken = UserDefaults.standard.value(forKey: "voipToken") as? String {
+ print("VOIP token is: \(voipToken)")
+ CometChat.registerTokenForPushNotification(token: voipToken, settings: ["voip":true]) { (success) in
+ print("onSuccess to registerTokenForPushNotification: \(success)")
+ DispatchQueue.main.async {self.activityIndicator.stopAnimating()
+ print("login success with : \(current_user.stringValue())")
+ self.performSegue(withIdentifier: "presentPushNotification", sender: nil)
+ }
+ } onError: { (error) in
+ print("error to registerTokenForPushNotification")
+ }
+ }
+ }
+ }
+ }) { (error) in
+ print("error while login", error);
+ }
+ }
+```
+
+
+
+
+
+3. Import PushKit and CallKit in AppDelegate.Swift file.
+
+
+
+```swift
+import PushKit
+import CallKit
+```
+
+
+
+
+
+### Receive Push Notifications
+
+1. Registering for the APNs notifications
+
+
+
+```swift
+var window: UIWindow?
+var uuid: UUID?
+var activeCall: Call?
+var cancelCall: Bool = true
+var onCall = true
+var callController = CXCallController()
+let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
+var provider: CXProvider? = nil
+
+func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ self.voipRegistration()
+ // [START register_for_notifications]
+if #available(iOS 10.0, *) {
+UNUserNotificationCenter.current().delegate = self
+let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
+UNUserNotificationCenter.current().requestAuthorization(
+options: authOptions,
+completionHandler: {_, _ in })
+} else {
+let settings: UIUserNotificationSettings =
+UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
+application.registerUserNotificationSettings(settings)
+}
+application.registerForRemoteNotifications()
+// [END register_for_notifications]
+return true
+}
+// Register for VoIP notifications
+func voipRegistration() {
+// Create a push registry object
+let mainQueue = DispatchQueue.main
+let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
+voipRegistry.delegate = self
+voipRegistry.desiredPushTypes = [PKPushType.voIP]
+}
+```
+
+
+
+
+
+2. Add AppDelegate extension for receiving Push Notifications
+
+
+
+```swift
+extension AppDelegate : UNUserNotificationCenterDelegate {
+// Receive displayed notifications for iOS 10 devices.
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+willPresent notification: UNNotification,
+withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+print("willPresent notification: \(notification.request.content.userInfo)")
+if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject =
+userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
+if let baseMessage = CometChat.processMessage(dict).0 {
+switch baseMessage.messageCategory {
+case .message:
+if let message = baseMessage as? BaseMessage {
+switch message.messageType {
+case .text:
+print("text Messagge is: \((message as? TextMessage)?.stringValue())")
+case .image:
+print("image Messagge is: \((message as? MediaMessage)?.stringValue())")
+case .video:
+print("video Messagge is: \((message as? MediaMessage)?.stringValue())")
+case .audio:
+print("audio Messagge is: \((message as? MediaMessage)?.stringValue())")
+case .file:
+print("file Messagge is: \((message as? MediaMessage)?.stringValue())")
+case .custom:
+print("custom Messagge is: \((message as? MediaMessage)?.stringValue())")
+case .groupMember:
+break
+@unknown default:
+break
+}
+}
+case .action: break
+case .call:
+if let call = baseMessage as? Call {
+print("call is: \(call.stringValue())")
+}
+case .custom:
+if let customMessage = baseMessage as? CustomMessage {
+print("customMessage is: \(customMessage.stringValue())")
+}
+@unknown default: break
+}
+}
+}
+completionHandler([.alert, .badge, .sound])
+}
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+didReceive response: UNNotificationResponse,
+withCompletionHandler completionHandler: @escaping () -> Void) {
+let notification = response.notification.request.content.userInfo
+print("notification is 11: \(notification)")
+completionHandler()
+}
+func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
+print("Device Token : ",token)
+let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
+print("Device Token 11: ",hexString)
+UserDefaults.standard.set(hexString, forKey: "apnsToken")
+CometChat.registerTokenForPushNotification(token: hexString, settings: ["voip":false]) { (success) in
+print("registerTokenForPushNotification voip: \(success)")
+} onError: { (error) in
+print("registerTokenForPushNotification error: \(error)")
+}
+}
+}
+```
+
+
+
+
+
+3. Add AppDelegate extension for VOIP notifications. Launch CallKit screen when the VOIP notification is received. Once the CallKit screen is displayed, you can Accept or Reject the CometChat call accordingly.
+
+
+
+```swift
+// MARK: CallKit & PushKit
+extension AppDelegate: PKPushRegistryDelegate , CXProviderDelegate {
+
+
+func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
+ let deviceToken = pushCredentials.token.reduce("", {$0 + String(format: "%02X", $1) })
+ print("voip token is: \(deviceToken)")
+ UserDefaults.standard.set(deviceToken, forKey: "voipToken")
+ CometChat.registerTokenForPushNotification(token: deviceToken, settings: ["voip":true]) { (success) in
+ print("registerTokenForPushNotification voip: \(success)")
+ } onError: { (error) in
+ print("registerTokenForPushNotification error: \(error)")
+ }
+ }
+
+func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
+ if let userInfo = payload.dictionaryPayload as? [String : Any], let messageObject =
+ userInfo["message"], let dict = messageObject as? [String : Any] {
+
+ if let baseMessage = CometChat.processMessage(dict).0 {
+ switch baseMessage.messageCategory {
+ case .message: break
+ case .action: break
+ case .call:
+ if let call = baseMessage as? Call {
+ switch call.callStatus {
+ case .initiated:
+ self.activeCall = call
+ self.uuid = UUID()
+ if let name = (call.sender)?.name {
+ let config = CXProviderConfiguration(localizedName: "APNS + Callkit")
+ config.iconTemplateImageData = #imageLiteral(resourceName: "your_app_icon").pngData()
+ config.includesCallsInRecents = false
+ config.ringtoneSound = "ringtone.caf"
+ config.supportsVideo = true
+ provider = CXProvider(configuration: config)
+ provider?.setDelegate(self, queue: nil)
+ let update = CXCallUpdate()
+ update.remoteHandle = CXHandle(type: .generic, value: name.capitalized)
+ if call.callType == .video {
+ update.hasVideo = true
+ }else{
+ update.hasVideo = false
+ }
+ provider?.reportNewIncomingCall(with: self.uuid!, update: update, completion: { error in
+ if error == nil {
+ self.configureAudioSession()
+ }
+ })
+ }
+ case .ongoing, .unanswered, .rejected, .busy, .cancelled:
+ if self.activeCall != nil {
+ if self.cancelCall {
+ self.end(uuid: self.uuid!)
+ }
+ }
+ case .ended: break
+ @unknown default: break }
+ }
+ case .custom: break
+ @unknown default: break
+ }
+ }
+ }
+ }
+
+ internal func configureAudioSession() {
+ do {
+ try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: [.mixWithOthers, .allowBluetooth, .defaultToSpeaker])
+ try AVAudioSession.sharedInstance().setActive(true)
+
+ } catch let error as NSError {
+ print(error)
+ }
+ }
+
+ func providerDidReset(_ provider: CXProvider) {
+ if let uuid = self.uuid {
+ onCall = true
+ provider.reportCall(with: uuid, endedAt: Date(), reason: .unanswered)
+ }
+ }
+
+func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
+ if let activeCall = activeCall {
+ startCall()
+ }
+ action.fulfill()
+ }
+
+func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
+ NotificationCenter.default.post(name: NSNotification.Name(rawValue: "didRejectButtonPressed"), object: nil, userInfo: nil)
+ end(uuid: self.uuid!)
+ onCall = true
+ if let activeCall = activeCall {
+ CometChat.rejectCall(sessionID: activeCall.sessionID ?? "", status: .rejected, onSuccess: {(rejectedCall) in
+ DispatchQueue.main.async {
+ CometChatSnackBoard.display(message: "CALL_REJECTED".localized(), mode: .info, duration: .short)
+ }
+ }) { (error) in
+ DispatchQueue.main.async {
+ if let errorMessage = error?.errorDescription {
+ CometChatSnackBoard.display(message: "CALL_REJECTED".localized(), mode: .info, duration: .short)
+ }
+ }
+ }
+ provider.reportCall(with: self.uuid!, endedAt: Date(), reason: .remoteEnded)
+ }
+ action.fail()
+ }
+
+ func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
+ print(#function)
+ }
+
+ func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
+ action.fulfill()
+ print(#function)
+ }
+
+ func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
+ print(#function)
+ }
+
+ func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
+ print(#function)
+ }
+
+ func end(uuid: UUID) {
+ print("endUUID",uuid)
+ let endCallAction = CXEndCallAction(call: uuid)
+ let transaction = CXTransaction()
+ transaction.addAction(endCallAction)
+ requestTransaction(transaction, action: "")
+ }
+
+ func setHeld(uuid: UUID, onHold: Bool) {
+ print("setHeld",uuid)
+ let setHeldCallAction = CXSetHeldCallAction(call: uuid, onHold: onHold)
+ let transaction = CXTransaction()
+ transaction.addAction(setHeldCallAction)
+
+ requestTransaction(transaction, action: "")
+ }
+
+ internal func requestTransaction(_ transaction: CXTransaction, action: String = "") {
+ callController.request(transaction) { error in
+ if let error = error {
+ print("Error requesting transaction: \(error)")
+ } else {
+ print("Requested transaction successfully")
+ }
+ }
+ }
+
+ public func startCall(){
+ let activeCall = CometChatCall()
+ cancelCall = false
+ activeCall.modalPresentationStyle = .fullScreen
+ if let window = UIApplication.shared.windows.first , let rootViewController = window.rootViewController {
+ var currentController = rootViewController
+ while let presentedController = currentController.presentedViewController {
+ currentController = presentedController
+ }
+ currentController.present(activeCall, animated: true, completion: nil)
+ }
+ }
+}
+```
+
+
+
+
+
+## Miscellaneous
+
+### Create view controller for Calls
+
+Create a viewController which will start the call when the user starts the call.
+
+
+
+```swift
+import UIKit
+import CometChatPro
+import CallKit
+
+class CometChatCall: UIViewController {
+
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
+ if let call = appDelegate.activeCall {
+
+ if (call.callInitiator as? User)?.uid != CometChat.getLoggedInUser()?.uid {
+
+ CometChat.acceptCall(sessionID: call.sessionID ?? "") { acceptedCall in
+
+ DispatchQueue.main.async {
+ let callSettings = CallSettings.CallSettingsBuilder(callView: self.view, sessionId: acceptedCall?.sessionID ?? "").setMode(mode: .MODE_SINGLE).build()
+
+ CometChat.startCall(callSettings: callSettings) { userJoined in
+ appDelegate.onCall = true
+ } onUserLeft: { onUserLeft in
+
+ } onUserListUpdated: { onUserListUpdated in
+
+ } onAudioModesUpdated: { onAudioModesUpdated in
+
+ } onUserMuted: { onUserMuted in
+
+ } onCallSwitchedToVideo: { onCallSwitchedToVideo in
+
+ } onRecordingStarted: { onRecordingStarted in
+
+ } onRecordingStopped: { onRecordingStopped in
+
+ } onError: { error in
+ DispatchQueue.main.async {
+ self.dismiss(animated: true, completion: nil)
+ }
+ } onCallEnded: { ended in
+ DispatchQueue.main.async {
+ var str = ""
+ if let uuuid = appDelegate.uuid {
+ print("CometChatCalls", uuuid)
+ }
+ self.dismiss(animated: true, completion: nil)
+ self.dismiss(animated: true)
+ }
+ }
+ }
+ } onError: { error in
+
+ }
+ }else{
+
+ let callSettings = CallSettings.CallSettingsBuilder(callView: self.view, sessionId: call.sessionID ?? "").setMode(mode: .MODE_SINGLE).build()
+
+ CometChat.startCall(callSettings: callSettings) { userJoined in
+
+ } onUserLeft: { onUserLeft in
+
+ } onUserListUpdated: { onUserListUpdated in
+
+ } onAudioModesUpdated: { onAudioModesUpdated in
+
+ } onUserMuted: { onUserMuted in
+
+ } onCallSwitchedToVideo: { onCallSwitchedToVideo in
+
+ } onRecordingStarted: { onRecordingStarted in
+
+ } onRecordingStopped: { onRecordingStopped in
+
+ } onError: { error in
+ DispatchQueue.main.async {
+ self.dismiss(animated: true, completion: nil)
+ }
+ } onCallEnded: { ended in
+ DispatchQueue.main.async {
+ self.dismiss(animated: true, completion: nil)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+
+
+
+
+### Convert Push Notification payload to Message object
+
+CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` which will take the JSON received in The push notification as input, and return the corresponding `TextMessage`, `MediaMessage`,`CustomMessage` or `Call` object in return. Once the message object is received, you can use the entity as per your requirements.
+
+This code needs to be added to the `willPresent notification` method of the `UNUserNotificationCenterDelegate` delegate.
+
+
+
+```swift
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+ willPresent notification: UNNotification,
+ withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+ if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject = userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
+
+ if let baseMessage = CometChat.processMessage(dict).0 {
+ switch baseMessage.messageCategory {
+ case .message:
+ if let message = baseMessage as? BaseMessage {
+ switch message.messageType {
+ case .text:
+ print("text Messagge is: \((message as?TextMessage)?.stringValue())")
+ case .image:
+ print("image Messagge is: \((message as? MediaMessage)?.stringValue())")
+ case .video:
+ print("video Messagge is: \((message as? MediaMessage)?.stringValue())")
+ case .audio:
+ print("audio Messagge is: \((message as? MediaMessage)?.stringValue())")
+ case .file:
+ print("file Messagge is: \((message as? MediaMessage)?.stringValue())")
+ case .custom:
+ print("custom Messagge is: \((message as? MediaMessage)?.stringValue())")
+ case .groupMember: break
+ @unknown default:break}
+ }
+ case .action: break
+ case .call:
+ if let call = baseMessage as? Call {
+ print("call is: \(call.stringValue())")
+ }
+ case .custom:
+ if let customMessage = baseMessage as? CustomMessage {
+ print("customMessage is: \(customMessage.stringValue())")
+ }
+ @unknown default: break
+ }
+ }
+ }
+ completionHandler([.alert, .badge, .sound])
+ }
+
+extension String {
+ func stringTodictionary() -> [String:Any]? {
+ var dictonary:[String:Any]?
+ if let data = self.data(using: .utf8) {
+ do {
+ dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
+ if let myDictionary = dictonary
+ {
+ return myDictionary;
+ }
+ } catch let error as NSError {
+ print(error)
+ }
+ }
+ return dictonary;
+ }
+}
+```
+
+
+
+
diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx
index e3887164..046a9909 100644
--- a/notifications/ios-apns-push-notifications.mdx
+++ b/notifications/ios-apns-push-notifications.mdx
@@ -1,486 +1,753 @@
---
-title: "iOS APNs"
+title: "iOS APNs Push Notifications"
+description: "Implement APNs push notifications with CometChat UIKit for iOS, including CallKit integration for VoIP calls."
---
-Apple Push Notification service or APNs is used to send notifications to iOS devices. With this, you can also use Apple's CallKit for showing the call screen.
-
- iOS Push notifications sample app
-
- View on Github
+ Reference implementation of iOS UIKit, APNs and Push Notification Setup.
-## Get APNS Credentials
-
-The following steps in this section are written on the assumption that you already have an app ID assigned to your client app.
-
-### Step 1: Create a Certificate Signing Request
-
-To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
-
-1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
-
-
-
-
-
-2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button.
+## What this guide covers
-
-
-
-
-3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
-
-### Step 2: Create an SSL certificate
-
-1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
-2. Go to Certificates, Identifiers & Profiles.
+- CometChat dashboard setup (enable push, add APNs Device + APNs VoIP providers) with screenshots.
+- APNs + PushKit/CallKit wiring (tokens, delegates, CallKit).
+- Incoming message/call handling and deep links.
+- Payload customization and testing.
-
-
-
+{/* ## What you need first
-3. Create new Certificate by clicking on the + icon.
+- CometChat App ID, Region, Auth Key; Push Notifications with **APNs Device provider ID** and **APNs VoIP provider ID**.
+- Apple capabilities: Push Notifications, Background Modes (Remote notifications, Voice over IP), CallKit usage descriptions. Physical device required.
+- `.p8` APNs key (Key ID, Team ID, Bundle ID) or certificates. */}
-
-
-
+## How APNs + CometChat work together
-4. Under Services, select - Apple Push Notification services SSL (Sandbox & Production)
+- **APNs is the transport:** Apple issues the APNs device/VoIP tokens and delivers the payloads. No FCM bridge is involved.
+- **CometChat providers:** The APNs Device and APNs VoIP providers you add in the CometChat dashboard hold your APNs key/cert. When you call `CometChatNotifications.registerPushToken(..., .APNS_IOS_DEVICE / .APNS_IOS_VOIP, providerId)` after login, CometChat binds those tokens to the logged-in user and sends to APNs for you.
+- **Flow:** Permission prompt → APNs returns device + VoIP tokens → after `CometChat.login`, register both tokens with the matching provider IDs → CometChat sends to APNs → APNs delivers → `UNUserNotificationCenterDelegate` (and `PushKit`/`CallKit` for VoIP) surface the notification/tap.
-
-
-
+## 1. Enable push and add providers (CometChat Dashboard)
-5. Select your App ID from the dropdown.
+1) Go to **Notifications → Settings** and enable **Push Notifications**.
-
+
-6. Upload CSR file., upload the CSR file you created through the **Choose File** button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
+2) Click **Add Credentials**:
+ - Add an **APNs Device** provider (alerts) using your `.p8` key, Team ID, Key ID, and Bundle ID; copy the Provider ID.
+ - Add an **APNs VoIP** provider (calls) with the same `.p8` (recommended for CallKit reliability); copy the Provider ID.
-
+
-
-
-
-
-### Step 3: Export and update .p8 certificate
-
-1. To generate a .p8 key file, go to [Apple developer account page](https://developer.apple.com/account/), then select Certificates, IDs & Profiles.
-2. Select Keys and click on the "+" button to add a new key.
-3. In the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click "Continue" and click "Register".
-4. Then proceed to download the key file by clicking Download.
-5. Make note of the `Key ID`, `Team ID` and your `Bundle ID` for saving in the Extension's settings.
-
-**If you wish to use the .p12 certificate instead, do the following:**
-
-1. Type a name for the .p12 file and save it to your Mac.
-2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
-3. DO NOT provide an export password when prompted.
-4. The .p12 file will be required in the next step for uploading in the CometChat Dashboard.
-
-## Extension settings
-
-### Step 1: Enable the extension
-
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open the settings for this extension and save the following.
+Keep the provider IDs—you’ll paste them into your app constants.
-### Step 2: Save your settings
+## 2. Apple setup
-
-
-
+1) Capabilities: Push Notifications, Background Modes → Remote notifications & Voice over IP, CallKit usage descriptions in `Info.plist` (mic/camera).
+2) APNs Auth Key: generate `.p8` (or use cert), note **Key ID**, **Team ID**, and **Bundle ID**; upload to CometChat providers.
-
+
-On the Settings page you need to enter the following:
-
-1. **Set extension version**
-
- 1. The extension version has to be set to 'V2' or 'V1 & V2' in order to use APNs as the provider.
-
-2. **Select Platforms**
+## 3. Wiring APNs + PushKit/CallKit
- 1. You can select the platforms on which you wish to receive Push Notifications.
-
-3. **APNs Settings**
-
- 1. You can turn off the Production mode when you create a development build of your application.
- 2. Upload the .p8 or .p12 certificate exported in the previous step.
-
-4. **Push payload message options**
-
-
-
-
-
-The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
-
-* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
-
-5. **Notification Triggers**
-
- 1. Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
-
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
-
- 2. These are pretty self-explanatory and you can toggle them as per your requirement.
-
-## iOS App Setup
-
-### Initial Setup
-
-1. Call `CometChat.init()` method to initialize CometChat in your application. This needs to be called only once.
-2. The user has to be logged in using `CometChat.login()` method. On the success callback, register the token with the extension. Two tokens need to be registered, out of which one is APNs token and other is CallKit token: a. `CometChat.registerTokenForPushNotification(token: apnsToken, settings: ["voip":false])`\
- b. `CometChat.registerTokenForPushNotification(token: voipToken, settings: ["voip":true])`
+- From below code, copy `CometChatAPNsHelper.swift`, `CometChatPNHelper.swift`, and the two `AppDelegate` extensions (`AppDelegate+PN.swift` and `AppDelegate+VoIP.swift`) into your project.
+- These files implement APNs + PushKit/CallKit handling, notification presentation, tap and quick-reply actions, and call management.
+- Update bundle ID, team ID, and provider IDs (`AppConstants.PROVIDER_ID` etc.). Keep the `voip` push type.
-
-```swift
-let authKey = "XXXX XXXX XXXXX"
+
+```swift lines
+import Foundation
+import UIKit
+import CometChatSDK
+import CometChatUIKitSwift
+
+extension AppDelegate: UNUserNotificationCenterDelegate {
+
+ // MARK: - Foreground Notifications
+ func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+ print("willPresent notification: \(notification.request.content.userInfo)")
+ let userInfo = notification.request.content.userInfo
+
+ if CometChatPNHelper.shouldPresentNotification(userInfo: userInfo) == false {
+ print("Suppressing notification (user is in active chat)")
+ completionHandler([])
+ return
+ }
+
+ completionHandler([.banner, .badge, .sound])
+ }
-CometChat.login(UID: UID, authKey: authKey, onSuccess: { (current_user) in
- DispatchQueue.main.async {
- if let apnsToken = UserDefaults.standard.value(forKey: "apnsToken") as? String {
- print("APNS token is: \(apnsToken)")
- CometChat.registerTokenForPushNotification(token: apnsToken, settings: ["voip":false]) { (success) in
- print("onSuccess to registerTokenForPushNotification: \(success)")
- DispatchQueue.main.async {self.activityIndicator.stopAnimating()
- print("login success with : \(current_user.stringValue())")
- self.performSegue(withIdentifier: "presentPushNotification", sender: nil)
- }
- } onError: { (error) in
- print("error to registerTokenForPushNotification")
- }
- }
- if let voipToken = UserDefaults.standard.value(forKey: "voipToken") as? String {
- print("VOIP token is: \(voipToken)")
- CometChat.registerTokenForPushNotification(token: voipToken, settings: ["voip":true]) { (success) in
- print("onSuccess to registerTokenForPushNotification: \(success)")
- DispatchQueue.main.async {self.activityIndicator.stopAnimating()
- print("login success with : \(current_user.stringValue())")
- self.performSegue(withIdentifier: "presentPushNotification", sender: nil)
- }
- } onError: { (error) in
- print("error to registerTokenForPushNotification")
+ // MARK: - Notification Tap/Interaction
+ func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
+
+ let userInfo = response.notification.request.content.userInfo
+ print("User tapped notification: \(userInfo)")
+
+ if response.actionIdentifier == "REPLY_ACTION" {
+ if let textResponse = response as? UNTextInputNotificationResponse {
+ let userReply = textResponse.userText
+ print("Quick reply: \(userReply)")
+ CometChatPNHelper.handleQuickReplyActionOnNotification(userInfo: userInfo, text: userReply, completionHandler: completionHandler)
}
- }
- }
+ completionHandler()
+ return
}
- }) { (error) in
- print("error while login", error);
- }
+
+ CometChatPNHelper.handleTapActionOnNotification(userInfo: userInfo, completionHandler: completionHandler)
}
+}
```
+
+
+ ```swift lines
+#if canImport(CometChatCallsSDK)
-
-
-
-
-3. Import PushKit and CallKit in AppDelegate.Swift file.
-
-
-
-```swift
+import Foundation
import PushKit
import CallKit
-```
-
-
+import AVFoundation
+import CometChatSDK
+import CometChatCallsSDK
-
+extension AppDelegate: PKPushRegistryDelegate, CXProviderDelegate {
-### Receive Push Notifications
+ // MARK: - VoIP Push Token Updates
-1. Registering for the APNs notifications
+ func pushRegistry(
+ _ registry: PKPushRegistry,
+ didUpdate pushCredentials: PKPushCredentials,
+ for type: PKPushType
+ ) {
+ print("VoIP token updated for type: \(type.rawValue)")
+ cometchatAPNsHelper.registerForVoIPCalls(pushCredentials: pushCredentials)
+ }
-
-
-```swift
-var window: UIWindow?
-var uuid: UUID?
-var activeCall: Call?
-var cancelCall: Bool = true
-var onCall = true
-var callController = CXCallController()
-let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
-var provider: CXProvider? = nil
-
-func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
- self.voipRegistration()
- // [START register_for_notifications]
-if #available(iOS 10.0, *) {
-UNUserNotificationCenter.current().delegate = self
-let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
-UNUserNotificationCenter.current().requestAuthorization(
-options: authOptions,
-completionHandler: {_, _ in })
-} else {
-let settings: UIUserNotificationSettings =
-UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
-application.registerUserNotificationSettings(settings)
-}
-application.registerForRemoteNotifications()
-// [END register_for_notifications]
-return true
-}
-// Register for VoIP notifications
-func voipRegistration() {
-// Create a push registry object
-let mainQueue = DispatchQueue.main
-let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
-voipRegistry.delegate = self
-voipRegistry.desiredPushTypes = [PKPushType.voIP]
-}
-```
+ func pushRegistry(
+ _ registry: PKPushRegistry,
+ didInvalidatePushTokenFor type: PKPushType
+ ) {
+ print("VoIP push token invalidated for type: \(type.rawValue)")
+ initializePushKit()
+ refreshPushCredentials()
+ }
-
+ // MARK: - PushKit Setup
+
+ func initializePushKit() {
+ if pushRegistry == nil {
+ let registry = PKPushRegistry(queue: DispatchQueue.main)
+ registry.delegate = self
+ registry.desiredPushTypes = [.voIP]
+ pushRegistry = registry
+ print("Push registry initialized")
+ } else {
+ print("Push registry already initialized")
+ }
+ }
-
+ func refreshPushCredentials() {
+ guard let registry = pushRegistry else {
+ print("Push registry is nil")
+ return
+ }
-2. Add AppDelegate extension for receiving Push Notifications
+ registry.desiredPushTypes = []
+ registry.desiredPushTypes = [.voIP]
+ print("VoIP token refreshed")
+ }
-
-
-```swift
-extension AppDelegate : UNUserNotificationCenterDelegate {
-// Receive displayed notifications for iOS 10 devices.
-func userNotificationCenter(_ center: UNUserNotificationCenter,
-willPresent notification: UNNotification,
-withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
-print("willPresent notification: \(notification.request.content.userInfo)")
-if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject =
-userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
-if let baseMessage = CometChat.processMessage(dict).0 {
-switch baseMessage.messageCategory {
-case .message:
-if let message = baseMessage as? BaseMessage {
-switch message.messageType {
-case .text:
-print("text Messagge is: \((message as? TextMessage)?.stringValue())")
-case .image:
-print("image Messagge is: \((message as? MediaMessage)?.stringValue())")
-case .video:
-print("video Messagge is: \((message as? MediaMessage)?.stringValue())")
-case .audio:
-print("audio Messagge is: \((message as? MediaMessage)?.stringValue())")
-case .file:
-print("file Messagge is: \((message as? MediaMessage)?.stringValue())")
-case .custom:
-print("custom Messagge is: \((message as? MediaMessage)?.stringValue())")
-case .groupMember:
-break
-@unknown default:
-break
-}
-}
-case .action: break
-case .call:
-if let call = baseMessage as? Call {
-print("call is: \(call.stringValue())")
-}
-case .custom:
-if let customMessage = baseMessage as? CustomMessage {
-print("customMessage is: \(customMessage.stringValue())")
-}
-@unknown default: break
-}
-}
-}
-completionHandler([.alert, .badge, .sound])
-}
-func userNotificationCenter(_ center: UNUserNotificationCenter,
-didReceive response: UNNotificationResponse,
-withCompletionHandler completionHandler: @escaping () -> Void) {
-let notification = response.notification.request.content.userInfo
-print("notification is 11: \(notification)")
-completionHandler()
-}
-func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
-let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
-print("Device Token : ",token)
-let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
-print("Device Token 11: ",hexString)
-UserDefaults.standard.set(hexString, forKey: "apnsToken")
-CometChat.registerTokenForPushNotification(token: hexString, settings: ["voip":false]) { (success) in
-print("registerTokenForPushNotification voip: \(success)")
-} onError: { (error) in
-print("registerTokenForPushNotification error: \(error)")
-}
-}
-}
-```
+ // MARK: - Incoming VoIP Push
+
+ func pushRegistry(
+ _ registry: PKPushRegistry,
+ didReceiveIncomingPushWith payload: PKPushPayload,
+ for type: PKPushType,
+ completion: @escaping () -> Void
+ ) {
+ print("Incoming VoIP push received")
+ let provider = cometchatAPNsHelper.didReceiveIncomingPushWith(payload: payload)
+ provider?.setDelegate(self, queue: nil)
+ completion()
+ }
-
+ // MARK: - CallKit Delegates
-
+ func providerDidReset(_ provider: CXProvider) {
+ print("CallKit provider did reset")
+ cometchatAPNsHelper.onProviderDidReset(provider: provider)
+ }
-3. Add AppDelegate extension for VOIP notifications. Launch CallKit screen when the VOIP notification is received. Once the CallKit screen is displayed, you can Accept or Reject the CometChat call accordingly.
+ func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
+ print("User answered call")
+
+ // CRITICAL: Configure audio session BEFORE answering
+ configureAudioSession()
+
+ cometchatAPNsHelper.onAnswerCallAction(action: action)
+ }
-
-
-```swift
-// MARK: CallKit & PushKit
-extension AppDelegate: PKPushRegistryDelegate , CXProviderDelegate {
+ func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
+ print("User ended call")
+ cometchatAPNsHelper.onEndCallAction(action: action)
+ action.fulfill()
+ }
+ func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
+ print("User toggled mute: \(action.isMuted)")
+ CometChatCalls.audioMuted(action.isMuted)
+ action.fulfill()
+ }
+
+ // MARK: - CRITICAL: Audio Session Delegates (MISSING IN YOUR CODE)
+
+ /// Called when CallKit activates the audio session
+ func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
+ print("Audio session activated")
+
+ // Configure audio session for VoIP
+ configureAudioSession()
+
+ // Removed CometChatCalls.startAudioSession() as per instructions
+ }
+
+ /// Called when CallKit deactivates the audio session
+ func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
+ print("Audio session deactivated")
+
+ // Removed CometChatCalls.stopAudioSession() as per instructions
+ }
+
+ // MARK: - Audio Session Configuration
+
+ /// Configure AVAudioSession for VoIP calls
+ private func configureAudioSession() {
+ let audioSession = AVAudioSession.sharedInstance()
+
+ do {
+ // Set category for VoIP with speaker and bluetooth support
+ try audioSession.setCategory(
+ .playAndRecord,
+ mode: .voiceChat,
+ options: [.allowBluetooth, .allowBluetoothA2DP]
+ )
+
+ // Activate the session
+ try audioSession.setActive(true)
+
+ print("Audio session configured successfully")
+
+ } catch {
+ print("Failed to configure audio session: \(error.localizedDescription)")
+ }
+ }
+}
-func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
- let deviceToken = pushCredentials.token.reduce("", {$0 + String(format: "%02X", $1) })
- print("voip token is: \(deviceToken)")
- UserDefaults.standard.set(deviceToken, forKey: "voipToken")
- CometChat.registerTokenForPushNotification(token: deviceToken, settings: ["voip":true]) { (success) in
- print("registerTokenForPushNotification voip: \(success)")
- } onError: { (error) in
- print("registerTokenForPushNotification error: \(error)")
- }
- }
-
-func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
- if let userInfo = payload.dictionaryPayload as? [String : Any], let messageObject =
- userInfo["message"], let dict = messageObject as? [String : Any] {
-
- if let baseMessage = CometChat.processMessage(dict).0 {
- switch baseMessage.messageCategory {
- case .message: break
- case .action: break
- case .call:
- if let call = baseMessage as? Call {
- switch call.callStatus {
- case .initiated:
- self.activeCall = call
- self.uuid = UUID()
- if let name = (call.sender)?.name {
- let config = CXProviderConfiguration(localizedName: "APNS + Callkit")
- config.iconTemplateImageData = #imageLiteral(resourceName: "your_app_icon").pngData()
- config.includesCallsInRecents = false
- config.ringtoneSound = "ringtone.caf"
- config.supportsVideo = true
- provider = CXProvider(configuration: config)
- provider?.setDelegate(self, queue: nil)
- let update = CXCallUpdate()
- update.remoteHandle = CXHandle(type: .generic, value: name.capitalized)
- if call.callType == .video {
- update.hasVideo = true
- }else{
- update.hasVideo = false
- }
- provider?.reportNewIncomingCall(with: self.uuid!, update: update, completion: { error in
- if error == nil {
- self.configureAudioSession()
- }
- })
- }
- case .ongoing, .unanswered, .rejected, .busy, .cancelled:
- if self.activeCall != nil {
- if self.cancelCall {
- self.end(uuid: self.uuid!)
- }
- }
- case .ended: break
- @unknown default: break }
+#endif
+ ```
+
+
+```swift lines
+import Foundation
+import UIKit
+import CometChatSDK
+import CometChatUIKitSwift
+import PushKit
+import CallKit
+import AVFAudio
+
+#if canImport(CometChatCallsSDK)
+import CometChatCallsSDK
+#endif
+
+class CometChatAPNsHelper {
+
+ var uuid: UUID?
+ var activeCall: Call?
+ var cancelCall: Bool = true
+ var onCall = true
+ var callController = CXCallController()
+ let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
+ var provider: CXProvider? = nil
+
+ // MARK: - Configure Push Notifications
+ public func configurePushNotification(application: UIApplication, delegate: AppDelegate) {
+
+ print("Configuring Push Notifications...")
+
+ let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
+ UNUserNotificationCenter.current().requestAuthorization(
+ options: authOptions,
+ completionHandler: { granted, error in
+ print("Push notification authorization granted: \(granted)")
+ if let error = error {
+ print("Authorization error: \(error.localizedDescription)")
+ } else if granted {
+ print("User granted notification permissions")
+ // Register for remote notifications on main thread
+ DispatchQueue.main.async {
+ UIApplication.shared.registerForRemoteNotifications()
}
- case .custom: break
- @unknown default: break
+ } else {
+ print("User denied notification permissions")
}
+ })
+
+ // Define the reply action
+ let replyAction = UNTextInputNotificationAction(
+ identifier: "REPLY_ACTION",
+ title: "Reply",
+ options: [],
+ textInputButtonTitle: "Send",
+ textInputPlaceholder: "Type your reply here"
+ )
+
+ // Define the notification category
+ let messageCategory = UNNotificationCategory(
+ identifier: "MESSAGE_CATEGORY",
+ actions: [replyAction],
+ intentIdentifiers: [],
+ options: []
+ )
+
+ // Register the category
+ UNUserNotificationCenter.current().setNotificationCategories([messageCategory])
+
+ // Add login listener
+ CometChat.addLoginListener("loginlistener-pnToken-register-login", self)
+
+ #if canImport(CometChatCallsSDK)
+ let voipRegistry: PKPushRegistry = PKPushRegistry(queue: DispatchQueue.main)
+ voipRegistry.delegate = (delegate as? PKPushRegistryDelegate)
+ voipRegistry.desiredPushTypes = [PKPushType.voIP]
+ CometChatCallEvents.addListener("loginlistener-pnToken-register-login", self)
+ #endif
+ }
+
+ // MARK: - Register APNs Token
+ public func registerTokenForPushNotification(deviceToken: Data) {
+ guard CometChat.getLoggedInUser() != nil else {
+ print("Cannot register token: User not logged in")
+ return
+ }
+
+ let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
+ UserDefaults.standard.set(hexString, forKey: "apnspuToken")
+ print("APNs token: \(hexString)")
+
+ CometChatNotifications.registerPushToken(
+ pushToken: hexString,
+ platform: CometChatNotifications.PushPlatforms.APNS_IOS_DEVICE,
+ providerId: AppConstants.PROVIDER_ID,
+ onSuccess: { success in
+ print("APNs token registered successfully: \(success)")
+ },
+ onError: { error in
+ print("APNs token registration failed: \(error.errorCode) - \(error.errorDescription)")
+ }
+ )
+ }
+
+ // MARK: - Register Pending Token (After Login)
+ private func registerPendingTokenIfNeeded() {
+ if let pendingToken = UserDefaults.standard.string(forKey: "pendingAPNsToken") {
+ print("Registering pending APNs token after login...")
+ if let tokenData = hexStringToData(pendingToken) {
+ registerTokenForPushNotification(deviceToken: tokenData)
+ UserDefaults.standard.removeObject(forKey: "pendingAPNsToken")
+ }
+ }
+ }
+
+ private func hexStringToData(_ string: String) -> Data? {
+ let len = string.count / 2
+ var data = Data(capacity: len)
+ for i in 0.. CXProvider? {
+ guard let sender = payload.dictionaryPayload["sender"] as? String,
+ let senderName = payload.dictionaryPayload["senderName"] as? String,
+ let body = payload.dictionaryPayload["body"] as? String,
+ let callAction = payload.dictionaryPayload["callAction"] as? String,
+ let receiver = payload.dictionaryPayload["receiver"] as? String,
+ let type = payload.dictionaryPayload["type"] as? String,
+ let callType = payload.dictionaryPayload["callType"] as? String,
+ let sessionId = payload.dictionaryPayload["sessionId"] as? String,
+ let conversationId = payload.dictionaryPayload["conversationId"] as? String else {
+ print("Incomplete VoIP payload")
+ return nil
+ }
+
+ let applicationState = UIApplication.shared.applicationState
+ print("VoIP push received - Action: \(callAction), State: \(applicationState.rawValue)")
+
+ if type == "call" {
+ switch callAction {
+ case "initiated":
+ switch applicationState {
+ case .active:
+ if CometChat.getActiveCall() != nil {
+ print("User already on a call, rejecting with busy...")
+ CometChat.rejectCall(sessionID: sessionId, status: .busy, onSuccess: { rejectedCall in
+ print("Rejected incoming call with busy status")
+ }, onError: { error in
+ print("Failed to reject with busy: \(error?.errorDescription ?? "")")
+ })
+ return nil
+ } else {
+ return updatedInitiateCall(sender: sender, senderName: senderName, body: body, callAction: callAction, receiver: receiver, callType: callType, sessionId: sessionId, conversationId: conversationId)
+ }
+ case .inactive, .background:
+ return updatedInitiateCall(sender: sender, senderName: senderName, body: body, callAction: callAction, receiver: receiver, callType: callType, sessionId: sessionId, conversationId: conversationId)
+ @unknown default:
+ break
+ }
+
+ case "ongoing":
+ print("Call ongoing")
+ break
+
+ case "unanswered":
+ provider?.reportCall(with: uuid!, endedAt: Date(), reason: .unanswered)
+ handleMissedCallNotification(payload: payload.dictionaryPayload)
+
+ case "rejected":
+ provider?.reportCall(with: uuid!, endedAt: Date(), reason: .unanswered)
+
+ case "busy":
+ if let uuid = uuid {
+ provider?.reportCall(with: uuid, endedAt: Date(), reason: .unanswered)
+ self.uuid = nil
+ }
+
+ case "cancelled":
+ provider?.reportCall(with: uuid!, endedAt: Date(), reason: .failed)
+ handleMissedCallNotification(payload: payload.dictionaryPayload)
+
+ case "ended":
+ provider?.reportCall(with: uuid!, endedAt: Date(), reason: .remoteEnded)
+
+ default:
+ provider?.reportCall(with: uuid!, endedAt: Date(), reason: .remoteEnded)
+ }
+ }
+
+ return nil
+ }
+
+ public func onAnswerCallAction(action: CXAnswerCallAction) {
+ if activeCall != nil {
startCall()
}
action.fulfill()
}
-
-func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
- NotificationCenter.default.post(name: NSNotification.Name(rawValue: "didRejectButtonPressed"), object: nil, userInfo: nil)
- end(uuid: self.uuid!)
- onCall = true
- if let activeCall = activeCall {
- CometChat.rejectCall(sessionID: activeCall.sessionID ?? "", status: .rejected, onSuccess: {(rejectedCall) in
- DispatchQueue.main.async {
- CometChatSnackBoard.display(message: "CALL_REJECTED".localized(), mode: .info, duration: .short)
+
+ private func updatedInitiateCall(sender: String, senderName: String, body: String, callAction: String, receiver: String, callType: String, sessionId: String, conversationId: String) -> CXProvider? {
+
+ let callTypeValue: CometChat.CallType = callType == "audio" ? .audio : .video
+ let receiverType: CometChat.ReceiverType = conversationId.contains("group") ? .group : .user
+ let call = Call(receiverId: receiver, callType: callTypeValue, receiverType: receiverType)
+ call.sessionID = sessionId
+ call.callStatus = .initiated
+ call.initiatedAt = Date().timeIntervalSince1970
+ call.callInitiator = User(uid: sender, name: senderName)
+ call.callType = callTypeValue
+ call.callReceiver = User(uid: receiver, name: receiver)
+
+ activeCall = call
+ uuid = UUID()
+
+ let callerName = senderName
+ let config = CXProviderConfiguration(localizedName: "APNS + Callkit")
+ config.iconTemplateImageData = UIImage(named: "AppIcon")?.pngData()
+ config.includesCallsInRecents = true
+ config.ringtoneSound = "ringtone.caf"
+ config.supportsVideo = true
+
+ provider = CXProvider(configuration: config)
+
+ let update = CXCallUpdate()
+ update.remoteHandle = CXHandle(type: .generic, value: callerName.capitalized)
+ update.hasVideo = callType == "video"
+
+ provider?.reportNewIncomingCall(with: uuid!, update: update, completion: { error in
+ if error == nil {
+ self.configureAudioSession()
+ }
+ })
+
+ return provider!
+ }
+
+ private func configureAudioSession() {
+ do {
+ try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: [.mixWithOthers, .allowBluetooth, .defaultToSpeaker])
+ try AVAudioSession.sharedInstance().setActive(true)
+ } catch let error as NSError {
+ print("Audio session error: \(error)")
+ }
+ }
+
+ private func startCall() {
+ let cometChatOngoingCall = CometChatOngoingCall()
+
+ CometChat.acceptCall(sessionID: activeCall?.sessionID ?? "") { call in
+ DispatchQueue.main.async {
+ let isAudioCall = (self.activeCall?.callType == .audio)
+ var callSettingsBuilder = CometChatCallsSDK.CallSettingsBuilder()
+ callSettingsBuilder = callSettingsBuilder.setIsAudioOnly(isAudioCall)
+ cometChatOngoingCall.set(callSettingsBuilder: callSettingsBuilder)
+ cometChatOngoingCall.set(callWorkFlow: .defaultCalling)
+ cometChatOngoingCall.set(sessionId: call?.sessionID ?? "")
+ cometChatOngoingCall.modalPresentationStyle = .fullScreen
+
+ if let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate,
+ let window = sceneDelegate.window,
+ let rootViewController = window.rootViewController {
+ var currentController = rootViewController
+ while let presentedController = currentController.presentedViewController {
+ currentController = presentedController
+ }
+ currentController.present(cometChatOngoingCall, animated: true)
}
- }) { (error) in
+ }
+
+ cometChatOngoingCall.setOnCallEnded { [weak self] call in
DispatchQueue.main.async {
- if let errorMessage = error?.errorDescription {
- CometChatSnackBoard.display(message: "CALL_REJECTED".localized(), mode: .info, duration: .short)
+ if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
+ if let rootViewController = scene.windows.first?.rootViewController {
+ self?.dismissCometChatIncomingCall(from: rootViewController)
+ self?.reloadViewController(rootViewController)
+ }
}
}
+ self?.provider?.reportCall(with: self?.uuid ?? UUID(), endedAt: Date(), reason: .remoteEnded)
}
- provider.reportCall(with: self.uuid!, endedAt: Date(), reason: .remoteEnded)
- }
- action.fail()
+ } onError: { error in
+ print("Error accepting call: \(error?.errorDescription ?? "")")
+ }
}
-
- func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
- print(#function)
+
+ func onCallEnded(call: CometChatSDK.Call) {
+ guard let uuid = uuid else { return }
+
+ if activeCall != nil {
+ let transaction = CXTransaction(action: CXEndCallAction(call: uuid))
+ callController.request(transaction, completion: { error in })
+ activeCall = nil
+ }
+
+ DispatchQueue.main.sync { [self] in
+ if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
+ if let rootViewController = scene.windows.first?.rootViewController {
+ dismissCometChatIncomingCall(from: rootViewController)
+ self.reloadViewController(rootViewController)
+ }
+ }
+ }
}
-
- func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
- action.fulfill()
- print(#function)
+
+ func onCallInitiated(call: CometChatSDK.Call) {
+ let callerName = (call.callReceiver as? User)?.name
+ callController = CXCallController()
+ uuid = UUID()
+
+ let transactionCallStart = CXTransaction(action: CXStartCallAction(call: uuid!, handle: CXHandle(type: .generic, value: callerName ?? "")))
+ callController.request(transactionCallStart, completion: { error in })
}
-
- func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
- print(#function)
+
+ private func dismissCometChatIncomingCall(from viewController: UIViewController) {
+ if let presentedViewController = viewController.presentedViewController {
+ if presentedViewController is CometChatIncomingCall {
+ presentedViewController.dismiss(animated: false, completion: nil)
+ } else {
+ dismissCometChatIncomingCall(from: presentedViewController)
+ }
+ }
}
-
- func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
- print(#function)
+
+ public func onProviderDidReset(provider: CXProvider) {
+ if let uuid = self.uuid {
+ onCall = true
+ provider.reportCall(with: uuid, endedAt: Date(), reason: .unanswered)
+ }
}
-
- func end(uuid: UUID) {
- print("endUUID",uuid)
- let endCallAction = CXEndCallAction(call: uuid)
+
+ public func onEndCallAction(action: CXEndCallAction) {
+ let endCallAction = CXEndCallAction(call: uuid!)
let transaction = CXTransaction()
transaction.addAction(endCallAction)
- requestTransaction(transaction, action: "")
- }
-
- func setHeld(uuid: UUID, onHold: Bool) {
- print("setHeld",uuid)
- let setHeldCallAction = CXSetHeldCallAction(call: uuid, onHold: onHold)
- let transaction = CXTransaction()
- transaction.addAction(setHeldCallAction)
-
- requestTransaction(transaction, action: "")
- }
-
- internal func requestTransaction(_ transaction: CXTransaction, action: String = "") {
+
callController.request(transaction) { error in
if let error = error {
print("Error requesting transaction: \(error)")
@@ -488,198 +755,428 @@ func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
print("Requested transaction successfully")
}
}
+
+ if let activeCall = activeCall {
+ if CometChat.getActiveCall() == nil || (CometChat.getActiveCall()?.callStatus == .initiated && CometChat.getActiveCall()?.callInitiator != CometChat.getLoggedInUser()) {
+ CometChat.rejectCall(sessionID: activeCall.sessionID ?? "", status: .rejected, onSuccess: { [self] (rejectedCall) in
+ action.fulfill()
+ print("CallKit: Reject call success")
+ DispatchQueue.main.async { [self] in
+ if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
+ if let rootViewController = scene.windows.first?.rootViewController {
+ self.dismissCometChatIncomingCall(from: rootViewController)
+ self.reloadViewController(rootViewController)
+ }
+ }
+ if let uuid = uuid {
+ provider?.reportCall(with: uuid, endedAt: Date(), reason: .remoteEnded)
+ self.uuid = nil
+ }
+ }
+ }) { (error) in
+ print("CallKit: Reject call failed: \(error?.errorDescription ?? "")")
+ }
+ } else {
+ CometChat.endCall(sessionID: CometChat.getActiveCall()?.sessionID ?? "") { call in
+ CometChatCalls.endSession()
+ action.fulfill()
+ print("CallKit: End call success")
+ DispatchQueue.main.async { [self] in
+ if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
+ if let rootViewController = scene.windows.first?.rootViewController {
+ self.dismissCometChatIncomingCall(from: rootViewController)
+ self.reloadViewController(rootViewController)
+ }
+ }
+ }
+ } onError: { error in
+ print("CallKit: End call failed: \(error?.errorDescription ?? "")")
+ }
+ }
+ }
}
+}
- public func startCall(){
- let activeCall = CometChatCall()
- cancelCall = false
- activeCall.modalPresentationStyle = .fullScreen
- if let window = UIApplication.shared.windows.first , let rootViewController = window.rootViewController {
- var currentController = rootViewController
- while let presentedController = currentController.presentedViewController {
- currentController = presentedController
- }
- currentController.present(activeCall, animated: true, completion: nil)
+extension CometChatAPNsHelper: CometChatCallEventListener {
+ func ccCallEnded(call: Call) {
+ guard let uuid = uuid else { return }
+
+ if activeCall != nil {
+ let transactionCallAccepted = CXTransaction(action: CXEndCallAction(call: uuid))
+ callController.request(transactionCallAccepted, completion: { error in })
+ activeCall = nil
}
}
}
-```
-
+#endif
+ ```
+
+
+ ```swift lines
+import Foundation
+import UIKit
+import CometChatSDK
+import CometChatUIKitSwift
-
+class CometChatPNHelper {
-## Miscellaneous
+ let cometchatAPNsHelper = CometChatAPNsHelper()
+ static var currentActiveUser: CometChatSDK.User?
+ static var currentActiveGroup: CometChatSDK.Group?
-### Create view controller for Calls
+ static func handleTapActionOnNotification(userInfo: [AnyHashable: Any], completionHandler: @escaping () -> Void) {
+ guard let notificationType = userInfo["type"] as? String,
+ let receiverType = userInfo["receiverType"] as? String else {
+ print("Notification type or receiver type not found in payload")
+ completionHandler()
+ return
+ }
-Create a viewController which will start the call when the user starts the call.
+ switch notificationType {
+ case "chat":
+ if receiverType == "user" {
+ handleChatNotification(userInfo: userInfo)
+ } else if receiverType == "group" {
+ handleGroupChatNotification(userInfo: userInfo)
+ } else {
+ print("Invalid receiver type for chat notification")
+ }
-
-
-```swift
-import UIKit
-import CometChatPro
-import CallKit
+ case "call":
+ if receiverType == "user" {
+ handleChatNotification(userInfo: userInfo)
+ } else if receiverType == "group" {
+ handleGroupChatNotification(userInfo: userInfo)
+ } else {
+ print("Invalid receiver type for call notification")
+ }
+
+ default:
+ navigateToDefaultScreen()
+ }
+
+ completionHandler()
+ }
+
+ static func handleQuickReplyActionOnNotification(userInfo: [AnyHashable: Any], text: String, completionHandler: @escaping () -> Void) {
+ guard let notificationType = userInfo["type"] as? String,
+ let receiverType = userInfo["receiverType"] as? String else {
+ print("Notification type or receiver type not found in payload")
+ completionHandler()
+ return
+ }
-class CometChatCall: UIViewController {
+ switch notificationType {
+ case "chat":
+ if receiverType == "user" {
+ replyToUserWith(message: text, userInfo: userInfo)
+ } else if receiverType == "group" {
+ replyToGroupWith(message: text, userInfo: userInfo)
+ } else {
+ print("Invalid receiver type for chat notification")
+ }
+ default:
+ break
+ }
+ completionHandler()
+ }
- override func viewDidLoad() {
- super.viewDidLoad()
- if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
- if let call = appDelegate.activeCall {
+ static func navigateToViewController(_ viewController: UIViewController) {
- if (call.callInitiator as? User)?.uid != CometChat.getLoggedInUser()?.uid {
+ guard let window = UIApplication.shared.windows.first else {
+ print("Window not found")
+ return
+ }
- CometChat.acceptCall(sessionID: call.sessionID ?? "") { acceptedCall in
+ if let navigationController = window.rootViewController as? UINavigationController {
+ if let currentViewController = navigationController.viewControllers.last,
+ currentViewController.description == viewController.description {
+ print("Already in same view")
+ return
- DispatchQueue.main.async {
- let callSettings = CallSettings.CallSettingsBuilder(callView: self.view, sessionId: acceptedCall?.sessionID ?? "").setMode(mode: .MODE_SINGLE).build()
+ }
+ navigationController.popViewController(animated: false)
+ navigationController.pushViewController(viewController, animated: false)
+ } else {
+ print("Root view controller is not a UINavigationController")
+ }
- CometChat.startCall(callSettings: callSettings) { userJoined in
- appDelegate.onCall = true
- } onUserLeft: { onUserLeft in
+ }
- } onUserListUpdated: { onUserListUpdated in
+ static func replyToUserWith(message text: String, userInfo: [AnyHashable: Any], withParentId: Int? = nil) {
+ guard let sender = userInfo["sender"] as? String,
+ let senderName = userInfo["senderName"] as? String else {
+ print("Sender information missing in payload")
+ return
+ }
- } onAudioModesUpdated: { onAudioModesUpdated in
+ let textMessage = TextMessage(receiverUid: sender, text: text, receiverType: .user)
+ if let parentID = withParentId {
+ textMessage.parentMessageId = parentID
+ }
+ CometChatUIKit.sendTextMessage(message: textMessage)
+ }
- } onUserMuted: { onUserMuted in
+ static func replyToGroupWith(message text: String, userInfo: [AnyHashable: Any], withParentId: Int? = nil) {
+ guard let groupID = userInfo["receiver"] as? String,
+ let groupName = userInfo["receiverName"] as? String else {
+ print("Group information missing in payload")
+ return
+ }
- } onCallSwitchedToVideo: { onCallSwitchedToVideo in
+ let textMessage = TextMessage(receiverUid: groupID, text: text, receiverType: .group)
+ if let parentID = withParentId {
+ textMessage.parentMessageId = parentID
+ }
+ CometChatUIKit.sendTextMessage(message: textMessage)
+ }
- } onRecordingStarted: { onRecordingStarted in
+ static func handleChatNotification(userInfo: [AnyHashable: Any]) {
+ guard let sender = userInfo["sender"] as? String,
+ let senderName = userInfo["senderName"] as? String else {
+ print("Sender information missing in payload")
+ return
+ }
- } onRecordingStopped: { onRecordingStopped in
+ let senderUser = User(uid: sender, name: senderName)
+ senderUser.avatar = userInfo["senderAvatar"] as? String
- } onError: { error in
- DispatchQueue.main.async {
- self.dismiss(animated: true, completion: nil)
- }
- } onCallEnded: { ended in
- DispatchQueue.main.async {
- var str = ""
- if let uuuid = appDelegate.uuid {
- print("CometChatCalls", uuuid)
- }
- self.dismiss(animated: true, completion: nil)
- self.dismiss(animated: true)
- }
- }
- }
- } onError: { error in
+ getUser(forUID: sender) { retrievedUser in
+ DispatchQueue.main.async {
+ if let user = retrievedUser {
+ senderUser.status = user.status
+ } else {
+ print("Failed to retrieve user status")
+ }
- }
- }else{
+ let chatViewController = MessagesVC()
+ chatViewController.user = retrievedUser
+ self.navigateToViewController(chatViewController)
+ }
- let callSettings = CallSettings.CallSettingsBuilder(callView: self.view, sessionId: call.sessionID ?? "").setMode(mode: .MODE_SINGLE).build()
+ }
+ }
- CometChat.startCall(callSettings: callSettings) { userJoined in
- } onUserLeft: { onUserLeft in
+ static func handleGroupChatNotification(userInfo: [AnyHashable: Any]) {
+ guard let groupID = userInfo["receiver"] as? String,
+ let groupName = userInfo["receiverName"] as? String else {
+ print("Group information missing in payload")
+ return
+ }
- } onUserListUpdated: { onUserListUpdated in
+ let groupUser = Group(guid: groupID, name: groupName, groupType: .private, password: nil)
- } onAudioModesUpdated: { onAudioModesUpdated in
+ self.getGroup(for: groupUser, guid: groupID) { fetchedGroup in
+ DispatchQueue.main.async {
+ if let group = fetchedGroup {
+ groupUser.membersCount = group.membersCount
+ groupUser.icon = group.icon
+ } else {
+ print("Failed to fetch group members count")
+ }
+ let chatViewController = MessagesVC()
+ chatViewController.group = fetchedGroup
+ self.navigateToViewController(chatViewController)
+ }
+ }
+ }
- } onUserMuted: { onUserMuted in
+ static func handleCallNotification(userInfo: [AnyHashable: Any]) {
+ guard let sender = userInfo["sender"] as? String,
+ let senderName = userInfo["senderName"] as? String else {
+ print("Sender information missing in payload")
+ return
+ }
- } onCallSwitchedToVideo: { onCallSwitchedToVideo in
+ let user = User(uid: sender, name: senderName)
+ user.avatar = userInfo["senderAvatar"] as? String
+ DispatchQueue.main.async {
+ let callViewController = MessagesVC()
+ callViewController.user = user
+ CometChatPNHelper.navigateToViewController(callViewController)
+ }
+ }
- } onRecordingStarted: { onRecordingStarted in
+ static func handleGroupCallNotification(userInfo: [AnyHashable: Any]) {
+ guard let groupID = userInfo["receiver"] as? String,
+ let groupName = userInfo["receiverName"] as? String else {
+ print("Group information missing in payload")
+ return
+ }
- } onRecordingStopped: { onRecordingStopped in
+ let groupUser = Group(guid: groupID, name: groupName, groupType: .private, password: nil)
+ groupUser.icon = userInfo["receiverAvatar"] as? String
+ DispatchQueue.main.async {
- } onError: { error in
- DispatchQueue.main.async {
- self.dismiss(animated: true, completion: nil)
- }
- } onCallEnded: { ended in
- DispatchQueue.main.async {
- self.dismiss(animated: true, completion: nil)
- }
- }
- }
- }
- }
+ let callViewController = MessagesVC()
+ callViewController.group = groupUser
+ CometChatPNHelper.navigateToViewController(callViewController)
}
-}
-```
+ }
-
+ static func navigateToDefaultScreen() {
+ DispatchQueue.main.async {
+ let defaultViewController = MessagesVC()
-
+ guard let window = UIApplication.shared.windows.first else {
+ print("Window not found")
+ return
+ }
-### Convert Push Notification payload to Message object
+ if let navigationController = window.rootViewController as? UINavigationController {
+ navigationController.pushViewController(defaultViewController, animated: true)
+ } else {
+ print("Root view controller is not a UINavigationController")
+ }
+ }
+ }
+ static func getUser(forUID uid: String, completionHandler: @escaping (CometChatSDK.User?) -> Void) {
+ CometChat.getUser(UID: uid, onSuccess: { user in
+ let user = user
+ completionHandler(user)
+ }) { error in
+ print("User fetching failed with error: \(error?.errorDescription ?? "Unknown error")")
+ completionHandler(nil)
+ }
+ }
-CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` which will take the JSON received in The push notification as input, and return the corresponding `TextMessage`, `MediaMessage`,`CustomMessage` or `Call` object in return. Once the message object is received, you can use the entity as per your requirements.
+ static func getGroup(for group: Group, guid: String, completionHandler: @escaping (Group?) -> Void) {
+ CometChat.getGroup(GUID: guid, onSuccess: { fetchedGroup in
+ completionHandler(fetchedGroup)
+ }) { error in
+ print("Group details fetching failed with error: \(error?.errorDescription ?? "Unknown error")")
+ completionHandler(nil)
+ }
+ }
-This code needs to be added to the `willPresent notification` method of the `UNUserNotificationCenterDelegate` delegate.
+ static func shouldPresentNotification(userInfo: [AnyHashable: Any]) -> Bool {
+ guard let notificationType = userInfo["type"] as? String,
+ let receiverType = userInfo["receiverType"] as? String else {
+ return true
+ }
-
-
-```swift
-func userNotificationCenter(_ center: UNUserNotificationCenter,
- willPresent notification: UNNotification,
- withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
- if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject = userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
-
- if let baseMessage = CometChat.processMessage(dict).0 {
- switch baseMessage.messageCategory {
- case .message:
- if let message = baseMessage as? BaseMessage {
- switch message.messageType {
- case .text:
- print("text Messagge is: \((message as?TextMessage)?.stringValue())")
- case .image:
- print("image Messagge is: \((message as? MediaMessage)?.stringValue())")
- case .video:
- print("video Messagge is: \((message as? MediaMessage)?.stringValue())")
- case .audio:
- print("audio Messagge is: \((message as? MediaMessage)?.stringValue())")
- case .file:
- print("file Messagge is: \((message as? MediaMessage)?.stringValue())")
- case .custom:
- print("custom Messagge is: \((message as? MediaMessage)?.stringValue())")
- case .groupMember: break
- @unknown default:break}
- }
- case .action: break
- case .call:
- if let call = baseMessage as? Call {
- print("call is: \(call.stringValue())")
- }
- case .custom:
- if let customMessage = baseMessage as? CustomMessage {
- print("customMessage is: \(customMessage.stringValue())")
- }
- @unknown default: break
- }
- }
- }
- completionHandler([.alert, .badge, .sound])
- }
-
-extension String {
- func stringTodictionary() -> [String:Any]? {
- var dictonary:[String:Any]?
- if let data = self.data(using: .utf8) {
- do {
- dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
- if let myDictionary = dictonary
- {
- return myDictionary;
- }
- } catch let error as NSError {
- print(error)
+ if notificationType == "chat" {
+ if receiverType == "user" {
+ let sender = userInfo["sender"] as? String
+ if sender == CometChatPNHelper.currentActiveUser?.uid {
+ return false
+ }
+ } else if receiverType == "group" {
+ let receiver = userInfo["receiver"] as? String
+ if receiver == CometChatPNHelper.currentActiveGroup?.guid {
+ return false
+ }
}
- }
- return dictonary;
+ }
+
+ return true
}
}
-```
+ ```
+
+
+```swift lines highlight={5-8}
+import Foundation
+import UIKit
-
+class AppConstants {
+ static var APP_ID: String = ""
+ static var AUTH_KEY: String = ""
+ static var REGION: String = ""
+ static var PROVIDER_ID: String = ""
+}
+extension AppConstants{
+ static func saveAppConstants(){
+ UserDefaults.standard.set(APP_ID, forKey: "appID")
+ UserDefaults.standard.set(AUTH_KEY, forKey: "authKey")
+ UserDefaults.standard.set(REGION, forKey: "region")
+ }
+
+ static func retrieveAppConstants(){
+ APP_ID = UserDefaults.standard.string(forKey: "appID") ?? AppConstants.APP_ID
+ AUTH_KEY = UserDefaults.standard.string(forKey: "authKey") ?? AppConstants.AUTH_KEY
+ REGION = UserDefaults.standard.string(forKey: "region") ?? AppConstants.REGION
+ }
+}
+```
+
+
+## 4. Register APNs device + VoIP tokens with CometChat
+
+- In your `AppDelegate.swift`, implement the following methods to handle APNs registration success and failure, and to register the device token with CometChat.
+- Make sure to import the necessary modules at the top of the file.
+- Complete your `AppDelegate.swift` as shown below:
+
+```Swift lines highlight={17, 19, 22}
+import UIKit
+import PushKit
+import CometChatSDK
+import CometChatUIKitSwift
+
+@main
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+ var pushRegistry: PKPushRegistry?
+ let cometchatAPNsHelper = CometChatAPNsHelper()
+ func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+
+ UNUserNotificationCenter.current().delegate = self
+
+ cometchatAPNsHelper.configurePushNotification(application: application, delegate: self)
+
+ // Initialize PushKit
+ initializePushKit()
+
+ return true
+ }
+
+ // MARK: - APNs Registration Success
+ func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ print("APNs Device token received!")
+
+ if CometChat.getLoggedInUser() != nil {
+ print("User is logged in, registering APNs token...")
+ cometchatAPNsHelper.registerTokenForPushNotification(deviceToken: deviceToken)
+ } else {
+ print("User NOT logged in yet, will register token after login")
+ // Store token for later registration
+ let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
+ UserDefaults.standard.set(hexString, forKey: "pendingAPNsToken")
+ }
+ }
+
+ // MARK: - APNs Registration Failure
+ func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ print("Failed to register for APNs: \(error.localizedDescription)")
+ }
+}
+```
+
+## 5. Testing checklist
+
+1. Install on a device; grant notification permission. Verify APNs device token logs.
+2. Log in, then confirm both device + VoIP tokens register with CometChat (success callbacks).
+3. Send a message from another user:
+ - Foreground: ensure `willPresent` shows your chosen presentation.
+ - Background/terminated: tapping opens the correct conversation.
+4. Trigger an incoming call; CallKit UI should show caller info. Accept should join the call; Decline should reject via CometChat and end CallKit.
+5. Rotate tokens (reinstall or toggle VoIP) to ensure re-registration works.
+
+## 6. Troubleshooting
+
+| Symptom | Quick checks |
+| --- | --- |
+| No pushes | Entitlements set, APNs provider creds correct, bundle ID matches dashboard, permission granted. |
+| Token registration fails | Run after login; provider IDs correct for device vs VoIP. |
+| Taps do nothing | Verify notification center delegate and navigation readiness before routing. |
+| Call UI missing | Ensure PushKit delegate fires, CallKit capabilities enabled, VoIP provider ID set. |
+| Audio errors | Configure `AVAudioSession` for playAndRecord when reporting/accepting calls. |
diff --git a/notifications/ios-fcm-push-notifications-legacy.mdx b/notifications/ios-fcm-push-notifications-legacy.mdx
new file mode 100644
index 00000000..2c89e5aa
--- /dev/null
+++ b/notifications/ios-fcm-push-notifications-legacy.mdx
@@ -0,0 +1,690 @@
+---
+title: "iOS FCM"
+---
+
+Learn how to send out Push Notifications to your iOS using Firebase Cloud Messaging or FCM.
+
+
+Don't want to use FCM?
+
+You can refer to [our setup](/notifications/ios-apns-push-notifications) using the Apple Push Notifications service (APNs).
+
+
+
+
+ iOS Push notifications sample app
+
+ View on Github
+
+
+## Firebase Project Setup
+
+Visit [Firebase Console](https://console.firebase.google.com/) and login/signup using your Gmail ID.
+
+### Step 1: Create a new Firebase Project
+
+On your Firebase Console, create a new project.
+
+
+
+
+
+This is a simple 3 step process where:
+
+1. You give a name to your project
+2. Add Google Analytics to your project (Optional)
+3. Configure Google Analytics account (Optional)
+
+Click on Create and you are ready to go.
+
+### Step 2: Add Firebase to your iOS App
+
+1. Click on the iOS icon as shown on the screen below.
+
+
+
+
+
+2. Register your Android app by providing the following details: a. iOS bundle name b. App nickname (optional) c. App Store ID (optional)
+
+
+
+
+
+3. Download the GoogleService-Info.plist file and place it in the mentioned location of your project. Move your config file into the root of your Xcode project. If prompted, select to add the config file to all targets as follows.
+
+
+
+
+
+
+
+
+
+4. We will Add Firebase SDK and Initialisation code later. So, click on 'Next', 'Next', and 'Continue to the Console'.
+
+### Step 3: Download the service account file
+
+
+
+
+
+## Extension settings
+
+### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications extension.
+3. Open the settings for this extension and save the following settings.
+
+
+
+
+
+### Step 2: Save your settings
+
+On the Settings page you need to enter the following:
+
+1. **Set extension version**
+
+* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
+* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
+* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+
+2. **Select the platforms that you want to support**
+
+* Select from Web, Android, Ionic, React Native, Flutter & iOS.
+
+3. **Notification payload settings**
+
+* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
+
+4. **Push payload message options**
+
+
+
+
+
+The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+
+* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+
+5. **Notification Triggers**
+
+
+
+
+
+* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+* These are pretty self-explanatory and you can toggle them as per your requirement.
+
+## Get APNS Credentials
+
+The following steps in this section are written on the assumption that you already have an app ID assigned to your client app.
+
+### Step 1: Create a Certificate Signing Request
+
+To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
+
+1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
+
+
+
+
+
+2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button.
+
+
+
+
+
+3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
+
+### Step 2: Create an SSL certificate
+
+1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
+2. Go to Certificates, Identifiers & Profiles. In the Identifiers > App IDs and select the Push Notifications service under Application Services
+3. Click the Edit button.
+
+
+
+
+
+4. Under the Push Notifications service, choose which SSL certificate to create either Development or Production.
+
+
+
+
+
+5. In the Generate your certificate pane that appears after the selection, under Upload CSR file., upload the CSR file you created through the Choose File... button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
+
+
+
+
+
+6. In order to install the downloaded certificate to the KeyChain Access on your Mac, double-click it. You can find the certificate in the KeyChain Access > login > Certificates.
+
+### Step 3: Export and update .p12 file to Firebase
+
+1. Type a name for the .p12 file and save it to your Mac.
+2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
+
+
+
+
+
+### Step 4: Upload your APNs Certificates
+
+1. Go to Firebase console and open your project.
+2. Inside your iOS project in the Firebase console, select settings and then select the `Cloud Messaging` tab.
+3. Scroll down to iOS app configuration, click the Upload button for APNS certificate.
+4. Browse to the location where you saved your APNs Certificates, select it, and click Open.
+
+
+
+
+
+## iOS App Setup
+
+### Step 1: Initial Firebase Cloud Messaging client setup
+
+1. Add the Firebase SDK, Add the firebase pods that you want to install. You can include a Pod in your Podfile like this:
+
+
+
+```swift
+pod 'Firebase_Core'
+pod 'Firebase_Messaging'
+```
+
+
+
+
+
+2. Import the Firebase module in your `ApplicationDelegate:`
+
+
+
+```swift
+import Firebase
+```
+
+
+
+
+```objc
+@import Firebase;
+```
+
+
+
+
+
+3. Configure a FirebaseApp shared instance, typically in your application's `application:didFinishLaunchingWithOptions: method:`
+
+
+
+```swift
+FirebaseApp.configure()
+```
+
+
+
+
+```objc
+[FIRApp configure];
+```
+
+
+
+
+
+### Step 2: Register the FCM Token
+
+1. Get the FCM Token for remote notifications, typically in your application's `application:didFinishLaunchingWithOptions: method:`
+
+
+
+```swift
+Messaging.messaging().delegate = self
+
+if #available(iOS 10.0, *) {
+ UNUserNotificationCenter.current().delegate = self
+
+ let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
+ UNUserNotificationCenter.current().requestAuthorization(
+ options: authOptions,
+ completionHandler: {
+ _,
+ _ in
+ })
+} else {
+ let settings: UIUserNotificationSettings =
+ UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
+ application.registerUserNotificationSettings(settings)
+}
+
+application.registerForRemoteNotifications()
+```
+
+
+
+
+```objc
+[FIRMessaging messaging].delegate = self;
+
+if ([UNUserNotificationCenter class] != nil) {
+
+ [UNUserNotificationCenter currentNotificationCenter].delegate = self;
+
+ UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
+
+ [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions: authOptions completionHandler: ^ (BOOL granted, NSError * _Nullable error) {
+ // ...
+ }
+ ];
+
+} else {
+ UIUserNotificationType allNotificationTypes = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
+
+ UIUserNotificationSettings * settings = [UIUserNotificationSettings settingsForTypes: allNotificationTypes categories: nil];
+
+ [application registerUserNotificationSettings: settings];
+
+}
+[application registerForRemoteNotifications];
+```
+
+
+
+
+
+
+
+```swift
+func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ print("Unable to register for remote notifications: \\(error.localizedDescription)")
+}
+
+func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ print("APNs token retrieved: \\(deviceToken)")
+
+ Messaging.messaging().apnsToken = deviceToken
+}
+```
+
+
+
+
+```objc
+-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
+{
+ NSLog(@"Unable to register for remote notifications: %@", error);
+}
+
+-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
+{
+ NSLog(@"APNs device token retrieved: %@", deviceToken);
+ [FIRMessaging messaging].APNSToken = deviceToken;
+}
+```
+
+
+
+
+
+2. Register the FCM token with our Push Notifications extension on success of CometChat.login
+
+
+
+```swift
+let authKey = "XXXX XXXX XXXXX"
+
+CometChat.login(UID: UID, authKey: authKey, onSuccess: { (user) in
+ DispatchQueue.main.async {
+ if let token = UserDefaults.standard.value(forKey: "fcmToken") as? String {
+ CometChat.registerTokenForPushNotification(token: token, onSuccess: { (success) in
+ print("onSuccess to registerTokenForPushNotification: \\(success)")
+ }) { (error) in
+ print("error to registerTokenForPushNotification")
+ }
+}
+```
+
+
+
+
+
+3. This also needs to be done when you refresh your FCM Token
+
+
+
+```swift
+extension AppDelegate : MessagingDelegate {
+ // [START refresh_token]
+ func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
+ print("Firebase registration token: \\(fcmToken)")
+
+ UserDefaults.standard.set(fcmToken, forKey: "fcmToken")
+ CometChat.registerTokenForPushNotification(token: fcmToken, onSuccess: { (sucess) in
+ print("token registered \\(sucess)")
+ }) { (error) in
+ print("token registered error \\(String(describing: error?.errorDescription))")
+ }
+
+
+ let dataDict:[String: String] = ["token": fcmToken]
+ NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
+ }
+}
+```
+
+
+
+
+
+### Step 3: Start receiving Push Notifications
+
+1. Receive remote notification, typically in your application's `App Delegate:`
+
+
+
+```swift
+func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
+
+ // Print full message.
+ print(userInfo)
+}
+
+func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping(UIBackgroundFetchResult) -> Void) {
+
+ // Print full message.
+ print(userInfo)
+
+ completionHandler(UIBackgroundFetchResult.newData)
+}
+```
+
+
+
+
+```objc
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
+
+ // Print full message.
+ NSLog(@"%@", userInfo);
+}
+
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
+ fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+
+ // Print full message.
+ NSLog(@"%@", userInfo);
+
+ completionHandler(UIBackgroundFetchResultNewData);
+}
+```
+
+
+
+
+
+2. Receive Notification for `CustomMessage`:
+
+To receive and display notifications for `CustomMessage`, the developer needs to set `metadata` while sending the `CustomMessage` value as follows:
+
+
+
+```swift
+var receiverID = "cometchat-uid-1";
+var message = [
+ "someRandomKey": "someRandomData"
+];
+
+var customMessage = CustomMessage(receiverUid: receiverID, receiverType: ReceiverTypeUser, customData: message);
+
+// to display custom notification banner add this , "pushNotification" key is not to modify, although you can modify banner text as shown beow //
+var customNotificationDisplayText = [
+ "pushNotification": "notification_banner_text_here";
+];
+
+// set it as metadata of `Custom message`
+customMessage.metaData = customNotificationDisplayText;
+
+CometChat.sendCustomMessage(withMessage: customMessage, onSuccess: { sentMessage in
+
+ print("sentMessage \\(sentMessage.stringValue)");
+
+}, onError: { error in
+
+ if let error = error?.errorDescription() {
+ print("error sending custom message \\(error)");
+ }
+});
+```
+
+
+
+
+```objc
+NSString * receiverID = @ "cometchat-uid-1";
+NSDictionary * message = [NSDictionary dictionaryWithObjectsAndKeys: @ "someRandomData", @ "someRandomKey", nil];
+
+CustomMessage * customMessage = [
+ [CustomMessage alloc] initWithReceiverUid: receiverID receiverType: ReceiverTypeUser customData: message
+];
+
+// to display custom notification banner add this //
+NSDictionary * customNotificationDisplayText = [NSDictionary dictionaryWithObjectsAndKeys: @ "notification_banner_text_here", @ "pushNotification", nil];
+
+[customMessage setMetaData: customNotificationDisplayText];
+
+
+[CometChat sendCustomMessageWithMessage: customMessage onSuccess: ^ (CustomMessage * _Nonnull sentMessage) {
+
+ NSLog(@ "sentMessage %@", [sentMessage stringValue]);
+
+ }
+ onError: ^ (CometChatException * _Nullable error) {
+
+ NSLog(@ "error sending custom message %@", [error errorDescription]);
+ }
+];
+```
+
+
+
+
+
+Push Notification Payload sample for text and media messages-
+
+
+
+```json
+{
+ "alert": "Nancy Grace: Text Message",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "text": "Text Message"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "142",
+ "sentAt": 1555668711,
+ "category": "message",
+ "type": "text"
+ }
+}
+```
+
+
+
+
+```json
+{
+ "alert": "Nancy Grace: has sent an image",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "attachments": [
+ {
+ "extension": "png",
+ "size": 14327,
+ "name": "extension_leftpanel.png",
+ "mimeType": "image/png",
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ }
+ ],
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "145",
+ "sentAt": 1555671238,
+ "category": "message",
+ "type": "image"
+ }
+}
+```
+
+
+
+
+
+## Advanced
+
+### Convert Push Notification payload to Message object
+
+CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` which will take the JSON received in The push notification as input, and return the corresponding `TextMessage`, `MediaMessage`,`CustomMessage` or `Call` object in return. Once the message object is received, you can use the entity as per your requirements.
+
+This code needs to be added to the `willPresent notification` method of the `UNUserNotificationCenterDelegate` delegate.
+
+
+
+```swift
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+ willPresent notification: UNNotification,
+ withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+ if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject = userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
+
+ if let baseMessage = CometChat.processMessage(dict).0 {
+ switch baseMessage.messageCategory {
+ case .message:
+ if let message = baseMessage as? BaseMessage {
+ switch message.messageType {
+ case .text:
+ print("text Messagge is: \\((message as?TextMessage)?.stringValue())")
+ case .image:
+ print("image Messagge is: \\((message as? MediaMessage)?.stringValue())")
+ case .video:
+ print("video Messagge is: \\((message as? MediaMessage)?.stringValue())")
+ case .audio:
+ print("audio Messagge is: \\((message as? MediaMessage)?.stringValue())")
+ case .file:
+ print("file Messagge is: \\((message as? MediaMessage)?.stringValue())")
+ case .custom:
+ print("custom Messagge is: \\((message as? MediaMessage)?.stringValue())")
+ case .groupMember: break
+ @unknown default:break}
+ }
+ case .action: break
+ case .call:
+ if let call = baseMessage as? Call {
+ print("call is: \\(call.stringValue())")
+ }
+ case .custom:
+ if let customMessage = baseMessage as? CustomMessage {
+ print("customMessage is: \\(customMessage.stringValue())")
+ }
+ @unknown default: break
+ }
+ }
+ }
+ completionHandler([.alert, .badge, .sound])
+ }
+
+extension String {
+ func stringTodictionary() -> [String:Any]? {
+ var dictonary:[String:Any]?
+ if let data = self.data(using: .utf8) {
+ do {
+ dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
+ if let myDictionary = dictonary
+ {
+ return myDictionary;
+ }
+ } catch let error as NSError {
+ print(error)
+ }
+ }
+ return dictonary;
+ }
+}
+```
+
+
+
+
+
+### Miscellaneous
+
+1. [Increment App Icon Badge Count](/sdk/ios/increment-app-icon-badge-count)
+2. [Launch chat window on tap of push notification](/sdk/ios/launch-chat-window-on-tap-of-push-notification)
diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx
index 2c89e5aa..c8bdfebd 100644
--- a/notifications/ios-fcm-push-notifications.mdx
+++ b/notifications/ios-fcm-push-notifications.mdx
@@ -1,690 +1,321 @@
---
-title: "iOS FCM"
+title: "iOS FCM Push Notifications"
+description: "Guide to integrating Firebase Cloud Messaging (FCM) push notifications in iOS apps using CometChat."
---
-Learn how to send out Push Notifications to your iOS using Firebase Cloud Messaging or FCM.
-
-
-Don't want to use FCM?
-
-You can refer to [our setup](/notifications/ios-apns-push-notifications) using the Apple Push Notifications service (APNs).
-
-
+FCM doesn't support VoIP pushes; use APNs + PushKit for VoIP calls.
- iOS Push notifications sample app
-
- View on Github
+Reference implementation of iOS UIKit, FCM and Push Notification Setup.
-## Firebase Project Setup
+## What this guide covers
-Visit [Firebase Console](https://console.firebase.google.com/) and login/signup using your Gmail ID.
+- CometChat dashboard setup (enable push, add FCM iOS + APNs providers) with screenshots.
+- Firebase/FCM + CometChat provider wiring (credentials, Podfile, capabilities).
+- Token registration/rotation with CometChat Push Notifications (FCM iOS provider).
+- Incoming message/call handling and deep links.
+- Payload customization and testing.
-### Step 1: Create a new Firebase Project
+{/* ## What you need first
-On your Firebase Console, create a new project.
-
-
-
-
+- CometChat App ID, Region, Auth Key; Push Notifications with **FCM iOS provider ID** (and APNs device/VoIP provider IDs if you deliver via APNs/PushKit).
+- Firebase project with an iOS app; `GoogleService-Info.plist` downloaded; APNs key uploaded to Firebase Cloud Messaging.
+- Xcode capabilities: Push Notifications, Background Modes (Remote notifications, Voice over IP), CallKit usage descriptions. Physical device for push testing. */}
-This is a simple 3 step process where:
+## How FCM + CometChat work together
-1. You give a name to your project
-2. Add Google Analytics to your project (Optional)
-3. Configure Google Analytics account (Optional)
+- **FCM’s role:** Firebase issues the iOS FCM registration token and delivers the push payload. On iOS, FCM hands off to APNs using the APNs key/cert you upload in Firebase.
+- **CometChat Notifications’ role:** The FCM iOS provider you create in the CometChat dashboard holds your Firebase service account. When you call `CometChatNotifications.registerPushToken(..., .FCM_IOS, providerId)`, CometChat binds that FCM token to the logged-in user and sends pushes to FCM on your behalf.
+- **Flow (same bridge used in `android-push-notifications.mdx`):** Request permission → register for remote notifications → FCM returns the registration token → after `CometChat.login` succeeds, register that token with `CometChatNotifications` using the FCM provider ID → CometChat sends payloads to FCM → FCM hands to APNs → `UNUserNotificationCenterDelegate` surfaces the notification/tap.
-Click on Create and you are ready to go.
+## 1. Enable push and add providers (CometChat Dashboard)
-### Step 2: Add Firebase to your iOS App
-
-1. Click on the iOS icon as shown on the screen below.
+1) Go to **Notifications → Settings** and enable **Push Notifications**.
-
+
-2. Register your Android app by providing the following details: a. iOS bundle name b. App nickname (optional) c. App Store ID (optional)
+2) On Firebase:
+ - Go to **Project Settings → Service accounts** and generate a new private key; download the JSON file.
-
+
-3. Download the GoogleService-Info.plist file and place it in the mentioned location of your project. Move your config file into the root of your Xcode project. If prompted, select to add the config file to all targets as follows.
-
-
-
-
+3) On CometChat Dashboard:
+ - Add an **FCM iOS provider** and upload your Firebase service account JSON; copy the Provider ID.
-
+
-4. We will Add Firebase SDK and Initialisation code later. So, click on 'Next', 'Next', and 'Continue to the Console'.
+ ## 2. Upload your APNs Certificates
-### Step 3: Download the service account file
+1) In the **Firebase Console**, go to **Project Settings → Cloud Messaging**.
+2) Under **iOS app configuration**, upload your APNs authentication key or certificates.
-
+
-## Extension settings
-
-### Step 1: Enable the extension
+## 3. Prepare Firebase and CometChat
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open the settings for this extension and save the following settings.
+1) Firebase Console: download and add `GoogleService-Info.plist` to your target.
-
+
-### Step 2: Save your settings
-
-On the Settings page you need to enter the following:
-
-1. **Set extension version**
-
-* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
-* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
-* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
-
-2. **Select the platforms that you want to support**
-
-* Select from Web, Android, Ionic, React Native, Flutter & iOS.
-
-3. **Notification payload settings**
-
-* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
-
-4. **Push payload message options**
+2) Xcode capabilities: Push Notifications, Background Modes → Remote notifications.
-
+
-The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
-
-* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
-
-5. **Notification Triggers**
-
-
-
-
+## 4. Add dependencies (Podfile)
-* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
-
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
-
-* These are pretty self-explanatory and you can toggle them as per your requirement.
-
-## Get APNS Credentials
-
-The following steps in this section are written on the assumption that you already have an app ID assigned to your client app.
-
-### Step 1: Create a Certificate Signing Request
-
-To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
-
-1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
-
-
-
-
-
-2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button.
-
-
-
-
-
-3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
-
-### Step 2: Create an SSL certificate
-
-1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
-2. Go to Certificates, Identifiers & Profiles. In the Identifiers > App IDs and select the Push Notifications service under Application Services
-3. Click the Edit button.
-
-
-
-
-
-4. Under the Push Notifications service, choose which SSL certificate to create either Development or Production.
-
-
-
-
+```ruby lines
+target 'YourApp' do
+ use_frameworks!
-5. In the Generate your certificate pane that appears after the selection, under Upload CSR file., upload the CSR file you created through the Choose File... button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
-
-
-
-
-
-6. In order to install the downloaded certificate to the KeyChain Access on your Mac, double-click it. You can find the certificate in the KeyChain Access > login > Certificates.
-
-### Step 3: Export and update .p12 file to Firebase
-
-1. Type a name for the .p12 file and save it to your Mac.
-2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
-
-
-
-
-
-### Step 4: Upload your APNs Certificates
-
-1. Go to Firebase console and open your project.
-2. Inside your iOS project in the Firebase console, select settings and then select the `Cloud Messaging` tab.
-3. Scroll down to iOS app configuration, click the Upload button for APNS certificate.
-4. Browse to the location where you saved your APNs Certificates, select it, and click Open.
-
-
-
-
-
-## iOS App Setup
-
-### Step 1: Initial Firebase Cloud Messaging client setup
-
-1. Add the Firebase SDK, Add the firebase pods that you want to install. You can include a Pod in your Podfile like this:
-
-
-
-```swift
-pod 'Firebase_Core'
-pod 'Firebase_Messaging'
-```
-
-
-
-
-
-2. Import the Firebase module in your `ApplicationDelegate:`
-
-
-
-```swift
-import Firebase
-```
-
-
-
-
-```objc
-@import Firebase;
-```
-
-
-
-
-
-3. Configure a FirebaseApp shared instance, typically in your application's `application:didFinishLaunchingWithOptions: method:`
-
-
-
-```swift
-FirebaseApp.configure()
-```
-
-
-
-
-```objc
-[FIRApp configure];
+ pod 'CometChatCallsSDK'
+ pod 'CometChatUIKitSwift', '5.1.4'
+ pod 'Firebase/Messaging' # add for FCM
+end
```
-
-
-
-
-### Step 2: Register the FCM Token
+Run `pod install`.
+
-1. Get the FCM Token for remote notifications, typically in your application's `application:didFinishLaunchingWithOptions: method:`
-
-
-
-```swift
-Messaging.messaging().delegate = self
-
-if #available(iOS 10.0, *) {
- UNUserNotificationCenter.current().delegate = self
-
- let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
- UNUserNotificationCenter.current().requestAuthorization(
- options: authOptions,
- completionHandler: {
- _,
- _ in
- })
-} else {
- let settings: UIUserNotificationSettings =
- UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
- application.registerUserNotificationSettings(settings)
-}
+## 5. Wire AppDelegate (Firebase + delegates)
-application.registerForRemoteNotifications()
-```
+```swift lines highlight={15, 17, 19-37}
+import UIKit
+import FirebaseCore
+import FirebaseMessaging
+import CometChatUIKitSwift
+import CometChatSDK
-
+@main
+class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
-
-```objc
-[FIRMessaging messaging].delegate = self;
+ func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
-if ([UNUserNotificationCenter class] != nil) {
+ FirebaseApp.configure()
- [UNUserNotificationCenter currentNotificationCenter].delegate = self;
+ Messaging.messaging().delegate = self
- UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
+ UNUserNotificationCenter.current().delegate = self
+ UNUserNotificationCenter.current().requestAuthorization(
+ options: [.alert, .badge, .sound]
+ ) { granted, error in
+ print("Notification permission granted:", granted)
+ if let error = error {
+ print("Permission error:", error.localizedDescription)
+ }
+
+ // Only register for remote notifications if permission granted
+ if granted {
+ DispatchQueue.main.async {
+ print("Registering for remote notifications...")
+ application.registerForRemoteNotifications()
+ }
+ } else {
+ print("Push notification permission denied by user")
+ }
+ }
- [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions: authOptions completionHandler: ^ (BOOL granted, NSError * _Nullable error) {
- // ...
+ return true
}
- ];
-
-} else {
- UIUserNotificationType allNotificationTypes = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
- UIUserNotificationSettings * settings = [UIUserNotificationSettings settingsForTypes: allNotificationTypes categories: nil];
-
- [application registerUserNotificationSettings: settings];
-
-}
-[application registerForRemoteNotifications];
-```
-
-
-
-
-
-
-
-```swift
-func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
- print("Unable to register for remote notifications: \\(error.localizedDescription)")
-}
-
-func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- print("APNs token retrieved: \\(deviceToken)")
-
- Messaging.messaging().apnsToken = deviceToken
-}
-```
-
-
-
-
-```objc
--(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
-{
- NSLog(@"Unable to register for remote notifications: %@", error);
-}
-
--(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
-{
- NSLog(@"APNs device token retrieved: %@", deviceToken);
- [FIRMessaging messaging].APNSToken = deviceToken;
-}
-```
-
-
-
-
-
-2. Register the FCM token with our Push Notifications extension on success of CometChat.login
-
-
-
-```swift
-let authKey = "XXXX XXXX XXXXX"
-
-CometChat.login(UID: UID, authKey: authKey, onSuccess: { (user) in
- DispatchQueue.main.async {
- if let token = UserDefaults.standard.value(forKey: "fcmToken") as? String {
- CometChat.registerTokenForPushNotification(token: token, onSuccess: { (success) in
- print("onSuccess to registerTokenForPushNotification: \\(success)")
- }) { (error) in
- print("error to registerTokenForPushNotification")
- }
-}
-```
-
-
-
-
-
-3. This also needs to be done when you refresh your FCM Token
+ func application(
+ _ application: UIApplication,
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
+ ) {
+ Messaging.messaging().apnsToken = deviceToken
+
+ // Store the token for later registration after login
+ let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
+ UserDefaults.standard.set(hexString, forKey: "apnsPushToken")
+ print("APNs token received and stored: \(hexString)")
+ }
+
+ func application(
+ _ application: UIApplication,
+ didFailToRegisterForRemoteNotificationsWithError error: Error
+ ) {
+ print("Failed to register for remote notifications: \(error.localizedDescription)")
+ }
+
+ func application(
+ _ application: UIApplication,
+ didReceiveRemoteNotification userInfo: [AnyHashable : Any],
+ fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
+ ) {
+ print("Background notification received:", userInfo)
+ completionHandler(.newData)
+ }
-
-
-```swift
-extension AppDelegate : MessagingDelegate {
- // [START refresh_token]
- func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
- print("Firebase registration token: \\(fcmToken)")
-
- UserDefaults.standard.set(fcmToken, forKey: "fcmToken")
- CometChat.registerTokenForPushNotification(token: fcmToken, onSuccess: { (sucess) in
- print("token registered \\(sucess)")
- }) { (error) in
- print("token registered error \\(String(describing: error?.errorDescription))")
+ func messaging(
+ _ messaging: Messaging,
+ didReceiveRegistrationToken fcmToken: String?
+ ) {
+ guard let fcmToken = fcmToken else {
+ print("FCM token is nil")
+ return
}
-
- let dataDict:[String: String] = ["token": fcmToken]
- NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
+ print("FCM Token received:", fcmToken)
+
+ // Store FCM token as well
+ UserDefaults.standard.set(fcmToken, forKey: "fcmPushToken")
+
+ if let apnsToken = UserDefaults.standard.string(forKey: "apnsPushToken") {
+ print("Registering APNs token with CometChat: \(apnsToken)")
+ CometChatNotifications.registerPushToken(
+ pushToken: fcmToken,
+ platform: CometChatNotifications.PushPlatforms.FCM_IOS,
+ providerId: AppConstants.PROVIDER_ID,
+ onSuccess: { success in
+ print("APNs token registered with CometChat: \(success)")
+ },
+ onError: { error in
+ print("APNs token registration failed: \(error.errorCode) - \(error.errorDescription)")
+ }
+ )
+ } else {
+ print("No stored APNs token found - Check if Push Notifications capability is enabled in Xcode")
+ }
}
-}
-```
-
-
-
-
-
-### Step 3: Start receiving Push Notifications
-
-1. Receive remote notification, typically in your application's `App Delegate:`
-
-
-
-```swift
-func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
-
- // Print full message.
- print(userInfo)
-}
-
-func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping(UIBackgroundFetchResult) -> Void) {
-
- // Print full message.
- print(userInfo)
- completionHandler(UIBackgroundFetchResult.newData)
-}
-```
-
-
-
-
-```objc
-- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
-
- // Print full message.
- NSLog(@"%@", userInfo);
-}
-- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
- fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
-
- // Print full message.
- NSLog(@"%@", userInfo);
-
- completionHandler(UIBackgroundFetchResultNewData);
+ func userNotificationCenter(
+ _ center: UNUserNotificationCenter,
+ willPresent notification: UNNotification,
+ withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
+ ) {
+ completionHandler([.banner, .sound, .badge])
+ }
+
+ // Call this method AFTER CometChat login succeeds
+ static func registerStoredPushToken() {
+ guard CometChat.getLoggedInUser() != nil else {
+ print("Cannot register push token: User not logged in")
+ return
+ }
+
+ // Register APNs token
+ if let apnsToken = UserDefaults.standard.string(forKey: "apnsPushToken") {
+ print("Registering APNs token with CometChat: \(apnsToken)")
+ CometChatNotifications.registerPushToken(
+ pushToken: apnsToken,
+ platform: CometChatNotifications.PushPlatforms.APNS_IOS_DEVICE,
+ providerId: AppConstants.PROVIDER_ID,
+ onSuccess: { success in
+ print("APNs token registered with CometChat: \(success)")
+ },
+ onError: { error in
+ print("APNs token registration failed: \(error.errorCode) - \(error.errorDescription)")
+ }
+ )
+ } else {
+ print("No stored APNs token found - Check if Push Notifications capability is enabled in Xcode")
+ }
+
+ // Register FCM token
+ if let fcmToken = UserDefaults.standard.string(forKey: "fcmPushToken") {
+ print("Registering FCM token with CometChat...")
+ CometChat.registerTokenForPushNotification(
+ token: fcmToken,
+ onSuccess: { message in
+ print("CometChat FCM token registered successfully")
+ },
+ onError: { error in
+ print("CometChat FCM token registration failed:", error?.errorDescription ?? "Unknown error")
+ }
+ )
+ } else {
+ print("No stored FCM token found")
+ }
+ }
}
```
-
+**What this code is doing**
-
+- Initializes Firebase, sets `MessagingDelegate` and `UNUserNotificationCenterDelegate`, asks for alert/badge/sound permission, and registers for remote notifications.
+- Sets `Messaging.messaging().apnsToken` so FCM can map the APNs token and later deliver via APNs.
+- Stores the FCM registration token and calls `registerStoredPushToken()` so CometChat can bind the token to your logged-in user (using the Provider ID you configured).
+- Leaves `willPresent` to show banners/sounds in foreground instead of silently ignoring the notification.
-2. Receive Notification for `CustomMessage`:
+## 6. Create FCM helper
-To receive and display notifications for `CustomMessage`, the developer needs to set `metadata` while sending the `CustomMessage` value as follows:
+Create `CometChatFCMHelper.swift` and add:
-
-
```swift
-var receiverID = "cometchat-uid-1";
-var message = [
- "someRandomKey": "someRandomData"
-];
-
-var customMessage = CustomMessage(receiverUid: receiverID, receiverType: ReceiverTypeUser, customData: message);
-
-// to display custom notification banner add this , "pushNotification" key is not to modify, although you can modify banner text as shown beow //
-var customNotificationDisplayText = [
- "pushNotification": "notification_banner_text_here";
-];
-
-// set it as metadata of `Custom message`
-customMessage.metaData = customNotificationDisplayText;
-
-CometChat.sendCustomMessage(withMessage: customMessage, onSuccess: { sentMessage in
-
- print("sentMessage \\(sentMessage.stringValue)");
-
-}, onError: { error in
-
- if let error = error?.errorDescription() {
- print("error sending custom message \\(error)");
- }
-});
-```
+import UIKit
+import UserNotifications
-
+final class CometChatFCMHelper {
-
-```objc
-NSString * receiverID = @ "cometchat-uid-1";
-NSDictionary * message = [NSDictionary dictionaryWithObjectsAndKeys: @ "someRandomData", @ "someRandomKey", nil];
+ static let shared = CometChatFCMHelper()
+ private init() {}
-CustomMessage * customMessage = [
- [CustomMessage alloc] initWithReceiverUid: receiverID receiverType: ReceiverTypeUser customData: message
-];
+ /// Configure push notifications (iOS-safe)
+ func configure(application: UIApplication, delegate: UNUserNotificationCenterDelegate) {
-// to display custom notification banner add this //
-NSDictionary * customNotificationDisplayText = [NSDictionary dictionaryWithObjectsAndKeys: @ "notification_banner_text_here", @ "pushNotification", nil];
-
-[customMessage setMetaData: customNotificationDisplayText];
-
-
-[CometChat sendCustomMessageWithMessage: customMessage onSuccess: ^ (CustomMessage * _Nonnull sentMessage) {
-
- NSLog(@ "sentMessage %@", [sentMessage stringValue]);
-
- }
- onError: ^ (CometChatException * _Nullable error) {
-
- NSLog(@ "error sending custom message %@", [error errorDescription]);
- }
-];
-```
-
-
-
-
-
-Push Notification Payload sample for text and media messages-
-
-
-
-```json
-{
- "alert": "Nancy Grace: Text Message",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
+ // 1. Request permission
+ UNUserNotificationCenter.current().requestAuthorization(
+ options: [.alert, .badge, .sound]
+ ) { granted, error in
+ print("Notification permission:", granted)
+ if let error = error {
+ print("Permission error:", error.localizedDescription)
+ }
}
- },
- "text": "Text Message"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "142",
- "sentAt": 1555668711,
- "category": "message",
- "type": "text"
- }
-}
-```
-
-
-
-```json
-{
- "alert": "Nancy Grace: has sent an image",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "attachments": [
- {
- "extension": "png",
- "size": 14327,
- "name": "extension_leftpanel.png",
- "mimeType": "image/png",
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- }
- ],
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
+ // 2. Set delegate
+ UNUserNotificationCenter.current().delegate = delegate
+
+ // 3. Register for APNs
+ DispatchQueue.main.async {
+ application.registerForRemoteNotifications()
}
- },
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "145",
- "sentAt": 1555671238,
- "category": "message",
- "type": "image"
- }
+ }
}
```
-
-
-
-
-## Advanced
-
-### Convert Push Notification payload to Message object
-
-CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` which will take the JSON received in The push notification as input, and return the corresponding `TextMessage`, `MediaMessage`,`CustomMessage` or `Call` object in return. Once the message object is received, you can use the entity as per your requirements.
+{/* Alternative: pass the FCM token into `UIKitSettings` via `.set(fcmKey:)` before `CometChatUIKit.init` (e.g., `SceneDelegate`), alongside APNs device/VoIP tokens. */}
-This code needs to be added to the `willPresent notification` method of the `UNUserNotificationCenterDelegate` delegate.
+**What this helper is doing**
-
-
-```swift
-func userNotificationCenter(_ center: UNUserNotificationCenter,
- willPresent notification: UNNotification,
- withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
- if let userInfo = notification.request.content.userInfo as? [String : Any], let messageObject = userInfo["message"], let str = messageObject as? String, let dict = str.stringTodictionary() {
-
- if let baseMessage = CometChat.processMessage(dict).0 {
- switch baseMessage.messageCategory {
- case .message:
- if let message = baseMessage as? BaseMessage {
- switch message.messageType {
- case .text:
- print("text Messagge is: \\((message as?TextMessage)?.stringValue())")
- case .image:
- print("image Messagge is: \\((message as? MediaMessage)?.stringValue())")
- case .video:
- print("video Messagge is: \\((message as? MediaMessage)?.stringValue())")
- case .audio:
- print("audio Messagge is: \\((message as? MediaMessage)?.stringValue())")
- case .file:
- print("file Messagge is: \\((message as? MediaMessage)?.stringValue())")
- case .custom:
- print("custom Messagge is: \\((message as? MediaMessage)?.stringValue())")
- case .groupMember: break
- @unknown default:break}
- }
- case .action: break
- case .call:
- if let call = baseMessage as? Call {
- print("call is: \\(call.stringValue())")
- }
- case .custom:
- if let customMessage = baseMessage as? CustomMessage {
- print("customMessage is: \\(customMessage.stringValue())")
- }
- @unknown default: break
- }
- }
- }
- completionHandler([.alert, .badge, .sound])
- }
-
-extension String {
- func stringTodictionary() -> [String:Any]? {
- var dictonary:[String:Any]?
- if let data = self.data(using: .utf8) {
- do {
- dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
- if let myDictionary = dictonary
- {
- return myDictionary;
- }
- } catch let error as NSError {
- print(error)
- }
- }
- return dictonary;
- }
-}
-```
+- Wraps permission prompts + delegate wiring so you can call one method from `AppDelegate`.
+- Sets the notification delegate early and registers for APNs on the main queue (Apple requirement).
+- Keeps all push setup in a reusable singleton to call from tests, scenes, or multi-target setups.
-
+## 7. Testing checklist
-
+1. `FirebaseApp.configure()` runs; FCM token logs after login; registration with CometChat succeeds.
+2. Message from another user:
+ - Foreground: `willPresent` behavior as expected.
+ - Background/terminated: tap opens the correct chat.
+3. Rotate the FCM token (`didReceiveRegistrationToken`) and confirm re-registration.
+4. VoIP: VoIP token registers; incoming call shows CallKit; Accept/Decline controls the CometChat call.
-### Miscellaneous
+## 8. Troubleshooting
-1. [Increment App Icon Badge Count](/sdk/ios/increment-app-icon-badge-count)
-2. [Launch chat window on tap of push notification](/sdk/ios/launch-chat-window-on-tap-of-push-notification)
+| Symptom | Quick checks |
+| --- | --- |
+| No FCM pushes | `GoogleService-Info.plist` present; APNs key uploaded to Firebase; bundle ID matches; permission granted. |
+| Token registration fails | Run after login; provider ID matches FCM iOS provider; `Messaging.messaging().delegate` set. |
+| Taps ignored | Ensure `UNUserNotificationCenterDelegate` methods fire and navigation is ready before routing. |
+| Call UI missing | Add PushKit + CallKit wiring; register VoIP token with an APNs VoIP provider; ensure VoIP/background modes are enabled. |
diff --git a/notifications/legacy-push-notifications.mdx b/notifications/legacy-push-notifications.mdx
index 55262310..f5643074 100644
--- a/notifications/legacy-push-notifications.mdx
+++ b/notifications/legacy-push-notifications.mdx
@@ -1,5 +1,5 @@
---
-title: "Legacy"
+title: "Integration Guide for Legacy Push Notifications"
---
diff --git a/notifications/logs.mdx b/notifications/logs.mdx
index e919a080..bda5f3b6 100644
--- a/notifications/logs.mdx
+++ b/notifications/logs.mdx
@@ -1,25 +1,30 @@
---
title: "Notification Logs"
+description: "Debug why a push/email/SMS did or didn’t send, see provider responses, and filter by event or user."
---
-## Summary
+Notification logs help you:
+- Verify which push/email/SMS notifications were triggered.
+- See why a notification wasn’t sent (preferences, mute/DND, missing token).
+- Inspect provider responses to spot delivery patterns or failures.
-Notification logs enable you to track Push, Email, and SMS notifications triggered for CometChat events. Notification logs provide insights into why a notification was not triggered for a specific event (maybe due to preferences). Additionally, responses from providers are logged to analyze success and failure patterns from the provider's end.
+### Turn logging on
-## Logs availability
+- In the CometChat Dashboard, go to **Notifications → Logs** and enable logging.
+- Logging stays active for 7 days, then auto-disables. Re-enable it any time.
-When enabled, logging remains active for 7 days and automatically disables afterward. Logs collected during this period are available for review for up to 14 days—seven days while logging is active and an additional seven days after logging stops. After this period, the logs are permanently deleted.
+### Retention timeline
-## Supported providers
+Logs are kept for up to 14 days: 7 days while logging is active, plus 7 days after it stops. After that, they are permanently deleted.
-Push notification logging is supported for FCM, APNs, and custom providers. Email notification logs are generated for SendGrid and custom providers. SMS notification logging is supported for Twilio and custom providers.
+### Coverage
-## Supported events
+- **Push providers:** FCM, APNs, and custom providers.
+- **Email providers:** SendGrid and custom providers.
+- **SMS providers:** Twilio and custom providers.
+- **Events:** Push covers messages/replies (1:1 and group), edits/deletes/reactions, and group membership events. Email/SMS cover messages and replies (1:1 and group).
-Push notification logs are generated for **messages** and **replies** sent in one-on-one and group coversations, for message actions like **message edited**, **deleted** and **reactions** and in case of groups, it also logs the **group events**.
+### Access and filtering
-SMS and Email notifications are supported for **messages** and **replies** sent in one-on-one and group conversations.
-
-## Available filters
-
-The Get logs API supports various query parameters to filter logs for better insights. More details about the usage can be found in the [API explorer documentation](https://api-explorer.cometchat.com/reference/notifications-logs).
+- **Dashboard:** Browse recent logs and narrow by channel, event, user, or status.
+- **API:** Use the Get logs API with query parameters (event, medium, receiver, status, time window, provider) for deeper filtering. See the [API explorer](https://api-explorer.cometchat.com/reference/notifications-logs) for the full list.
diff --git a/notifications/preferences-templates-sounds.mdx b/notifications/preferences.mdx
similarity index 59%
rename from notifications/preferences-templates-sounds.mdx
rename to notifications/preferences.mdx
index 4146939f..1c7acdcd 100644
--- a/notifications/preferences-templates-sounds.mdx
+++ b/notifications/preferences.mdx
@@ -1,12 +1,40 @@
---
-title: "Preferences, Templates & Sounds"
+title: "Preferences"
---
-## Common Preferences
+Preferences define how CometChat notifies users and which parts they can change.
-Login to CometChat dashboard and navigate to the Notifications section.
+Set app-wide defaults in **Dashboard -> Notifications -> Preferences**, then decide which settings end users can override. The SDK returns the merged view (admin defaults + user choices) and silently ignores updates to non-overridable fields so admin rules always win.
-Under Preferences tab, set the event preferences at the CometChat app-level and decide if users have the capability to override these settings. When **"Override"** toggle is enabled, users will have the capability to modify the default value that has been set.
+
+
+
+
+**How it flows**
+- Admin sets defaults and override rules in the dashboard.
+- Users inherit those defaults until they change a setting the admin allows.
+- `CometChatNotifications.fetchPreferences()` returns the current merged view.
+- `CometChatNotifications.updatePreferences()` saves allowed changes and returns the updated snapshot.
+
+**Key SDK helpers**
+- `CometChatNotifications.fetchPreferences()` / `updatePreferences()`
+- `CometChatNotifications.getMutedConversations()` / `muteConversations()` / `unmuteConversations()`
+- `CometChatNotifications.resetPreferences()` to restore dashboard defaults
+
+Each section pairs the dashboard defaults with matching client-side snippets so you can mirror the same behavior in your app.
+
+## Push payload message options
+
+These control what CometChat includes in push payloads. All are optional toggles:
+
+- Include entire message object in payload (largest footprint)
+- Include message metadata in payload
+- Include sender's metadata in payload
+- Include receiver's metadata in payload
+- Trim CometChat message object (strip down to stay within provider limits)
+- Additional data in payload (JSON you add)
+
+Use a minimal combination to stay under ~4 KB for FCM/APNs.
### Group preferences
@@ -38,6 +66,8 @@ Push notifications should be triggered for the message edited and message delete
#### Client-side implementation
+The fetch/update pattern shown here applies to group, one-on-one, mute, and schedule preferences; only the preference classes change.
+
**1. Fetch group preferences**
`CometChatNotifications.fetchPreferences()` method retrieves the notification preferences as an instance of `NotificationPreferences` class. If the user has not configured any preferences, the default preferences defined by the CometChat administrator via the dashboard will be returned.
@@ -151,7 +181,7 @@ CometChatNotifications.fetchPreferences(
`CometChatNotifications.updatePreferences()` method is used to update a user's notification preferences. The "**override**" toggle defined in the dashboard is crucial when updating preferences. If any preference is non-overridable, the method doesn't generate an error; it instead returns the `NotificationPreferences` object with the updated values where overrides are allowed.
-This functionality can be beneficial for temporarily superseding certain user preferences to ensure notifications for a specific event are delivered. Nonetheless, it is advisable to use this approach temporarily to avoid confusing users with unexpected changes to their notification settings.
+Use this sparingly, only when you must temporarily override preferences for a critical event, so users are not surprised by unexpected notification changes.
It is unnecessary to specify all values; only set and save the preferences that have been changed.
@@ -190,7 +220,7 @@ groupPreferences.setMemberScopeChangedPreference(
// Load the updates in the NotificationPreferences instance.
updatedPreferences.setGroupPreferences(groupPreferences);
-// Update the preferences and receive the udpated copy.
+// Update the preferences and receive the updated copy.
const preferences = await CometChatNotifications.updatePreferences(
updatedPreferences
);
@@ -319,8 +349,8 @@ As the name suggests, these preferences help you to configure Notifications for
| --------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| Conversations | New messages | • Don't notify • **Notify for all messages (Default)** • Notify for messages with mentions | • **Yes (Default)** • No |
| | New replies | • Don't notify • **Notify for all replies (Default)** • Notify for replies with mentions | • **Yes (Default)** • No |
-| Message actions | Message is edited | • Don't notify • **Notify (Default)** | - Yes • **No (Default)** |
-| | Message is deleted | • Don't notify • **Notify (Default)** | - Yes • **No (Default)** |
+| Message actions | Message is edited | • Don't notify • **Notify (Default)** | • Yes • **No (Default)** |
+| | Message is deleted | • Don't notify • **Notify (Default)** | • Yes • **No (Default)** |
| | Message receives a reaction | • Don't notify • Notify for reactions received on all messages • **Notify for reactions received on own messages (Default)** | • **Yes (Default)** • No |
@@ -416,7 +446,7 @@ CometChatNotifications.fetchPreferences(
`CometChatNotifications.updatePreferences()` method is used to update a user's notification preferences. The "**override**" toggle defined in the dashboard is crucial when updating preferences. If any preference is non-overridable, the method doesn't generate an error; it instead returns the `NotificationPreferences` object with the updated values where overrides are allowed.
-This functionality can be beneficial for temporarily superseding certain user preferences to ensure notifications for a specific event are delivered. Nonetheless, it is advisable to use this approach temporarily to avoid confusing users with unexpected changes to their notification settings.
+Use this sparingly, only when you must temporarily override preferences for a critical event, so users are not surprised by unexpected notification changes.
It is unnecessary to specify all values; only set and save the preferences that have been changed.
@@ -440,7 +470,7 @@ oneOnOnePreferences.setReactionsPreference(ReactionsOptions.DONT_SUBSCRIBE);
// Load the updates in the NotificationPreferences instance.
updatedPreferences.setOneOnOnePreferences(oneOnOnePreferences);
-// Update the preferences and receive the udpated copy.
+// Update the preferences and receive the updated copy.
const preferences = await CometChatNotifications.updatePreferences(
updatedPreferences
);
@@ -543,7 +573,7 @@ CometChatNotifications.updatePreferences(updatedPreferences,
#### Dashboard configuration
-These preferences allow you to control whether the users will be able to modify mute preferences.
+These preferences control whether end users can change mute settings in the client.
| Mute preferences | Can user configure? |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
@@ -661,7 +691,9 @@ CometChatNotifications.getMutedConversations(
`CometChatNotifications.updatePreferences()` method is used to update a user's notification preferences. The "**override**" toggle defined in the dashboard is crucial when updating preferences. If any preference is non-overridable, the method doesn't generate an error; it instead returns the `NotificationPreferences` object with the updated values where overrides are allowed.
-This functionality can be beneficial for temporarily superseding certain user preferences to ensure notifications for a specific event are delivered. Nonetheless, it is advisable to use this approach temporarily to avoid confusing users with unexpected changes to their notification settings. It is unnecessary to specify all values; only set and save the preferences that have been changed.
+Use this sparingly, only when you must temporarily override preferences for a critical event, so users are not surprised by unexpected notification changes.
+
+It is unnecessary to specify all values; only set and save the preferences that have been changed.
**To mute** one or more group or one-on-one conversations, utilize the `CometChatNotifications.muteConversations()` method. This method requires an array of `MutedConversation` objects, each containing the following properties:
@@ -695,7 +727,7 @@ mutePreferences.setDNDPreference(DNDOptions.ENABLED);
// Load the updates in the NotificationPreferences instance.
updatedPreferences.setMutePreferences(mutePreferences);
-// Update the preferences and receive the udpated copy.
+// Update the preferences and receive the updated copy.
const notificationPreferences = await CometChatNotifications.updatePreferences(
updatedPreferences
);
@@ -937,13 +969,13 @@ Notifications will be delivered based on the specified daily timetable, adhering
| Day | From | To | Can user override? |
| --------- | ----------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------- |
-| Monday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | • **Yes (Default)** - *Users can configure a personalized notification schedule.* • No |
-| Tuesday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
-| Wednesday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
-| Thursday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
-| Friday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
-| Saturday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
-| Sunday | • 0 to 2359 **(Default: 0)** • None | • Upto 2359 **(Default: 2359)** | |
+| Monday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | • **Yes (Default)** - *Users can configure a personalized notification schedule.* • No |
+| Tuesday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
+| Wednesday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
+| Thursday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
+| Friday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
+| Saturday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
+| Sunday | • 0 to 2359 **(Default: 0)** • None | • Up to 2359 **(Default: 2359)** | |
#### Client-side implementation
@@ -989,7 +1021,7 @@ CometChatNotifications.fetchPreferences(new CometChat.CallbackListener() {
@Override
public void onSuccess(NotificationPreferences defaultPreferences) {
- // Display the defaultPreferneces.
+ // Display the defaultPreferences.
}
@Override
@@ -1280,540 +1312,3 @@ CometChatNotifications.resetPreferences(onSuccess: (defaultPreferences) {
-
-## Push notification preferences
-
-The notification payload sent to FCM, APNs, or custom providers can be customized to include the CometChat message object for new messages and replies. To comply with the 4 KB payload size limit required by FCM and APNs, specific parts of the message object can be excluded to reduce the payload size. Additionally, a custom payload in the form of a JSON object can be included in the push payload.
-
-
-
-
-
-| Payload setting | Available preferences |
-| -------------------------------- | ----------------------------------------------------------------- |
-| Include CometChat message object | • **false (Default)** • true |
-| Include Sender's metadata | • false • **true (Default)** |
-| Include Receiver's metadata | • false • **true (Default)** |
-| Include message metadata | • false • **true (Default)** |
-| Trim CometChat text message | • **false (Default)** • true |
-| Custom JSON | No defaults for this value. If not set, this key is not included. |
-
-## Email notification preferences
-
-| Preference | Values | Description |
-| -------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Notify for unread messages only | • **true (Default)** • false | • Email notifications are sent only when there are unread messages in a conversation. • When set to `false`, the notifications are sent irrespective of whether there are unread messages or not. |
-| The interval between two emails (in minutes) | 120 | • By default, the notifications are triggered after 120 minutes. • The minimum allowed value is 1 minute. • The maximum is 1440 minutes (24 hours). |
-| Maximum emails per day | 20 | • By default, a maximum of 20 email notifications can be sent to a user on a given day. • The minimum value can be set to 1. • The maximum can be 30. |
-| Maximum emails per conversation per day | 2 | • By default, a maximum of 2 email notifications can be sent to a user for a given conversation on a given day. • The minimum value can be set to 1. • The maximum can be 30. |
-| Include CometChat message object | • **false (Default)** • true | If enabled, the message object will be included in the email notification payload. |
-| Include Sender's metadata | • **false (Default)** • true | If enabled, the sender's metadata will be included in the message object (applicable only when the message object is included). |
-| Include Receiver's metadata | • **false (Default)** • true | If enabled, the receiver's metadata will be included in the message object (applicable only when the message object is included). |
-| Include message metadata | • **false (Default)** • true | If enabled, the message metadata will be included in the message object (applicable only when the message object is included). |
-
-## SMS notification preferences
-
-| Preference | Values | Description |
-| -------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Notify for unread messages only | • **true (Default)** • false | • SMS notifications are sent only when there are unread messages in a conversation. • When set to `false`, the notifications are sent irrespective of whether there are unread messages or not. |
-| The interval between two emails (in minutes) | 120 | • By default, the notifications are triggered after 120 minutes. • The minimum allowed value is 1 minute. • The maximum is 1440 minutes (24 hours). |
-| Maximum SMS per day | 20 | • By default, a maximum of 20 SMS notifications can be sent to a user on a given day. • The minimum value can be set to 1. • The maximum can be 30. |
-| Maximum SMS per conversation per day | 2 | • By default, a maximum of 2 SMS notifications can be sent to a user for a given conversation on a given day. • The minimum value can be set to 1. • The maximum can be 30. |
-| Include CometChat message object | • **false (Default)** • true | If enabled, the message object will be included in the SMS notification payload. |
-| Include Sender's metadata | • **false (Default)** • true | If enabled, the sender's metadata will be included in the message object (applicable only when the message object is included). |
-| Include Receiver's metadata | • **false (Default)** • true | If enabled, the receiver's metadata will be included in the message object (applicable only when the message object is included). |
-| Include message metadata | • **false (Default)** • true | If enabled, the message metadata will be included in the message object (applicable only when the message object is included). |
-
-## Common templates and sounds
-
-Templates are designed to specify the content displayed in notifications on the user's device for different events.
-
-Templates incorporate `placeholders`, which reference specific pieces of information determined by properties from the event.
-
-**For example**, New message event has the following structure:
-
-```json
-{
- "data": {
- "id": "17",
- "conversationId": "group_cometchat-guid-1",
- "sender": "cometchat-uid-2",
- "receiverType": "group",
- "receiver": "cometchat-guid-1",
- "category": "message",
- "type": "text",
- "data": {
- "text": "Hello! How are you?",
- "entities": {
- "sender": {
- "entity": {
- "uid": "cometchat-uid-2",
- "name": "George Alan",
- "role": "default",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp",
- "status": "available",
- "lastActiveAt": 1707901272
- },
- "entityType": "user"
- },
- "receiver": {
- "entity": {
- "guid": "cometchat-guid-1",
- "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp",
- "name": "Hiking Group",
- "type": "public",
- "owner": "cometchat-uid-1",
- "createdAt": 1706014061,
- "conversationId": "group_cometchat-guid-1",
- "onlineMembersCount": 3
- },
- "entityType": "group"
- }
- },
- },
- "sentAt": 1707902030,
- "updatedAt": 1707902030
- }
-}
-```
-
-The sender's name is accessible via `data.entities.sender.name`, so the placeholder for the sender's name will be `{{message.data.entities.sender.name}}`. This `placeholder` is substituted within the template with the actual name of the sender aka the `substitution value`.
-
-As an administrator, you can configure:
-
-1. **Default templates** - Use these templates to display previews by leveraging the information contained in the event.
-2. **Privacy templates** - Employ these templates to present generic content in the notification.
-
-### Privacy setting
-
-#### Dashboard configuration
-
-Configure which template will be used for displaying the content of the notifications displayed on user's devices. The available preferences are:
-
-1. Use default template - Enforces the use of default templates for all the users.
-2. Use privacy template - Enforces the use of privacy templates for all the users.
-3. **Use default templates with user privacy override (Default)** - Uses default templates by default, but allows the users to enable privacy to hide the previews.
-
-#### Client-side implementation
-
-**1. Fetch privacy setting**
-
-The method `CometChatNotifications.fetchPreferences()` retrieves the notification preferences saved by the user as an instance of `NotificationPreferences` class. If the user has not configured any preferences, the default preferences defined by the CometChat administrator via the dashboard will be utilized.
-
-
-
-```js
-// This is applicable for web, React native, Ionic cordova
-const preferences = await CometChatNotifications.fetchPreferences();
-
-// Display a toggle for use privacy option TODO
-const usePrivacyTemplate = preferences.getUsePrivacyTemplate();
-```
-
-
-
-
-```kotlin
-CometChatNotifications.fetchPreferences(new CometChat.CallbackListener() {
- @Override
- public void onSuccess(NotificationPreferences notificationPreferences) {
- // Display a toggle for use privacy option
- boolean usePrivacyTemplate = notificationPreferences.getUsePrivacyTemplate();
- }
-
- @Override
- public void onError(CometChatException e) {
- // Something went wrong while fetching notification preferences
- }
-});
-```
-
-
-
-
-```swift
-CometChatNotifications.fetchPreferences { notificationPreferences in
- // Display a toggle for use privacy option
- let usePrivacyTemplate = notificationPreferences.usePrivacyTemplate;
-
-} onError: { error in
- // Something went wrong while fetching notification preferences.
- print("fetchPreferences: \(error.errorCode) \(error.errorDescription)");
-}
-```
-
-
-
-
-```dart
-CometChatNotifications.fetchPreferences(
- onSuccess: (notificationPreferences) {
- // Display a toggle for use privacy option
- bool? usePrivacyTemplate = notificationPreferences.usePrivacyTemplate;
-
- },
- onError: (e) {
- debugPrint("fetchPreferences:error ${e.toString()}");
- });
-```
-
-
-
-
-
-**2. Update privacy setting**
-
-`CometChatNotifications.updatePreferences()` method is used to update a user's notification preferences. The "**override**" toggle defined in the dashboard is crucial when updating preferences. If any preference is non-overridable, the method doesn't generate an error; it instead returns the `NotificationPreferences` object with the updated values where overrides are allowed.
-
-This functionality can be beneficial for temporarily superseding certain user preferences to ensure notifications for a specific event are delivered. Nonetheless, it is advisable to use this approach temporarily to avoid confusing users with unexpected changes to their notification settings.
-
-It is unnecessary to specify all values; only set and save the preferences that have been changed.
-
-
-
-```js
-// This is applicable for web, React native, Ionic cordova
-// The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
-
-// Instantiate the NotificationPreferences.
-const updatedPreferences = new NotificationPreferences();
-
-// To update the preference for privacy template
-updatedPreferences.setUsePrivacyTemplate(true);
-
-// Update the preferences and receive the udpated copy.
-const notificationPreferences = await CometChatNotifications.updatePreferences(
- updatedPreferences
-);
-```
-
-
-
-
-```kotlin
-// The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
-// Instantiate the NotificationPreferences.
-NotificationPreferences updatedPreferences = new NotificationPreferences();
-
-// To update the preference for privacy template
-updatedPreferences.setUsePrivacyTemplate(true);
-
-// Update the preferences.
-CometChatNotifications.updatePreferences(updatedPreferences, new CometChat.CallbackListener() {
- @Override
- public void onSuccess(NotificationPreferences notificationPreferences) {
- // Updated notificationPreferences
- }
-
- @Override
- public void onError(CometChatException e) {
- // Something went wrong
- }
-});
-```
-
-
-
-
-```swift
-// The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
-
-// Instantiate the NotificationPreferences.
-let updatedPreferences = CometChatNotifications.NotificationPreferences();
-
-// To update the preference for privacy template
-updatedPreferences.set(usePrivacyTemplate: true)
-
-// Update the preferences.
-CometChatNotifications.updatePreferences(updatedPreferences) { prefs in
- print("updatePreferences: \(prefs)")
-} onError: { error in
- print("updatePreferences: \(error.errorCode) \(error.errorDescription)")
-}
-```
-
-
-
-
-```dart
-// The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
-
-// Instantiate the NotificationPreferences
-NotificationPreferences updatedPreferences = NotificationPreferences();
-
-// To update the preference for privacy template
-updatedPreferences.usePrivacyTemplate = true;
-
-// Update the preferences.
-CometChatNotifications.updatePreferences(updatedPreferences,
- onSuccess: (preferencesAfterUpdate) {
- debugPrint("updatePreferences:success");
- // Use the preferencesAfterUpdate
-}, onError: (e) {
- debugPrint("updatePreferences:error: ${e.toString()}");
-});
-```
-
-
-
-
-
-### Text message templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | `{{message.data.text}}` | New message |
-
-### Media message templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body for Image | 📷 Has sent an image | New image message |
-| Body for Audio | 🔈 Has sent an audio | New audio message |
-| Body for Audio | 🎥 Has sent a video | New video message |
-| Body for Audio | 📄 Has sent a file | New file message |
-
-### Custom message templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | `{{message.data.text}}` | `{{message.data.text}}` |
-| Body (Fallback) | New message | New message |
-
-**Note:** The "Body (Fallback)" value is utilized when any placeholders within the "Body" fail to resolve to an appropriate substitution value.
-
-**For example**, if `{{message.data.text}}` in the aforementioned scenario evaluates to `null` or `undefined`, the "Body (Fallback)" value will be utilized.
-
-Ideally, the "Body (Fallback)" value should not contain any placeholders to prevent additional resolution failures.
-
-### Interactive form templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | `{{data.interactiveData.title}}` | New message |
-
-### Interactive card templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | `{{data.interactiveData.title}}` | New message |
-
-### Interactive scheduler templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | New invite | New invite |
-
-### Custom Interactive message templates
-
-| Template for | Default template values | Privacy template values |
-| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
-| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
-| Body | New message | New message |
-
-### Sounds
-
-The sound files must be included within the app's bundle. These values are set within the notification payload as values of the "sound" field.
-
-**Sound for Call Notifications:** Specify the name of the sound file you wish to play for call notifications.
-
-**Sound for Chat Notifications:** Specify the name of the sound file you wish to play for chat notifications.
-
-## Email notification templates
-
-You can use a default template or a privacy template in case you consider the information to be displayed as sensitive. The data available for email's subject template is as follows:
-
-
-
-```json
-{
- "to": {
- "uid": "cometchat-uid-1",
- "name": "Andrew Joseph",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Are we meeting on this weekend?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "📷 Has shared an image",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "senderDetails": {
- "uid": "cometchat-uid-4",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
- }
-}
-```
-
-
-
-
-```json
-{
- "to": {
- "uid": "cometchat-uid-1",
- "name": "Andrew Joseph",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Hello all! What's up?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "This is the place I was thinking about",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "groupDetails": {
- "guid": "cometchat-guid-1",
- "name": "Hiking Group",
- "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
- }
-}
-```
-
-
-
-
-
-Considering the above data, an email's subject can be formatted as follows:
-
-| Subject for | Template | Final subject |
-| ----------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
-| Group notification | Hello `{{to.name}}`! You have `{{messages.length}}` message(s) in `{{groupDetails.name}}`. | Hello **Andrew Joseph**! You have **2** message(s) in **Hiking Group**. |
-| One-on-one notification | Hello `{{to.name}}`! You have `{{messages.length}}` message(s) from `{{senderDetails.name}}`. | Hello **Andrew Joseph**! You have **2** message(s) from **Susan Marie**. |
-
-## SMS notification templates
-
-You can use a default template or a privacy template in case you consider the information to be displayed as sensitive. The data available for SMS template is as follows:
-
-
-
-```json
-{
- "to": {
- "uid": "cometchat-uid-1",
- "name": "Andrew Joseph",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Are we meeting on this weekend?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "📷 Has shared an image",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "senderDetails": {
- "uid": "cometchat-uid-4",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
- }
-}
-```
-
-
-
-
-```json
-{
- "to": {
- "uid": "cometchat-uid-1",
- "name": "Andrew Joseph",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Hello all! What's up?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "This is the place I was thinking about",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "groupDetails": {
- "guid": "cometchat-guid-1",
- "name": "Hiking Group",
- "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
- }
-}
-```
-
-
-
-
-
-Considering the above data, an SMS can be formatted as follows:
-
-| SMS for | Template | Final content |
-| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
-| Group notification | You've received `{{messages.length}}` message(s) in `{{groupDetails.name}}`! Read them at [https://your-website.com](https://your-website.com). | You've received **2** message(s) in Hiking Group! Read them at [https://your-website.com](https://your-website.com). |
-| One-on-one notification | You've received `{{messages.length}}` message(s) from `{{senderDetails.name}}`! Read them at [https://your-website.com/chat](https://your-website.com/chat). | You've received **2** message(s) from **Susan Marie**! Read them at [https://your-website.com/chat](https://your-website.com/chat). |
-
-
-
-Replace [https://your-website.com/chat](https://your-website.com/chat) with the URL of your actual website.
-
-
diff --git a/notifications/push-customization.mdx b/notifications/push-customization.mdx
deleted file mode 100644
index fac1ed46..00000000
--- a/notifications/push-customization.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: "Customizations"
----
-
-Customizations allow controlling notifications for message events, group events, and call events. Users can set preferences for incoming notifications based on notification schedules, Do Not Disturb (DND) mode, and the mute status of specific conversations.
-
-Additional customizations include modifications to notification templates and sounds. These options also ensure that user privacy is maintained while displaying notifications on the device.
-
-For more information, refer to [Preferences, Templates & Sounds](/notifications/preferences-templates-sounds) documentation.
diff --git a/notifications/push-integration.mdx b/notifications/push-integration.mdx
index 32f39f99..57de2233 100644
--- a/notifications/push-integration.mdx
+++ b/notifications/push-integration.mdx
@@ -1,19 +1,19 @@
---
-title: "Integration"
+title: "Integration (TO BE REMOVED)"
---
-
-## Enable Push notifications
+
+ ## Step 1: Enable Push notifications
1. Login to [CometChat](https://app.cometchat.com/login) dashboard and select your app.
-2. Navigate to **Notifications** > **Notifications** in the left-hand menu.
+2. Navigate to **Notifications** > **Settings** in the left-hand menu.
3. Enable the Push notifications feature.
-4. Continue to configure Push notifications by clicking on "Configure".
-
-## Add Providers
+4. Continue to configure Push notifications.
+
+## Step 2: Add Providers
Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNS) are the two primary supported providers for sending push notifications.
@@ -32,7 +32,7 @@ Generate a service account key for your Firebase application by navigating to th
1. Select the "+ Add Credentials" button.
2. In the dialogue that appears, provide a unique, memorable identifier for your provider.
3. Upload the service account JSON file you previously acquired.
-4. Specify whether the push payload should include the "notification" key. Additional information on this configuration is available in FCM's documentation - [About FCM messages](https://firebase.google.com/docs/cloud-messaging/concept-options).
+4. Specify whether the push payload should include the "notification" key. Additional information on this configuration is available in FCM's documentation - [About FCM messages](https://firebase.google.com/docs/cloud-messaging/get-started).
Similarly, you can add multiple FCM Credentials in case you have multiple FCM projects for your apps.
@@ -44,7 +44,7 @@ Similarly, you can add multiple FCM Credentials in case you have multiple FCM pr
#### Pre-requisite
-1. To generate a .p8 key file, go to [Apple developer account](https://developer.apple.com/account), then select "Certificates, IDs & Profiles".
+1. To generate a .p8 key file, go to [Apple Developer Account](https://developer.apple.com/account), then select "Certificates, IDs & Profiles".
2. Select Keys and click on the "+" button to add a new key.
3. In the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click "Continue" and click "Register".
4. Then proceed to download the key file by clicking Download.
@@ -68,291 +68,11 @@ Similarly, you can add multiple APNS Credentials in case you have multiple apps
-### Add Custom provider credentials
-
-Custom providers allow you to make use of providers apart from FCM and APNs. This is implemented using webhook URL which gets all the required details that can be used to trigger Push notifications.
-
-#### Pre-requisite
-
-1. Your webhook endpoint must be accessible over `HTTPS`. This is essential to ensure the security and integrity of data transmission.
-2. This URL should be publicly accessible from the internet.
-3. Ensure that your endpoint supports the `HTTP POST` method. Event payloads will be delivered via `HTTP POST` requests in `JSON` format.
-4. Configure your endpoint to respond immediately to the CometChat server with a 200 OK response. The response should be sent within 2 seconds of receiving the request.
-5. For security, it is recommended to set up Basic Authentication that is usually used for server-to-server calls. This requires you to configure a username and password. Whenever your webhook URL is triggered, the HTTP Header will contain:
-
-```html
-Authorization: Basic
-```
-
-6. Your frontend application should implement the logic to get the push token and register it to your backend when the user logs in and unregister the push token when the user logs out.
-7. To enable multi-device logins, you can map the push tokens to the user's auth tokens. Where each new login makes use of a new auth token.
-
-#### Add credentials
-
-1. Click on the "+ Add Credentials" button.
-2. Enable the provider.
-3. Enter the publically accessible Webhook URL.
-4. It is recommended to enable Basic Authentication.
-5. Enter the username and password.
-6. Save the credentials.
-
-
-
-
-
-#### How does it work?
-
-The Custom provider is triggered once for an event in one-on-one conversation. In case of notifying the members of a group, the custom provider is triggered once for each user present in that group.
-
-For example, if there are 100 members in the group, your webhook will receive 100 HTTP requests. Once for each member of the group.
+### Custom providers
-Below are the sample payloads for different events:
+For webhook-based providers and payload samples, see [Custom Providers](/notifications/custom-providers).
-
-
-```json
-{
- "trigger": "push-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-2"
- },
- "notificationDetails": {
- // Notification details
- "title": "Andrew Joseph", // The title of the notification to be displayed
- "body": "Hello!", // The body of the notification to be displayed
-
- // Sender's details
- "sender": "cometchat-uid-1", // UID of the user who sent the message.
- "senderName": "Andrew Joseph", // Name of the user who sent the message.
- "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp", // Avatar URL of the user.
-
- // Receiver's details
- "receiver": "cometchat-uid-2", // UID or GUID of the receiver.
- "receiverName": "George Alan", // Name of the user or group.
- "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp", // Avatar URL of the receiver.
- "receiverType": "user", // Values can be "user" or "group"
-
- // Message details
- "tag": "123", // The ID of the message that can be used as the ID of the notification to be displayed.
- "conversationId": "cometchat-uid-1_user_cometchat-uid-2", // The ID of the conversation that the message belongs to.
- "type": "chat",
- "sentAt": "1741847453000",
- "message": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited or deleted.
- "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
- }
- },
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-```json
-{
- "trigger": "push-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-2"
- },
- "notificationDetails": {
- // Notification details
- "title": "Caller", // The title of the notification to be displayed
- "body": "AUDIO CALL", // "AUDIO CALL" or "VIDEO CALL"
-
- // Sender's details
- "sender": "cometchat-uid-1", // UID of the user who sent the message.
- "senderName": "Andrew Joseph", // Name of the user who sent the message.
- "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp", // Avatar URL of the user.
-
- // Receiver's details
- "receiver": "cometchat-uid-2", // UID or GUID of the receiver.
- "receiverName": "George Alan", // Name of the user or group.
- "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp", // Avatar URL of the receiver.
- "receiverType": "user", // "user" or "group"
-
- // Message details
- "tag": "123", // The ID of the message that can be used as the ID of the notification to be displayed.
- "conversationId": "cometchat-uid-1_user_cometchat-uid-2", // The ID of the conversation that the call belongs to.
- "type": "call",
-
- // Call details
- "callAction": "initiated", // "initiated" or "cancelled" or "unanswered" or "ongoing" or "rejected" or "ended" or "busy"
- "sessionId": "v1.123.aik2", // The unique sessionId of the call that can be used as an identifier in CallKit or ConnectionService.
- "callType": "audio", // "audio" or "video"
- "sentAt": "1741847453000",
- "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
- }
- },
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-```json
-{
- "trigger": "push-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-2"
- },
- "notificationDetails": {
- // Notification details
- "title": "Andrew Joseph",
- "body": "Reacted to your message: 😎",
-
- // Sender's details
- "sender": "cometchat-uid-1",
- "senderName": "Andrew Joseph",
- "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
-
- // Receiver's details
- "receiver": "cometchat-uid-1",
- "receiverName": "Andrew Joseph",
- "receiverAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
- "receiverType": "user",
-
- // Message details
- "tag": "58",
- "conversationId": "cometchat-uid-1_user_cometchat-uid-2",
- "type": "chat",
- "sentAt": "1741847453000",
- "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
- }
- },
- "appId": "app123",
- "region": "us",
- "webhook": "custom"
-}
-```
-
-
-
-
-```json
-{
- "trigger": "push-notification-payload-generated",
- "data": {
- "to": {
- "uid": "g-messages-none"
- },
- "notificationDetails": {
- // Notification details
- "title": "Hiking group",
- "body": "Andrew Joseph has left", // Similarly for joined, kicked, banned, unbanned, added events
-
- // Sender details
- "sender": "cometchat-uid-1",
- "senderName": "Andrew Joseph",
- "senderAvatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp",
-
- // Receiver details
- "receiver": "cometchat-guid-1",
- "receiverName": "Hiking group",
- "receiverType": "group",
-
- // Message details
- "tag": "cometchat-guid-1",
- "conversationId": "group_cometchat-guid-1",
- "type": "chat",
- "sentAt": "1741847453000",
- "custom": {Custom JSON} // Custom JSON object is added in case it is configured in the preferences.
- }
- },
- "appId": "app123",
- "region": "us",
- "webhook": "custom"
-}
-```
-
-
-
-
-
-#### Sample server-side code
-
-```typescript
-const express = require('express');
-const app = express();
-const PORT = process.env.PORT || 3000;
-
-app.use(express.json());
-
-// Optional: Basic authentication middleware
-const basicAuth = (req, res, next) => {
- const authHeader = req.headers['authorization'];
- if (!authHeader || !authHeader.startsWith('Basic ')) {
- return res.status(401).json({ message: 'Unauthorized' });
- }
- next();
-};
-
-const triggerPushNotification = async (to, notificationDetails) => {
- const { name, uid } = to;
- const { type, notificationTitle, notificationBody } = notificationDetails;
-
- if (type == 'call') {
- console.log('Push notification for calling event');
- // Use the following details to send a call notification.
- const { callAction, sessionId, callType } = notificationDetails;
- }
-
- if (type == 'chat') {
- console.log('Push notification for messaging event');
- }
-
- const token = await fetchPushToken(uid);
-
- // Your implementation for sending the Push notification
- await sendPushNotification(token, notificationTitle, notificationBody);
-};
-
-app.post('/webhook', basicAuth, (req, res) => {
- const { trigger, data, appId, region, webhook } = req.body;
-
- if (
- trigger !== 'push-notification-payload-generated' ||
- webhook !== 'custom'
- ) {
- return res.status(400).json({ message: 'Invalid trigger or webhook type' });
- }
-
- console.log('Received Webhook:', JSON.stringify(req.body, null, 2));
-
- triggerPushNotification(to, data)
- .then((result) => {
- console.log(
- 'Successfully triggered Push notification for',
- appId,
- to.uid,
- result
- );
- })
- .catch((error) => {
- console.error(
- 'Something went wrong while triggering Push notification for',
- appId,
- to.uid,
- error.message
- );
- });
-
- res.status(200).json({ message: 'Webhook received successfully' });
-});
-
-app.listen(PORT, () => {
- console.log(`Server is running on port ${PORT}`);
-});
-```
-
-## Tokens management
+## Step 3: Tokens management
### Register push token after login
@@ -617,7 +337,7 @@ CometChatNotifications.unregisterPushToken(onSuccess: (response) {
-## Handle incoming Push notifications
+## Step 4: Handle incoming Push notifications
Push notifications should be managed primarily when the app is in the background or in a terminated state.
@@ -665,50 +385,47 @@ The push payload delivered to the user's device includes the following informati
-## Sample apps
-Check out our sample apps for understanding the implementation.
+## Continue to platform-specific implementation
-
+Choose your platform to continue with the Push notifications implementation.
-
+
-Push notifications sample app for the web
+} href="/notifications/android-push-notifications">
-View on Github
+Android UI Kit Implementation
-
-
-Push notifications sample app for React Native
+} href="/notifications/ios-apns-push-notifications">
-View on Github
+iOS (APNS) UI Kit Implementation
-
+} href="/notifications/ios-fcm-push-notifications">
-Push notifications sample app for iOS
-
-View on Github
+iOS (FCM) UI Kit Implementation
-
-
-Push notifications sample app for Android
+} href="/notifications/flutter-push-notifications-android">
-View on Github
+Flutter (Android) UI Kit Implementation
-
+} href="/notifications/flutter-push-notifications-ios">
-Push notifications sample app for Flutter
+Flutter (iOS) UI Kit Implementation
-View on Github
+
+
+} href="/notifications/react-native-push-notifications">
+
+React Native UI Kit Implementation
-
-
+
+
\ No newline at end of file
diff --git a/notifications/push-notification-extension-overview.mdx b/notifications/push-notification-extension-overview.mdx
index 1ac85c41..a8ee42a8 100644
--- a/notifications/push-notification-extension-overview.mdx
+++ b/notifications/push-notification-extension-overview.mdx
@@ -1,5 +1,5 @@
---
-title: "Push Notification Extension (Legacy)"
+title: "Push Notification Extension"
---
diff --git a/notifications/push-overview.mdx b/notifications/push-overview.mdx
index 3490b8a1..09e21c6c 100644
--- a/notifications/push-overview.mdx
+++ b/notifications/push-overview.mdx
@@ -1,61 +1,69 @@
---
title: "Overview"
+description: "CometChat Push Notifications, their capabilities, common triggers, and how to integrate them into your application."
---
-## Introduction
+CometChat keeps users engaged when they are not in the app.
+CometChat listens for chat and call events, assembles payloads from your templates, and delivers via FCM or APNs so taps land in the right conversation or call screen.
-Push notifications are a crucial aspect of modern applications, providing real-time updates and enhancing user engagement. CometChat's Push Notification service offers a range of features and customization options to enable businesses to deliver timely alerts and keep users connected to their applications.
+## How it works at a glance
-
-
-
+- **Providers + tokens**: Add FCM/APNs (and APNs VoIP) providers in the CometChat dashboard. Register device tokens in your app after login; unregister on logout.
+- **Templates + payloads**: Customize notification text and sounds per message/call type (see Templates & Sounds). CometChat builds the payload and ships it to your provider.
+- **Delivery guardrails**: Routing, retries, quiet hours, and DND/mutes are honored automatically; logs help you trace delivery.
+- **Navigation**: Deep links open the right user/group/thread; call pushes can surface native call UI on supported platforms.
-## Key Features
+## Key capabilities
-1. **Support for multiple providers**:
+- Multi-platform coverage: Android, iOS (APNs or FCM), Flutter, React Native, and Web.
+- Message and call notifications with grouping, inline replies (where supported), and native call surfaces via CallKit/ConnectionService.
+- Preferences: per-user/per-conversation mutes, quiet hours, and global DND.
+- Timezone-aware scheduling for delayed/quiet-hour delivery.
+- Logging and troubleshooting from the dashboard.
- CometChat provides support for **Firebase Cloud Messaging (FCM)** and **Apple Push Notification Service (APNS)**. This approach, involving multiple providers, provides flexibility in notification delivery, independent of the recipient's platform. It also enables CometChat to respond effectively to the evolving push notification landscape, maintaining consistent delivery across Android, iOS, and web platforms.
+## Common triggers
-2. **Support for multiple platforms**:
+- New messages (1:1 and group), thread replies, mentions, and reactions (if enabled).
+- Message edits/deletes and admin/moderation actions (when you include them in templates).
+- Group membership changes (join/leave/ban).
+- Call invites, missed calls, and ongoing call updates.
- CometChat provides multi-platform support, compatible with an extensive array of mobile and web platforms. This includes native mobile platforms such as Android and iOS, web frameworks like React, Angular, and Vue.js, and hybrid environments including React Native and Flutter.
+## Before you integrate
-3. **Tokens management**:
+1. **Dashboard setup**: Enable Push Notifications, add providers (FCM for Android/iOS, APNs + optional APNs VoIP for iOS), and copy Provider IDs.
+2. **Credentials**: Keep App ID, Region, Auth Key handy. Upload the Firebase service account JSON and APNs `.p8` key (Team ID + Key ID).
+3. **Client wiring**: Register tokens after login and unregister on logout. Follow the platform guide below for Gradle/Info.plist/CallKeep specifics.
- CometChat's Push Notification service provides developers with functions and APIs for easy tokens management, ensuring that push notifications are delivered reliably to intended user's devices.
+## Choose your platform
-4. **Preferences management**:
+
- Through CometChat's Notification Preferences, users and admins have the ability to customize the notification settings, that help provide pertinent alerts while avoiding notification fatigue.
+} href="/notifications/android-push-notifications">
+UI Kit implementation
+
-5. **Ability to set up a schedule**:
+} href="/notifications/ios-apns-push-notifications">
+UI Kit implementation
+
- CometChat's Push notifications service ensures that the notifications are delivered based on the specified daily timetable, adhering to the user's local time zone.
+} href="/notifications/ios-fcm-push-notifications">
+UI Kit implementation
+
-6. **Ability to mute notifications**:
+} href="/notifications/flutter-push-notifications-android">
+UI Kit implementation
+
- Users have the option to completely mute push notifications for the app (DND mode), or selectively mute them for specific users and groups, for a designated duration.
+} href="/notifications/flutter-push-notifications-ios">
+UI Kit implementation
+
-7. **Ability to set up Templates and Sounds**:
+} href="/notifications/react-native-push-notifications">
+UI Kit implementation
+
- CometChat offers developers a set of pre-defined templates that define the content shown in push notifications. These templates act as a blueprint for customizing the payload content sent with push notifications as per the needs and requirements.
+} href="/notifications/web-push-notifications">
+UI Kit implementation
+
-***
-
-## Triggering Events
-
-In CometChat various user actions and interactions within the chat environment can trigger push notifications to ensure users stay updated and engaged. Here are some common events that typically trigger push notifications:
-
-* **New Messages**: Whenever a user sends a new message in a one-on-one or group chat, CometChat can trigger a push notification to alert other participants about the incoming message.
-
-* **Replies**: When a user replies to a specific message within a chat, it can trigger a push notification to notify relevant users about the reply, ensuring they are aware of the ongoing conversation.
-
-* **Message Edited or Deleted**: Notifications are triggered when a user edits or deletes a message, informing relevant users about the changes made to the message content.
-
-* **Mentions**: If a user is mentioned by another user using their username or handle in a message, CometChat can trigger a push notification to notify the mentioned user about the mention, prompting their attention to the message.
-
-* **Reactions**: Users can react to messages with emojis or symbols. When a user reacts to a message, CometChat can trigger a push notification to the original sender or other participants in the chat to notify them about the reaction.
-
-* **Group Actions**: Notifications are triggered for group actions such as member joins, member bans, and member leaves, ensuring group members are informed about changes in group dynamics.
-
-* **Calling Events**: CometChat supports real-time audio and video calling features. Events related to incoming calls, missed calls or call invitations can trigger push notifications to alert users about these calling events, ensuring they don't miss important calls.
+
diff --git a/notifications/react-native-push-notifications-legacy.mdx b/notifications/react-native-push-notifications-legacy.mdx
new file mode 100644
index 00000000..2388a221
--- /dev/null
+++ b/notifications/react-native-push-notifications-legacy.mdx
@@ -0,0 +1,1017 @@
+---
+title: "React Native"
+---
+
+Learn how to set up Push notifications for React Native using Firebase Cloud Messaging or FCM.
+
+
+ React Native Push notifications sample app
+
+ View on Github
+
+
+## Firebase Project Setup
+
+Visit [Firebase](https://console.firebase.google.com/) and login/signup using your Gmail ID.
+
+### Step 1: Create a new Firebase Project
+
+Head over to the [Firebase Console](https://console.firebase.google.com/) to create a new project.
+
+
+
+
+
+This is a simple 3 step process where:
+
+1. You give a name to your project
+2. Add Google Analytics to your project (Optional)
+3. Configure Google Analytics account (Optional)
+
+Click on Create and you are ready to go.
+
+### Step 2: Add Firebase to your App
+
+React native setup will require 2 files for Android and iOS:
+
+1. For Android, you need to download the google-services.json file from the Firebase console.
+2. For iOS, you need to download the GoogleService-Info.plist file from the Firebase console.
+
+### Step 3: Download the service account file
+
+
+
+
+
+## Extension settings
+
+### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications extension.
+3. Open up the settings and save the following settings.
+
+
+
+
+
+### Step 2: Save your settings
+
+On the Settings page you need to enter the following:
+
+1. **Set extension version**
+
+* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
+* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
+* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+
+2. **Select the platforms that you want to support**
+
+* Select from Web, Android, Ionic, React Native, Flutter & iOS.
+
+3. **Notification payload settings**
+
+* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
+
+4. **Push payload message options**
+
+
+
+
+
+The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+
+* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+
+5. **Notification Triggers**
+
+
+
+
+
+* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+* These are pretty self-explanatory and you can toggle them as per your requirement.
+
+## App Setup
+
+### Step 1: Initial plugin setup
+
+1. For React Native, there are numerous plugins available via NPM which can be used to set up push notifications for your apps. [react-native-firebase](https://www.npmjs.com/package/react-native-firebase) and [react-native-notifications](https://www.npmjs.com/package/react-native-notifications) are just the two out of many available.
+2. To setup Push Notification, you need to follow the steps mentioned in the Plugin's Documentation.
+
+At this point, you will have:
+
+1. Two separate apps created on the Firebase console. (For Android and iOS).
+2. Plugin setup completed as per the respective documentation and our reference.
+
+### Step 2: Register FCM Token
+
+1. This step assumes that you already have a React Native app setup with CometChat installed. Make sure that the CometChat object is initialized and user has been logged in.
+2. On the success callback of user login, you can fetch the FCM Token and register it with the extension as shown below:
+
+
+
+```js
+// Pseudo-code with async-await syntax
+
+const APP_ID = 'APP_ID';
+const REGION = 'REGION';
+const AUTH_KEY = 'AUTH_KEY';
+
+const UID = 'UID';
+const APP_SETTINGS = new CometChat.AppSettingsBuilder()
+ .subscribePresenceForAllUsers()
+ .setRegion(REGION)
+ .build();
+
+try {
+ // First initialize the app
+ await CometChat.init(APP_ID, APP_SETTINGS);
+
+ // Login the user
+ await CometChat.login(UID, AUTH_KEY);
+
+ // Get the FCM device token
+ // You should have imported the following in the file:
+ // import messaging from '@react-native-firebase/messaging';
+ const FCM_TOKEN = await messaging().getToken();
+
+ // Register the token with Push Notifications (Legacy)
+ await CometChat.registerTokenForPushNotification(FCM_TOKEN);
+} catch (error) {
+ // Handle errors gracefully
+}
+```
+
+
+
+
+
+3. Registration also needs to happen in case of token refresh as shown below:
+
+
+
+```js
+// Pseudo-code
+
+// You should have imported the following in the file:
+// import messaging from '@react-native-firebase/messaging';
+try {
+ // Listen to whether the token changes
+ return messaging().onTokenRefresh(FCM_TOKEN => {
+ await CometChat.registerTokenForPushNotification(FCM_TOKEN);
+ });
+ // ...
+} catch(error) {
+ // Handle errors gracefully
+}
+```
+
+
+
+
+
+For React Native Firebase reference, visit the link below:
+
+### Step 3: Receive Notifications
+
+
+
+```js
+// Pseudo-code
+import messaging from '@react-native-firebase/messaging';
+import { Alert } from 'react-native';
+
+// Implementation can be done in a life-cycle method or hook
+const unsubscribe = messaging().onMessage(async (remoteMessage) => {
+ Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
+});
+```
+
+
+
+
+
+
+
+We send Data Notifications and you need to handle displaying notifications at your end. For eg: Using Notifee
+
+
+
+### Step 4: Stop receiving Notifications
+
+1. Simply logout the CometChat user and you will stop receiving notifications.
+2. As a good practice, you can also delete the FCM Token by calling `deleteToken` on the messaging object.
+
+
+
+```js
+// Pseudo-code using async-await syntax
+
+logout = async () => {
+ // User logs out of the app
+ await CometChat.logout();
+
+ // You should have imported the following in the file:
+ // import messaging from '@react-native-firebase/messaging';
+ // This is a good practice.
+ await messaging().deleteToken();
+};
+```
+
+
+
+
+
+## Advanced
+
+### Handle Custom Messages
+
+To receive notification of `CustomMessage`, you need to set metadata while sending the `CustomMessage`.
+
+
+
+```js
+var receiverID = 'UID';
+var customData = {
+ latitude: '50.6192171633316',
+ longitude: '-72.68182268750002',
+};
+
+var customType = 'location';
+var receiverType = CometChat.RECEIVER_TYPE.USER;
+var metadata = {
+ pushNotification: 'Your Notification Message',
+};
+
+var customMessage = new CometChat.CustomMessage(
+ receiverID,
+ receiverType,
+ customType,
+ customData
+);
+
+customMessage.setMetadata(metadata);
+
+CometChat.sendCustomMessage(customMessage).then(
+ (message) => {
+ // Message sent successfully.
+ console.log('custom message sent successfully', message);
+ },
+ (error) => {
+ console.log('custom message sending failed with error', error);
+ // Handle exception.
+ }
+);
+```
+
+
+
+
+
+### Converting push notification payload to message object
+
+CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` to convert the message JSON to the corresponding object of TextMessage, MediaMessage,CustomMessage, Action or Call.
+
+
+
+```js
+var processedMessage = CometChat.CometChatHelper.processMessage(JSON_MESSAGE);
+```
+
+
+
+
+
+
+
+Type of Attachment can be of the following the type\
+1.`CometChatConstants.MESSAGE_TYPE_IMAGE`\
+2.`CometChatConstants.MESSAGE_TYPE_VIDEO`\
+3.`CometChatConstants.MESSAGE_TYPE_AUDIO`\
+4.`CometChatConstants.MESSAGE_TYPE_FILE`
+
+
+
+Push Notification: Payload Sample for Text Message and Attachment/Media Message
+
+
+
+```json
+{
+ "alert": "Nancy Grace: Text Message",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "text": "Text Message"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "142",
+ "sentAt": 1555668711,
+ "category": "message",
+ "type": "text"
+ }
+}
+```
+
+
+
+
+```json
+{
+ "alert": "Nancy Grace: has sent an image",
+ "sound": "default",
+ "title": "CometChat",
+ "message": {
+ "receiver": "cometchat-uid-4",
+ "data": {
+ "attachments": [
+ {
+ "extension": "png",
+ "size": 14327,
+ "name": "extension_leftpanel.png",
+ "mimeType": "image/png",
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ }
+ ],
+ "entities": {
+ "receiver": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-4",
+ "role": "default",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "status": "offline"
+ }
+ },
+ "sender": {
+ "entityType": "user",
+ "entity": {
+ "uid": "cometchat-uid-3",
+ "role": "default",
+ "name": "Nancy Grace",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
+ "status": "offline"
+ }
+ }
+ },
+ "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
+ },
+ "sender": "cometchat-uid-3",
+ "receiverType": "user",
+ "id": "145",
+ "sentAt": 1555671238,
+ "category": "message",
+ "type": "image"
+ }
+}
+```
+
+
+
+
+
+### Integrating ConnectionService and CallKit Using CometChat Push Notification
+
+
+
+
+
+
+
+
+
+
+
+* Currently we can only handle default calling notification
+* Whenever the user answers the call we use RNCallKeep.backToForeground(); method to bring the app in to foreground but in some devices you might need to add few more permissions for this to work For example, In MIUI 11 you need to permission for Display pop-up windows while running in the background
+* When the iOS app is in lock state we are not able to open the app so the call start on callkeep it self and you can hear the audio but if you want a video call then the user has to unlock the phone click on the app icon on call screen.
+* If you want to use the callkit and connection service in foreground then you might consider turning the callNotifications settings in UI kit settings. For more information in UI kit settings check the [documentation](/ui-kit/react-native/getting-started#initialise-cometchatuikit).
+
+
+
+#### Setup push notification
+
+* Android
+
+Kindly follow the instruction for setting Firebase Cloud Messaging explained [here](/notifications/react-native-push-notifications)
+
+* iOS
+
+For iOS we use Apple Push Notification service or APNs to send push notification and VOIP notification. To configure this we need to follow some additional steps
+
+#### Step 1: Create a Certificate Signing Request
+
+To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
+
+1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
+
+
+
+
+
+2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button. \ \ \
+3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
+
+#### Step 2: Create an SSL certificate
+
+1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
+2. Go to Certificates, Identifiers & Profiles.
+
+
+
+
+
+3. Create new Certificate by clicking on the + icon.
+
+
+
+
+
+4. Under Services, select - Apple Push Notification services SSL (Sandbox & Production)
+
+
+
+
+
+5. Select your App ID from the dropdown.
+
+
+
+
+
+6. Upload CSR file., upload the CSR file you created through the **Choose File** button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
+
+
+
+
+
+
+
+
+
+#### Step 3: Export and update .p8 certificate
+
+1. To generate a .p8 key file, go to [Apple Developer Account](https://developer.apple.com/account/), then select Certificates, IDs & Profiles.
+2. Select Keys and click on the "+" button to add a new key.
+3. In the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click "Continue" and click "Register".
+4. Then proceed to download the key file by clicking Download.
+5. Make note of the `Key ID`, `Team ID` and your `Bundle ID` for saving in the Extension's settings.
+
+**If you wish to use the .p12 certificate instead, do the following:**
+
+1. Type a name for the .p12 file and save it to your Mac.
+2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
+3. DO NOT provide an export password when prompted.
+4. The .p12 file will be required in the next step for uploading in the CometChat Dashboard.
+
+#### Extension settings
+
+#### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications extension.
+3. Open the settings for this extension and save the following.
+
+
+
+
+
+#### Step 2: Save your settings
+
+On the Settings page you need to enter the following:
+
+
+
+
+
+1. **Set extension version**
+
+ The extension version has to be set to 'V2' or 'V1 & V2' in order to use APNs as the provider.
+
+2. **Select Platforms**
+
+ You can select the platforms on which you wish to receive Push Notifications.
+
+3. **Firebase Cloud Messaging Settings**
+
+ This includes the FCM Server key that you can fetch from the Firebase Dashboard.
+
+4. **APNs Settings**
+
+ You can turn off the Production mode when you create a development build of your application. Upload the .p12 certificate exported in the previous step.
+
+5. **Push Notifications Title**
+
+ This is usually the name of your app.
+
+6. **Notification Triggers**
+
+ Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+ These are pretty self-explanatory and you can toggle them as per your requirement.
+
+#### Installation
+
+We need to add two packages for this
+
+* React-native-CallKeep
+
+This package also require some additional installation steps. Follow [this](https://github.com/react-native-webrtc/react-native-callkeep) link to install react-native-callkeep
+
+
+
+```sh
+npm install react-native-callkeep
+//or
+yarn add react-native-callkeep
+```
+
+
+
+
+
+* React Native VoIP Push Notification
+
+This package also require some additional installation steps. Follow [this](https://github.com/react-native-webrtc/react-native-voip-push-notification#readme) link to install react-native-voip-push-notification.
+
+
+
+```sh
+npm install react-native-voip-push-notification
+# --- if using pod
+cd ios/ && pod install
+```
+
+
+
+
+
+#### App Setup
+
+First you need to Setup CallKeep at the start of the app in Index.js
+
+
+
+```js
+const options = {
+ ios: {
+ appName: 'My app name',
+ },
+ android: {
+ alertTitle: 'Permissions required',
+ alertDescription: 'This application needs to access your phone accounts',
+ cancelButton: 'Cancel',
+ okButton: 'ok',
+ imageName: 'phone_account_icon',
+
+ foregroundService: {
+ channelId: 'com.company.my',
+ channelName: 'Foreground service for my app',
+ notificationTitle: 'My app is running on background',
+ notificationIcon: 'Path to the resource icon of the notification',
+ },
+ },
+};
+RNCallKeep.setup(options);
+RNCallKeep.setAvailable(true);
+let callKeep = new CallKeepHelper();
+```
+
+
+
+
+
+In order to handle connectionService and CallKit we have made a helper call.
+
+
+
+```js
+import { CometChat } from '@cometchat/chat-sdk-react-native';
+import { Platform } from 'react-native';
+import uuid from 'react-native-uuid';
+import RNCallKeep, { AnswerCallPayload } from 'react-native-callkeep';
+import { navigate } from '../StackNavigator';
+import messaging from '@react-native-firebase/messaging';
+import VoipPushNotification from 'react-native-voip-push-notification';
+import invokeApp from 'react-native-invoke-app';
+import KeepAwake from 'react-native-keep-awake';
+import { AppState } from 'react-native';
+import _BackgroundTimer from 'react-native-background-timer';
+export default class CallKeepHelper {
+ constructor(msg) {
+ if (msg) {
+ CallKeepHelper.msg = msg;
+ }
+ this.setupEventListeners();
+ this.registerToken();
+ this.checkLoggedInUser();
+ this.addLoginListener();
+ CallKeepHelper.callEndedBySelf = false;
+ }
+ static FCMToken = null;
+ static voipToken = null;
+ static msg = null;
+ static callEndedBySelf = null;
+ static callerId = '';
+ static callerId1 = '';
+ static isLoggedIn = false;
+ checkLoggedInUser = async () => {
+ try {
+ let user = await CometChat.getLoggedinUser();
+ if (user) {
+ if (user) {
+ CallKeepHelper.isLoggedIn = true;
+ }
+ }
+ } catch (error) {
+ console.log('error checkLoggedInUser', error);
+ }
+ };
+
+ addLoginListener = () => {
+ var listenerID = 'UNIQUE_LISTENER_ID';
+ CometChat.addLoginListener(
+ listenerID,
+ new CometChat.LoginListener({
+ loginSuccess: (e) => {
+ CallKeepHelper.isLoggedIn = true;
+ this.registerTokenToCometChat();
+ },
+ })
+ );
+ };
+
+ registerTokenToCometChat = async () => {
+ if (!CallKeepHelper.isLoggedIn) {
+ return false;
+ }
+
+ try {
+ if (Platform.OS == 'android') {
+ if (CallKeepHelper.FCMToken) {
+ let response = await CometChat.registerTokenForPushNotification(
+ CallKeepHelper.FCMToken
+ );
+ }
+ } else {
+ if (CallKeepHelper.FCMToken) {
+ let response = await CometChat.registerTokenForPushNotification(
+ CallKeepHelper.FCMToken,
+ { voip: false }
+ );
+ }
+ if (CallKeepHelper.voipToken) {
+ let response = await CometChat.registerTokenForPushNotification(
+ CallKeepHelper.voipToken,
+ { voip: true }
+ );
+ }
+ }
+ } catch (error) {}
+ };
+
+ registerToken = async () => {
+ try {
+ const authStatus = await messaging().requestPermission();
+ const enabled =
+ authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatus === messaging.AuthorizationStatus.PROVISIONAL;
+ if (enabled) {
+ if (Platform.OS == 'android') {
+ let FCM = await messaging().getToken();
+
+ CallKeepHelper.FCMToken = FCM;
+ this.registerTokenToCometChat();
+ } else {
+ VoipPushNotification.registerVoipToken();
+ let FCM = await messaging().getAPNSToken();
+ CallKeepHelper.FCMToken = FCM;
+ this.registerTokenToCometChat();
+ }
+ }
+ } catch (error) {}
+ };
+
+ endCall = ({ callUUID }) => {
+ if (CallKeepHelper.callerId) RNCallKeep.endCall(CallKeepHelper.callerId);
+ _BackgroundTimer.start();
+ setTimeout(() => {
+ this.rejectCall();
+ }, 3000);
+ };
+
+ rejectCall = async () => {
+ if (
+ !CallKeepHelper.callEndedBySelf &&
+ CallKeepHelper.msg &&
+ CallKeepHelper.msg.call?.category !== 'custom'
+ ) {
+ var sessionID = CallKeepHelper.msg.sessionId;
+ var status = CometChat.CALL_STATUS.REJECTED;
+ let call = await CometChat.rejectCall(sessionID, status);
+ _BackgroundTimer.stop();
+ } else {
+ _BackgroundTimer.stop();
+ }
+ };
+
+ static displayCallAndroid = () => {
+ this.IsRinging = true;
+ CallKeepHelper.callerId = CallKeepHelper.msg.conversationId;
+ RNCallKeep.displayIncomingCall(
+ CallKeepHelper.msg.conversationId,
+ CallKeepHelper.msg.sender.name,
+ CallKeepHelper.msg.sender.name,
+ 'generic'
+ );
+ setTimeout(() => {
+ if (this.IsRinging) {
+ this.IsRinging = false;
+ RNCallKeep.reportEndCallWithUUID(CallKeepHelper.callerId, 6);
+ }
+ }, 15000);
+ };
+
+ // NOTE: YOU MIGHT HAVE TO MAKE SOME CHANGES OVER HERE AS YOU AS YOUR IMPLEMENTATION OF REACT-NATIVE-UI-KIT MIGHT BE DIFFERENT. YOU JUST NEED TO CALL THE ACCEPT CALL METHOD AND NAVIGATE TO CALL SCREEN.
+ answerCall = ({ callUUID }) => {
+ this.IsRinging = false;
+ CallKeepHelper.callEndedBySelf = true;
+ setTimeout(
+ () =>
+ navigate({
+ index: 0,
+ routes: [
+ { name: 'Conversation', params: { call: CallKeepHelper.msg } },
+ ],
+ }),
+ 2000
+ );
+ // RNCallKeep.endAllCalls();
+ RNCallKeep.backToForeground();
+ if (Platform.OS == 'ios') {
+ if (AppState.currentState == 'active') {
+ RNCallKeep.endAllCalls();
+ _BackgroundTimer.stop();
+ } else {
+ this.addAppStateListener();
+ }
+ } else {
+ RNCallKeep.endAllCalls();
+ _BackgroundTimer.stop();
+ }
+ };
+
+ addAppStateListener = () => {
+ AppState.addEventListener('change', (newState) => {
+ if (newState == 'active') {
+ RNCallKeep.endAllCalls();
+ _BackgroundTimer.stop();
+ }
+ });
+ };
+
+ didDisplayIncomingCall = (DidDisplayIncomingCallArgs) => {
+ if (DidDisplayIncomingCallArgs.callUUID) {
+ if (Platform.OS == 'ios') {
+ CallKeepHelper.callerId = DidDisplayIncomingCallArgs.callUUID;
+ }
+ }
+ if (DidDisplayIncomingCallArgs.error) {
+ console.log({
+ message: `Callkeep didDisplayIncomingCall error: ${DidDisplayIncomingCallArgs.error}`,
+ });
+ }
+
+ this.IsRinging = true;
+
+ setTimeout(() => {
+ if (this.IsRinging) {
+ this.IsRinging = false;
+ // 6 = MissedCall
+ // https://github.com/react-native-webrtc/react-native-callkeep#constants
+ RNCallKeep.reportEndCallWithUUID(
+ DidDisplayIncomingCallArgs.callUUID,
+ 6
+ );
+ }
+ }, 15000);
+ };
+
+ setupEventListeners() {
+ if (Platform.OS == 'ios') {
+ CometChat.addCallListener(
+ 'this.callListenerId',
+ new CometChat.CallListener({
+ onIncomingCallCancelled: (call) => {
+ RNCallKeep.endAllCalls();
+ },
+ })
+ );
+
+ RNCallKeep.addEventListener('didLoadWithEvents', (event) => {
+ for (let i = 0; i < event.length; i++) {
+ if (event[i]?.name == 'RNCallKeepDidDisplayIncomingCall') {
+ CallKeepHelper.callerId = event[i]?.data?.callUUID;
+ }
+ }
+ });
+
+ VoipPushNotification.addEventListener('register', async (token) => {
+ CallKeepHelper.voipToken = token;
+ this.registerTokenToCometChat();
+ });
+ VoipPushNotification.addEventListener('notification', (notification) => {
+ let msg = CometChat.CometChatHelper.processMessage(
+ notification.message
+ );
+
+ CallKeepHelper.msg = msg;
+ });
+
+ VoipPushNotification.addEventListener(
+ 'didLoadWithEvents',
+ async (events) => {
+ if (!events || !Array.isArray(events) || events.length < 1) {
+ return;
+ }
+ for (let voipPushEvent of events) {
+ let { name, data } = voipPushEvent;
+ if (
+ name ===
+ VoipPushNotification.RNVoipPushRemoteNotificationsRegisteredEvent
+ ) {
+ CallKeepHelper.voipToken = data;
+ } else if (
+ name ===
+ VoipPushNotification.RNVoipPushRemoteNotificationReceivedEvent
+ ) {
+ let msg = CometChat.CometChatHelper.processMessage(data.message);
+
+ CallKeepHelper.msg = msg;
+ }
+ }
+ }
+ );
+ }
+
+ RNCallKeep.addEventListener('endCall', this.endCall);
+
+ RNCallKeep.addEventListener('answerCall', this.answerCall);
+ }
+
+ removeEventListeners() {
+ RNCallKeep.removeEventListener('endCall');
+ RNCallKeep.removeEventListener('didDisplayIncomingCall');
+ RNCallKeep.removeEventListener('didLoadWithEvents');
+ VoipPushNotification.removeEventListener('didLoadWithEvents');
+ VoipPushNotification.removeEventListener('register');
+ VoipPushNotification.removeEventListener('notification');
+ }
+}
+```
+
+
+
+
+
+#### Android
+
+In android we are going to use Firebase push notification to display Call notification So basically when ever we receive a push notification for call we display call notification.
+
+we need to add a listener to listen to notification when the app is background or foreground state.
+
+
+
+```js
+messaging().setBackgroundMessageHandler(async (remoteMessage) => {
+ RNCallKeep.setup(options);
+ RNCallKeep.setAvailable(true);
+
+ try {
+ //Converting the message payload into CometChat Message.
+ let msg = CometChat.CometChatHelper.processMessage(
+ JSON.parse(remoteMessage.data.message)
+ );
+ if (msg.category == 'call') {
+ //need to check if the notification we received for Call initiated or ended
+ if (msg.action == 'initiated') {
+ CallKeepHelper.msg = msg; //setting the msg object in call keep helper class
+ CallKeepHelper.displayCallAndroid(); //this method is used to display incoming calls in android t
+ } else {
+ //if sender cancels the call before receiver accept or reject call then we also need to stop our notification
+ RNCallKeep.endCall(msg.conversationId);
+ }
+ }
+ } catch (e) {
+ console.log(e);
+ }
+});
+```
+
+
+
+
+
+#### iOS
+
+In iOS we use APNs push and voip push notification to display push notification and display call CallKit for calls. The notification are handled in Native iOS You need to add the code in AppDelegate.m file to display CallKit
+
+
+
+```objc
+//add this import at the top or the file
+#import "RNCallKeep.h"
+#import "RNFBMessagingModule.h"
+#import
+#import "RNVoipPushNotificationManager.h"
+
+
+
+
+_* <------ add this function *_
+- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
+ // Register VoIP push token (a property of PKPushCredentials) with server
+
+ [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
+}
+
+- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
+{
+ // --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to re-register the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
+}
+
+// --- Handle incoming pushes
+- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
+
+ // --- NOTE: apple forced us to invoke callkit ASAP when we receive voip push
+ // --- see: react-native-callkeep
+
+ // --- Retrieve information from your voip push payload
+ NSDictionary *content = [payload.dictionaryPayload valueForKey:@"aps"];
+ NSDictionary *sender = [content valueForKey:@"alert"];
+ NSString *uuid =[[[NSUUID UUID] UUIDString] lowercaseString];
+ NSString *callerName=[sender valueForKey:@"title"];
+ NSString *handle = [sender valueForKey:@"title"];
+
+ // --- Process the received push
+ [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
+
+ [RNCallKeep reportNewIncomingCall: uuid
+ handle: handle
+ handleType: @"generic"
+ hasVideo: NO
+ localizedCallerName: callerName
+ supportsHolding: YES
+ supportsDTMF: YES
+ supportsGrouping: YES
+ supportsUngrouping: YES
+ fromPushKit: YES
+ payload: nil
+ withCompletionHandler: completion];
+
+}
+```
+
+
+
+
diff --git a/notifications/react-native-push-notifications.mdx b/notifications/react-native-push-notifications.mdx
index 70b501a7..4bfcf8dc 100644
--- a/notifications/react-native-push-notifications.mdx
+++ b/notifications/react-native-push-notifications.mdx
@@ -1,1017 +1,392 @@
---
-title: "React Native"
+title: "React Native Push Notifications"
+description: "Bring the SampleAppWithPushNotifications experience—FCM/APNs into any React Native project using CometChat UI Kit."
---
-Learn how to set up Push notifications for React Native using Firebase Cloud Messaging or FCM.
-
- React Native Push notifications sample app
-
- View on Github
+ Reference implementation of React Native UI Kit, FCM/APNs and Push Notification Setup.
-## Firebase Project Setup
-
-Visit [Firebase](https://console.firebase.google.com/) and login/signup using your Gmail ID.
-
-### Step 1: Create a new Firebase Project
+## What this guide covers
-Head over to the [Firebase Console](https://console.firebase.google.com/) to create a new project.
-
-
-
-
+- CometChat Dashboard setup (enable push, add FCM + APNs providers).
+- Platform credentials (Firebase for Android/iOS, Apple entitlements).
+- Copying the sample notification stack and aligning IDs/provider IDs.
+- Native glue for Android (manifest permissions) and iOS (PushKit).
+- Token registration, navigation from pushes, testing, and troubleshooting.
-This is a simple 3 step process where:
+## What you need first
-1. You give a name to your project
-2. Add Google Analytics to your project (Optional)
-3. Configure Google Analytics account (Optional)
+- CometChat app credentials (App ID, Region, Auth Key) and Push Notifications enabled with an **FCM provider (React Native Android)** and **APNs provider** for iOS.
+- Firebase project with an Android app (`google-services.json` in `android/app`) and Cloud Messaging enabled; optional iOS app if you use FCM on iOS.
+- Apple push setup: APNs `.p8` key/cert in CometChat, iOS project with Push Notifications + Background Modes (Remote notifications) permissions.
+- React Native 0.81+, Node 18+, physical Android + iOS devices for reliable push/call testing.
-Click on Create and you are ready to go.
+## How FCM/APNs + CometChat work together
-### Step 2: Add Firebase to your App
+- **FCM (Android) and APNs (iOS) are the transports:** Firebase issues the Android FCM token; Apple issues the APNs token. They deliver the payloads to devices.
+- **CometChat providers hold your credentials:** The FCM provider you add (for React Native Android) stores your Firebase service account; the APNs provider stores your `.p8` key/cert.
+- **Registration flow:** Request permission → platform returns token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `FCM_REACT_NATIVE_ANDROID` on Android and `APNS_REACT_NATIVE_DEVICE` on iOS → CometChat sends pushes to FCM/APNs on your behalf → the app handles taps/foreground events via Notifee/PushNotificationIOS.
-React native setup will require 2 files for Android and iOS:
+## 1. Enable push and add providers (CometChat Dashboard)
-1. For Android, you need to download the google-services.json file from the Firebase console.
-2. For iOS, you need to download the GoogleService-Info.plist file from the Firebase console.
-
-### Step 3: Download the service account file
+1. Go to **Notifications → Settings** and enable **Push Notifications**.
-
+
-## Extension settings
-
-### Step 1: Enable the extension
-
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open up the settings and save the following settings.
+2. Add an **FCM** provider for React Native Android; upload the Firebase service account JSON and copy the Provider ID.
-
+
-### Step 2: Save your settings
-
-On the Settings page you need to enter the following:
-
-1. **Set extension version**
-
-* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
-* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
-* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
-
-2. **Select the platforms that you want to support**
-
-* Select from Web, Android, Ionic, React Native, Flutter & iOS.
-
-3. **Notification payload settings**
-
-* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
-
-4. **Push payload message options**
+3. Add an **APNs** provider for iOS and copy the Provider ID.
-
+
-The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+## 2. Prepare platform credentials
-* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+### 2.1 Firebase Console
-5. **Notification Triggers**
+1. Register your Android package name (same as `applicationId` in `android/app/build.gradle`) and download `google-services.json` into `android/app`.
+2. Enable Cloud Messaging.
-
+
-* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
-
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
+### 2.2 Apple Developer portal
-* These are pretty self-explanatory and you can toggle them as per your requirement.
+1. Generate an APNs Auth Key (`.p8`) and note the **Key ID** and **Team ID**.
+2. Enable Push Notifications plus Background Modes → *Remote notifications* on the bundle ID.
-## App Setup
-
-### Step 1: Initial plugin setup
+
+
+
-1. For React Native, there are numerous plugins available via NPM which can be used to set up push notifications for your apps. [react-native-firebase](https://www.npmjs.com/package/react-native-firebase) and [react-native-notifications](https://www.npmjs.com/package/react-native-notifications) are just the two out of many available.
-2. To setup Push Notification, you need to follow the steps mentioned in the Plugin's Documentation.
+## 3. Local configuration
-At this point, you will have:
+- Update `src/utils/AppConstants.tsx` with `appId`, `authKey`, `region`, `fcmProviderId`, and `apnProviderId`.
+- Keep `app.json` name consistent with your bundle ID / applicationId.
-1. Two separate apps created on the Firebase console. (For Android and iOS).
-2. Plugin setup completed as per the respective documentation and our reference.
+```ts lines
+const APP_ID = "";
+const AUTH_KEY = "";
+const REGION = "";
+const DEMO_UID = "cometchat-uid-1";
+```
-### Step 2: Register FCM Token
+### 3.1 Dependencies snapshot (from Sample App)
-1. This step assumes that you already have a React Native app setup with CometChat installed. Make sure that the CometChat object is initialized and user has been logged in.
-2. On the success callback of user login, you can fetch the FCM Token and register it with the extension as shown below:
+Install these dependencies in your React Native app:
-
-
-```js
-// Pseudo-code with async-await syntax
+```npm lines
+npm install \
+ @react-native-firebase/app@23.4.0 \
+ @react-native-firebase/messaging@23.4.0 \
+ @notifee/react-native@9.1.8 \
+ @react-native-community/push-notification-ios@1.11.0 \
+ react-native-voip-push-notification@3.3.3 \
+ github:cometchat/react-native-callkeep
+```
-const APP_ID = 'APP_ID';
-const REGION = 'REGION';
-const AUTH_KEY = 'AUTH_KEY';
+Match these or newer compatible versions in your app.
-const UID = 'UID';
-const APP_SETTINGS = new CometChat.AppSettingsBuilder()
- .subscribePresenceForAllUsers()
- .setRegion(REGION)
- .build();
+## 4. Android setup
-try {
- // First initialize the app
- await CometChat.init(APP_ID, APP_SETTINGS);
+### 4.1 Configure Firebase with Android credentials
- // Login the user
- await CometChat.login(UID, AUTH_KEY);
+To allow Firebase on Android to use the credentials, the `google-services` plugin must be enabled on the project. This requires modification to two files in the Android directory.
- // Get the FCM device token
- // You should have imported the following in the file:
- // import messaging from '@react-native-firebase/messaging';
- const FCM_TOKEN = await messaging().getToken();
+First, add the google-services plugin as a dependency inside of your `/android/build.gradle` file:
- // Register the token with Push Notifications (Legacy)
- await CometChat.registerTokenForPushNotification(FCM_TOKEN);
-} catch (error) {
- // Handle errors gracefully
+```android lines
+buildscript {
+ dependencies {
+ // ... other dependencies
+ classpath("com.google.gms:google-services:4.4.4")
+ }
}
```
-
+Lastly, execute the plugin by adding the following to your `/android/app/build.gradle` file:
-
-
-3. Registration also needs to happen in case of token refresh as shown below:
-
-
-
-```js
-// Pseudo-code
-
-// You should have imported the following in the file:
-// import messaging from '@react-native-firebase/messaging';
-try {
- // Listen to whether the token changes
- return messaging().onTokenRefresh(FCM_TOKEN => {
- await CometChat.registerTokenForPushNotification(FCM_TOKEN);
- });
- // ...
-} catch(error) {
- // Handle errors gracefully
-}
+```android lines
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
```
-
-
-
-
-For React Native Firebase reference, visit the link below:
-
-### Step 3: Receive Notifications
-
-
-
-```js
-// Pseudo-code
-import messaging from '@react-native-firebase/messaging';
-import { Alert } from 'react-native';
-
-// Implementation can be done in a life-cycle method or hook
-const unsubscribe = messaging().onMessage(async (remoteMessage) => {
- Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
-});
+### 4.2 Configure required permissions in `AndroidManifest.xml` as shown.
+
+```xml lines
+
+
+
+
+
+
+
+
+
+
+
+
+
```
-
+and ask for runtime permissions where needed (e.g. `POST_NOTIFICATIONS` on Android 13+).
-
+```tsx lines
+import { PermissionsAndroid, Platform } from "react-native";
-
+ const requestAndroidPermissions = async () => {
+ if (Platform.OS !== 'android') return;
-We send Data Notifications and you need to handle displaying notifications at your end. For eg: Using Notifee
+ try {
+ // Ask for push‑notification permission
+ const authStatus = await messaging().requestPermission();
+ const enabled =
+ authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
+ authStatus === messaging.AuthorizationStatus.PROVISIONAL;
+
+ if (!enabled) {
+ console.warn('Notification permission denied (FCM).');
+ }
+ } catch (error) {
+ console.warn('FCM permission request error:', error);
+ }
-
+ try {
+ await PermissionsAndroid.requestMultiple([
+ PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
+ PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
+ PermissionsAndroid.PERMISSIONS.CAMERA,
+ PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
+ ]);
+ } catch (err) {
+ console.warn('Android permissions error:', err);
+ }
+}
+```
-### Step 4: Stop receiving Notifications
+### 4.3 Register FCM token with CometChat
-1. Simply logout the CometChat user and you will stop receiving notifications.
-2. As a good practice, you can also delete the FCM Token by calling `deleteToken` on the messaging object.
+Inside your main app file where you initialize CometChat, add the below code snippet after the user has logged in successfully.
+Initilize and register the FCM token for Android as shown:
-
-
-```js
-// Pseudo-code using async-await syntax
+```ts lines
+requestAndroidPermissions();
-logout = async () => {
- // User logs out of the app
- await CometChat.logout();
+const FCM_TOKEN = await messaging().getToken();
+console.log("FCM Token:", FCM_TOKEN);
- // You should have imported the following in the file:
- // import messaging from '@react-native-firebase/messaging';
- // This is a good practice.
- await messaging().deleteToken();
-};
+// For React Native Android
+CometChatNotifications.registerPushToken(
+ FCM_TOKEN,
+ CometChatNotifications.PushPlatforms.FCM_REACT_NATIVE_ANDROID,
+ "YOUR_FCM_PROVIDER_ID" // from CometChat Dashboard
+ )
+ .then(() => {
+ console.log("Token registration successful");
+ })
+ .catch((err) => {
+ console.log("Token registration failed:", err);
+ });
```
-
+### 4.4 Unregister FCM token on logout
-
+Typically, push token unregistration should occur prior to user logout, using the `CometChat.logout()` method.
+For token unregistration, use the `CometChatNotifications.unregisterPushToken()` method provided by the SDKs.
-## Advanced
+## 5. iOS setup
-### Handle Custom Messages
+### 5.1 Project Setup
-To receive notification of `CustomMessage`, you need to set metadata while sending the `CustomMessage`.
+Enable **Push Notifications** and **Background Modes** (Remote notifications) in Xcode.
-
-
-```js
-var receiverID = 'UID';
-var customData = {
- latitude: '50.6192171633316',
- longitude: '-72.68182268750002',
-};
-
-var customType = 'location';
-var receiverType = CometChat.RECEIVER_TYPE.USER;
-var metadata = {
- pushNotification: 'Your Notification Message',
-};
-
-var customMessage = new CometChat.CustomMessage(
- receiverID,
- receiverType,
- customType,
- customData
-);
+
+
+
-customMessage.setMetadata(metadata);
+### 5.2 Install dependencies:
-CometChat.sendCustomMessage(customMessage).then(
- (message) => {
- // Message sent successfully.
- console.log('custom message sent successfully', message);
- },
- (error) => {
- console.log('custom message sending failed with error', error);
- // Handle exception.
- }
-);
+Install the required packages for iOS push notifications:
+```bash lines
+npm install react-native-push-notification@^8.1.1
+npm install @react-native-community/push-notification-ios@^1.12.0
```
-
-
-
+### 5.3 AppDelegate.swift modifications:
-### Converting push notification payload to message object
-
-CometChat SDK provides a method `CometChat.CometChatHelper.processMessage()` to convert the message JSON to the corresponding object of TextMessage, MediaMessage,CustomMessage, Action or Call.
-
-
-
-```js
-var processedMessage = CometChat.CometChatHelper.processMessage(JSON_MESSAGE);
+Add imports at the top:
+```swift lines
+import UserNotifications
+import RNCPushNotificationIOS
```
-
-
-
-
-
-
-Type of Attachment can be of the following the type\
-1.`CometChatConstants.MESSAGE_TYPE_IMAGE`\
-2.`CometChatConstants.MESSAGE_TYPE_VIDEO`\
-3.`CometChatConstants.MESSAGE_TYPE_AUDIO`\
-4.`CometChatConstants.MESSAGE_TYPE_FILE`
-
-
-
-Push Notification: Payload Sample for Text Message and Attachment/Media Message
-
-
-
-```json
-{
- "alert": "Nancy Grace: Text Message",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
- }
- },
- "text": "Text Message"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "142",
- "sentAt": 1555668711,
- "category": "message",
- "type": "text"
- }
-}
+Add `UNUserNotificationCenterDelegate` to the `AppDelegate` class declaration:
+```swift
+class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate
```
-
-
-
-```json
-{
- "alert": "Nancy Grace: has sent an image",
- "sound": "default",
- "title": "CometChat",
- "message": {
- "receiver": "cometchat-uid-4",
- "data": {
- "attachments": [
- {
- "extension": "png",
- "size": 14327,
- "name": "extension_leftpanel.png",
- "mimeType": "image/png",
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- }
- ],
- "entities": {
- "receiver": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-4",
- "role": "default",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "status": "offline"
- }
- },
- "sender": {
- "entityType": "user",
- "entity": {
- "uid": "cometchat-uid-3",
- "role": "default",
- "name": "Nancy Grace",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp",
- "status": "offline"
- }
+Add the following inside the `didFinishLaunchingWithOptions` method:
+```swift lines
+UNUserNotificationCenter.current().delegate = self
+
+UNUserNotificationCenter.current().requestAuthorization(
+ options: [.alert, .badge, .sound]
+) {
+ granted,
+ error in
+ if granted {
+ DispatchQueue.main.async {
+ application.registerForRemoteNotifications()
}
- },
- "url": "https://s3-eu-west-1.amazonaws.com/data.cometchat.com/1255466c41bd7f/media/1555671238_956450103_extension_leftpanel.png"
- },
- "sender": "cometchat-uid-3",
- "receiverType": "user",
- "id": "145",
- "sentAt": 1555671238,
- "category": "message",
- "type": "image"
- }
+ } else {
+ print("Push Notification permission not granted: \(String(describing: error))")
+ }
}
```
-
-
-
-
-### Integrating ConnectionService and CallKit Using CometChat Push Notification
-
-
-
-
-
-
-
-
-
-
-
-* Currently we can only handle default calling notification
-* Whenever the user answers the call we use RNCallKeep.backToForeground(); method to bring the app in to foreground but in some devices you might need to add few more permissions for this to work For example, In MIUI 11 you need to permission for Display pop-up windows while running in the background
-* When the iOS app is in lock state we are not able to open the app so the call start on callkeep it self and you can hear the audio but if you want a video call then the user has to unlock the phone click on the app icon on call screen.
-* If you want to use the callkit and connection service in foreground then you might consider turning the callNotifications settings in UI kit settings. For more information in UI kit settings check the [documentation](/ui-kit/react-native/getting-started#initialise-cometchatuikit).
-
-
-
-#### Setup push notification
-
-* Android
-
-Kindly follow the instruction for setting Firebase Cloud Messaging explained [here](/notifications/react-native-push-notifications)
-
-* iOS
-
-For iOS we use Apple Push Notification service or APNs to send push notification and VOIP notification. To configure this we need to follow some additional steps
-
-#### Step 1: Create a Certificate Signing Request
-
-To obtain a signing certificate required to sign apps for installation on iOS devices, you should first create a certificate signing request (CSR) file through Keychain Access on your Mac.
-
-1. Open the Keychain Access from the utility folder, go to Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority, and then click.
-
-
-
-
-
-2. The Certificate Information dialog box appears. Enter the email address that you use in your Apple Developer account, and enter a common name for your private key. Don't enter CA email address, choose Saved to disk, and then click the Continue button. \ \ \
-3. Specify the name of your CSR to save and choose the location to save the file on your local disk. Then your CSR file is created, which contains a public/private key pair.
-
-#### Step 2: Create an SSL certificate
-
-1. Sign in to your account at the [Apple Developer Member Center](https://developer.apple.com/membercenter).
-2. Go to Certificates, Identifiers & Profiles.
-
-
-
-
-
-3. Create new Certificate by clicking on the + icon.
-
-
-
-
-
-4. Under Services, select - Apple Push Notification services SSL (Sandbox & Production)
-
-
-
-
-
-5. Select your App ID from the dropdown.
-
-
-
-
-
-6. Upload CSR file., upload the CSR file you created through the **Choose File** button. To complete the process, choose Continue. When the certificate is ready, choose Download to save it to your Mac.
-
-
-
-
-
-
-
-
-
-#### Step 3: Export and update .p8 certificate
-
-1. To generate a .p8 key file, go to [Apple developer account page](https://developer.apple.com/account/), then select Certificates, IDs & Profiles.
-2. Select Keys and click on the "+" button to add a new key.
-3. In the new key page, type in your key name and check the Apple Push Notification service (APNs) box, then click "Continue" and click "Register".
-4. Then proceed to download the key file by clicking Download.
-5. Make note of the `Key ID`, `Team ID` and your `Bundle ID` for saving in the Extension's settings.
-
-**If you wish to use the .p12 certificate instead, do the following:**
-
-1. Type a name for the .p12 file and save it to your Mac.
-2. Browse to the location where you saved your key, select it, and click Open. Add the key ID for the key (available in Certificates, Identifiers & Profiles in the Apple Developer Member Center) and export it.
-3. DO NOT provide an export password when prompted.
-4. The .p12 file will be required in the next step for uploading in the CometChat Dashboard.
-
-#### Extension settings
-
-#### Step 1: Enable the extension
-
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open the settings for this extension and save the following.
-
-
-
-
-
-#### Step 2: Save your settings
-
-On the Settings page you need to enter the following:
-
-
-
-
-
-1. **Set extension version**
-
- The extension version has to be set to 'V2' or 'V1 & V2' in order to use APNs as the provider.
-
-2. **Select Platforms**
-
- You can select the platforms on which you wish to receive Push Notifications.
-
-3. **Firebase Cloud Messaging Settings**
-
- This includes the FCM Server key that you can fetch from the Firebase Dashboard.
-
-4. **APNs Settings**
-
- You can turn off the Production mode when you create a development build of your application. Upload the .p12 certificate exported in the previous step.
-
-5. **Push Notifications Title**
-
- This is usually the name of your app.
+Add the following methods to handle push notification events:
+```swift lines
+func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ print("APNs device token received: \(deviceToken)")
+ RNCPushNotificationIOS.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)
+}
-6. **Notification Triggers**
+func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ print("APNs registration failed: \(error)")
+ RNCPushNotificationIOS.didFailToRegisterForRemoteNotificationsWithError(error)
+}
- Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+ RNCPushNotificationIOS.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)
+}
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
+func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+ completionHandler([.banner, .sound, .badge])
+}
+
+func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
+ RNCPushNotificationIOS.didReceive(response)
+ completionHandler()
+}
+```
- These are pretty self-explanatory and you can toggle them as per your requirement.
+Add the following to `Podfile` to avoid framework linkage issues:
+```ruby
+use_frameworks! :linkage => :static
+```
-#### Installation
+You might have to remove below code if already present in your Podfile:
+```ruby lines
+linkage = ENV['USE_FRAMEWORKS']
+if linkage != nil
+ Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
+ use_frameworks! :linkage => linkage.to_sym
+end
+```
-We need to add two packages for this
+Then lets install pods and open the workspace:
+```bash lines
+cd ios
+pod install
+open YourProjectName.xcworkspace
+```
-* React-native-CallKeep
+### 5.4 App.tsx modifications:
-This package also require some additional installation steps. Follow [this](https://github.com/react-native-webrtc/react-native-callkeep) link to install react-native-callkeep
+Import CometChatNotifications:
-
-
-```sh
-npm install react-native-callkeep
-//or
-yarn add react-native-callkeep
+```tsx
+import { CometChat, CometChatNotifications } from "@cometchat/chat-sdk-react-native";
```
-
+Get device token and store it in a ref:
+Also, define your APNs provider ID from the CometChat Dashboard.
+And request permissions on mount:
-
+```tsx lines
+const APNS_PROVIDER_ID = 'YOUR_APNS_PROVIDER_ID'; // from CometChat Dashboard
+const apnsTokenRef = useRef < string | null > (null);
-* React Native VoIP Push Notification
+useEffect(() => {
+ if (Platform.OS !== 'ios') return;
-This package also require some additional installation steps. Follow [this](https://github.com/react-native-webrtc/react-native-voip-push-notification#readme) link to install react-native-voip-push-notification.
+ const onRegister = (deviceToken: string) => {
+ console.log(' APNs device token captured:', deviceToken);
+ apnsTokenRef.current = deviceToken;
+ };
-
-
-```sh
-npm install react-native-voip-push-notification
-# --- if using pod
-cd ios/ && pod install
-```
+ PushNotificationIOS.addEventListener('register', onRegister);
-
-
-
-
-#### App Setup
-
-First you need to Setup CallKeep at the start of the app in Index.js
-
-
-
-```js
-const options = {
- ios: {
- appName: 'My app name',
- },
- android: {
- alertTitle: 'Permissions required',
- alertDescription: 'This application needs to access your phone accounts',
- cancelButton: 'Cancel',
- okButton: 'ok',
- imageName: 'phone_account_icon',
-
- foregroundService: {
- channelId: 'com.company.my',
- channelName: 'Foreground service for my app',
- notificationTitle: 'My app is running on background',
- notificationIcon: 'Path to the resource icon of the notification',
- },
- },
-};
-RNCallKeep.setup(options);
-RNCallKeep.setAvailable(true);
-let callKeep = new CallKeepHelper();
-```
+ PushNotificationIOS.addEventListener('registrationError', error => {
+ console.error(' APNs registration error:', error);
+ });
-
-
-
-
-In order to handle connectionService and CallKit we have made a helper call.
-
-
-
-```js
-import { CometChat } from '@cometchat/chat-sdk-react-native';
-import { Platform } from 'react-native';
-import uuid from 'react-native-uuid';
-import RNCallKeep, { AnswerCallPayload } from 'react-native-callkeep';
-import { navigate } from '../StackNavigator';
-import messaging from '@react-native-firebase/messaging';
-import VoipPushNotification from 'react-native-voip-push-notification';
-import invokeApp from 'react-native-invoke-app';
-import KeepAwake from 'react-native-keep-awake';
-import { AppState } from 'react-native';
-import _BackgroundTimer from 'react-native-background-timer';
-export default class CallKeepHelper {
- constructor(msg) {
- if (msg) {
- CallKeepHelper.msg = msg;
- }
- this.setupEventListeners();
- this.registerToken();
- this.checkLoggedInUser();
- this.addLoginListener();
- CallKeepHelper.callEndedBySelf = false;
- }
- static FCMToken = null;
- static voipToken = null;
- static msg = null;
- static callEndedBySelf = null;
- static callerId = '';
- static callerId1 = '';
- static isLoggedIn = false;
- checkLoggedInUser = async () => {
- try {
- let user = await CometChat.getLoggedinUser();
- if (user) {
- if (user) {
- CallKeepHelper.isLoggedIn = true;
- }
- }
- } catch (error) {
- console.log('error checkLoggedInUser', error);
- }
- };
-
- addLoginListener = () => {
- var listenerID = 'UNIQUE_LISTENER_ID';
- CometChat.addLoginListener(
- listenerID,
- new CometChat.LoginListener({
- loginSuccess: (e) => {
- CallKeepHelper.isLoggedIn = true;
- this.registerTokenToCometChat();
- },
- })
+ // Trigger permission + native registration
+ PushNotificationIOS.requestPermissions().then(p =>
+ console.log('Push permissions:', p),
);
- };
-
- registerTokenToCometChat = async () => {
- if (!CallKeepHelper.isLoggedIn) {
- return false;
- }
- try {
- if (Platform.OS == 'android') {
- if (CallKeepHelper.FCMToken) {
- let response = await CometChat.registerTokenForPushNotification(
- CallKeepHelper.FCMToken
- );
- }
- } else {
- if (CallKeepHelper.FCMToken) {
- let response = await CometChat.registerTokenForPushNotification(
- CallKeepHelper.FCMToken,
- { voip: false }
- );
- }
- if (CallKeepHelper.voipToken) {
- let response = await CometChat.registerTokenForPushNotification(
- CallKeepHelper.voipToken,
- { voip: true }
- );
- }
- }
- } catch (error) {}
- };
+ return () => {
+ PushNotificationIOS.removeEventListener('register');
+ PushNotificationIOS.removeEventListener('registrationError');
+ };
+}, []);
+```
- registerToken = async () => {
- try {
- const authStatus = await messaging().requestPermission();
- const enabled =
- authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
- authStatus === messaging.AuthorizationStatus.PROVISIONAL;
- if (enabled) {
- if (Platform.OS == 'android') {
- let FCM = await messaging().getToken();
-
- CallKeepHelper.FCMToken = FCM;
- this.registerTokenToCometChat();
- } else {
- VoipPushNotification.registerVoipToken();
- let FCM = await messaging().getAPNSToken();
- CallKeepHelper.FCMToken = FCM;
- this.registerTokenToCometChat();
- }
- }
- } catch (error) {}
- };
-
- endCall = ({ callUUID }) => {
- if (CallKeepHelper.callerId) RNCallKeep.endCall(CallKeepHelper.callerId);
- _BackgroundTimer.start();
- setTimeout(() => {
- this.rejectCall();
- }, 3000);
- };
-
- rejectCall = async () => {
- if (
- !CallKeepHelper.callEndedBySelf &&
- CallKeepHelper.msg &&
- CallKeepHelper.msg.call?.category !== 'custom'
- ) {
- var sessionID = CallKeepHelper.msg.sessionId;
- var status = CometChat.CALL_STATUS.REJECTED;
- let call = await CometChat.rejectCall(sessionID, status);
- _BackgroundTimer.stop();
- } else {
- _BackgroundTimer.stop();
- }
- };
-
- static displayCallAndroid = () => {
- this.IsRinging = true;
- CallKeepHelper.callerId = CallKeepHelper.msg.conversationId;
- RNCallKeep.displayIncomingCall(
- CallKeepHelper.msg.conversationId,
- CallKeepHelper.msg.sender.name,
- CallKeepHelper.msg.sender.name,
- 'generic'
+After user login, register the APNs token:
+```tsx lines
+// Register token ONLY if we already have it
+if (apnsTokenRef.current) {
+ await CometChatNotifications.registerPushToken(
+ apnsTokenRef.current,
+ CometChatNotifications.PushPlatforms.APNS_REACT_NATIVE_DEVICE,
+ APNS_PROVIDER_ID
);
- setTimeout(() => {
- if (this.IsRinging) {
- this.IsRinging = false;
- RNCallKeep.reportEndCallWithUUID(CallKeepHelper.callerId, 6);
- }
- }, 15000);
- };
-
- // NOTE: YOU MIGHT HAVE TO MAKE SOME CHANGES OVER HERE AS YOU AS YOUR IMPLEMENTATION OF REACT-NATIVE-UI-KIT MIGHT BE DIFFERENT. YOU JUST NEED TO CALL THE ACCEPT CALL METHOD AND NAVIGATE TO CALL SCREEN.
- answerCall = ({ callUUID }) => {
- this.IsRinging = false;
- CallKeepHelper.callEndedBySelf = true;
- setTimeout(
- () =>
- navigate({
- index: 0,
- routes: [
- { name: 'Conversation', params: { call: CallKeepHelper.msg } },
- ],
- }),
- 2000
- );
- // RNCallKeep.endAllCalls();
- RNCallKeep.backToForeground();
- if (Platform.OS == 'ios') {
- if (AppState.currentState == 'active') {
- RNCallKeep.endAllCalls();
- _BackgroundTimer.stop();
- } else {
- this.addAppStateListener();
- }
- } else {
- RNCallKeep.endAllCalls();
- _BackgroundTimer.stop();
- }
- };
-
- addAppStateListener = () => {
- AppState.addEventListener('change', (newState) => {
- if (newState == 'active') {
- RNCallKeep.endAllCalls();
- _BackgroundTimer.stop();
- }
- });
- };
-
- didDisplayIncomingCall = (DidDisplayIncomingCallArgs) => {
- if (DidDisplayIncomingCallArgs.callUUID) {
- if (Platform.OS == 'ios') {
- CallKeepHelper.callerId = DidDisplayIncomingCallArgs.callUUID;
- }
- }
- if (DidDisplayIncomingCallArgs.error) {
- console.log({
- message: `Callkeep didDisplayIncomingCall error: ${DidDisplayIncomingCallArgs.error}`,
- });
- }
-
- this.IsRinging = true;
-
- setTimeout(() => {
- if (this.IsRinging) {
- this.IsRinging = false;
- // 6 = MissedCall
- // https://github.com/react-native-webrtc/react-native-callkeep#constants
- RNCallKeep.reportEndCallWithUUID(
- DidDisplayIncomingCallArgs.callUUID,
- 6
- );
- }
- }, 15000);
- };
-
- setupEventListeners() {
- if (Platform.OS == 'ios') {
- CometChat.addCallListener(
- 'this.callListenerId',
- new CometChat.CallListener({
- onIncomingCallCancelled: (call) => {
- RNCallKeep.endAllCalls();
- },
- })
- );
-
- RNCallKeep.addEventListener('didLoadWithEvents', (event) => {
- for (let i = 0; i < event.length; i++) {
- if (event[i]?.name == 'RNCallKeepDidDisplayIncomingCall') {
- CallKeepHelper.callerId = event[i]?.data?.callUUID;
- }
- }
- });
-
- VoipPushNotification.addEventListener('register', async (token) => {
- CallKeepHelper.voipToken = token;
- this.registerTokenToCometChat();
- });
- VoipPushNotification.addEventListener('notification', (notification) => {
- let msg = CometChat.CometChatHelper.processMessage(
- notification.message
- );
-
- CallKeepHelper.msg = msg;
- });
-
- VoipPushNotification.addEventListener(
- 'didLoadWithEvents',
- async (events) => {
- if (!events || !Array.isArray(events) || events.length < 1) {
- return;
- }
- for (let voipPushEvent of events) {
- let { name, data } = voipPushEvent;
- if (
- name ===
- VoipPushNotification.RNVoipPushRemoteNotificationsRegisteredEvent
- ) {
- CallKeepHelper.voipToken = data;
- } else if (
- name ===
- VoipPushNotification.RNVoipPushRemoteNotificationReceivedEvent
- ) {
- let msg = CometChat.CometChatHelper.processMessage(data.message);
-
- CallKeepHelper.msg = msg;
- }
- }
- }
- );
- }
-
- RNCallKeep.addEventListener('endCall', this.endCall);
-
- RNCallKeep.addEventListener('answerCall', this.answerCall);
- }
-
- removeEventListeners() {
- RNCallKeep.removeEventListener('endCall');
- RNCallKeep.removeEventListener('didDisplayIncomingCall');
- RNCallKeep.removeEventListener('didLoadWithEvents');
- VoipPushNotification.removeEventListener('didLoadWithEvents');
- VoipPushNotification.removeEventListener('register');
- VoipPushNotification.removeEventListener('notification');
- }
+ console.log(' APNs token registered with CometChat');
}
```
-
-
-
-
-#### Android
-
-In android we are going to use Firebase push notification to display Call notification So basically when ever we receive a push notification for call we display call notification.
-
-we need to add a listener to listen to notification when the app is background or foreground state.
-
-
-
-```js
-messaging().setBackgroundMessageHandler(async (remoteMessage) => {
- RNCallKeep.setup(options);
- RNCallKeep.setAvailable(true);
-
- try {
- //Converting the message payload into CometChat Message.
- let msg = CometChat.CometChatHelper.processMessage(
- JSON.parse(remoteMessage.data.message)
- );
- if (msg.category == 'call') {
- //need to check if the notification we received for Call initiated or ended
- if (msg.action == 'initiated') {
- CallKeepHelper.msg = msg; //setting the msg object in call keep helper class
- CallKeepHelper.displayCallAndroid(); //this method is used to display incoming calls in android t
- } else {
- //if sender cancels the call before receiver accept or reject call then we also need to stop our notification
- RNCallKeep.endCall(msg.conversationId);
- }
- }
- } catch (e) {
- console.log(e);
- }
-});
+Prior to logout, unregister the APNs token:
+```tsx
+await CometChatNotifications.unregisterPushToken();
```
-
-
-
-
-#### iOS
-
-In iOS we use APNs push and voip push notification to display push notification and display call CallKit for calls. The notification are handled in Native iOS You need to add the code in AppDelegate.m file to display CallKit
-
-
-
-```objc
-//add this import at the top or the file
-#import "RNCallKeep.h"
-#import "RNFBMessagingModule.h"
-#import
-#import "RNVoipPushNotificationManager.h"
-
+## 6. Handling notification taps and navigation
+To handle notification taps and navigate to the appropriate chat screen, you need to set up handlers for both foreground and background notifications.
+{/* :TODO: Add code snippets and explanation for setting up Notifee handlers and navigation logic. */}
-_* <------ add this function *_
-- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
- // Register VoIP push token (a property of PKPushCredentials) with server
- [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
-}
-
-- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
-{
- // --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to re-register the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
-}
+## 7. Testing checklist
-// --- Handle incoming pushes
-- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
-
- // --- NOTE: apple forced us to invoke callkit ASAP when we receive voip push
- // --- see: react-native-callkeep
-
- // --- Retrieve information from your voip push payload
- NSDictionary *content = [payload.dictionaryPayload valueForKey:@"aps"];
- NSDictionary *sender = [content valueForKey:@"alert"];
- NSString *uuid =[[[NSUUID UUID] UUIDString] lowercaseString];
- NSString *callerName=[sender valueForKey:@"title"];
- NSString *handle = [sender valueForKey:@"title"];
-
- // --- Process the received push
- [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
-
- [RNCallKeep reportNewIncomingCall: uuid
- handle: handle
- handleType: @"generic"
- hasVideo: NO
- localizedCallerName: callerName
- supportsHolding: YES
- supportsDTMF: YES
- supportsGrouping: YES
- supportsUngrouping: YES
- fromPushKit: YES
- payload: nil
- withCompletionHandler: completion];
-
-}
-```
+1. Android: install on device, grant POST_NOTIFICATIONS; log in and verify FCM token registration success.
+2. Send a message from another user:
+ - Foreground: Notifee banner shows unless that chat is open.
+ - Background/terminated: tap opens the correct conversation; Notifee background handler runs.
+3. iOS: verify APNs device token register; tap push from killed app opens the right chat; remote notifications finish handler is called.
+4. Rotate tokens (reinstall or revoke) and confirm `onTokenRefresh` re-registers.
-
+## 8. Troubleshooting
-
+| Symptom | Quick checks |
+| --- | --- |
+| No pushes | Confirm `google-services.json` location, package/bundle IDs match Firebase/Apple, Push extension enabled with correct provider IDs, permissions granted. |
+| Token registration fails | Ensure registration runs **after login**, provider IDs are set, and `registerDeviceForRemoteMessages()` is called (Android). |
+{/* | Notification taps do nothing | Keep Notifee foreground/background handlers and `checkInitialNotificationIOS`; ensure navigation ref is ready before routing. | */}
+{/* | Call UI not showing | Verify CallKeep setup, telecom permissions (Android) or CallKit entitlement (iOS), and that `VoipNotificationHandler.initialize()` runs post-login. | */}
+{/* | Inline reply needed | Extend Notifee action buttons; CometChat expects you to send the message manually after reading `remoteMessage.data`. | */}
diff --git a/notifications/sms-custom-providers.mdx b/notifications/sms-custom-providers.mdx
new file mode 100644
index 00000000..dcd65feb
--- /dev/null
+++ b/notifications/sms-custom-providers.mdx
@@ -0,0 +1,187 @@
+---
+title: "Custom Providers"
+---
+
+Use a custom provider to send CometChat SMS notifications through any gateway via webhook. Twilio is built-in; choose custom when you want another SMS service or your own bridge.
+
+## Prerequisites
+
+1. Public `https://` webhook that accepts `POST` JSON.
+2. Return `200 OK` within ~2 seconds (do heavy work async).
+3. Secure the endpoint (recommended): Basic Auth or a verified signature. With Basic Auth, requests include:
+
+```html
+Authorization: Basic
+```
+
+## Add credentials
+
+1. Click on the "+ Add Credentials" button.
+2. Enable the provider.
+3. Enter the publicly accessible webhook URL.
+4. (Recommended) Enable Basic Authentication and set username/password.
+5. Decide if you want to **Trigger only if phone number is stored with CometChat** (via [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details)); when off, the webhook fires regardless.
+6. Save the credentials.
+
+
+
+
+
+## How delivery works
+
+The Custom provider is triggered once for an event in one-on-one conversation. In case of notifying users in a group, the custom provider is triggered once for each user present in that group.
+
+
+
+```json
+{
+ "trigger": "sms-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-1",
+ "phno": "+919299334134", // Optional
+ "name": "Andrew Joseph"
+ },
+ "messages": [
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "Are we meeting on this weekend?",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ },
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "📷 Has shared an image",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ }
+ ],
+ "senderDetails": {
+ "uid": "cometchat-uid-4",
+ "name": "Susan Marie",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
+ },
+ "smsContent": "You've received new messages from Susan Marie! Read them at https://your-website.com/chat."
+ },
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+```json
+{
+ "trigger": "sms-notification-payload-generated",
+ "data": {
+ "to": {
+ "uid": "cometchat-uid-1",
+ "phno": "+919299334134", // Optional
+ "name": "Andrew Joseph"
+ },
+ "messages": [
+ {
+ "sender": {
+ "uid": "cometchat-uid-5",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-5.webp",
+ "name": "John Paul"
+ },
+ "message": "Hello all! What's up?",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ },
+ {
+ "sender": {
+ "uid": "cometchat-uid-4",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
+ "name": "Susan Marie"
+ },
+ "message": "This is the place I was thinking about",
+ "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
+ }
+ ],
+ "groupDetails": {
+ "guid": "cometchat-guid-1",
+ "name": "Hiking Group",
+ "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
+ },
+ "smsContent": "You've received new messages in Hiking Group! Read them at https://your-website.com."
+ },
+ "appId": "app123",
+ "region": "us/eu/in",
+ "webhook": "custom"
+}
+```
+
+
+
+
+
+#### Sample server-side code
+
+```javascript
+const express = require('express');
+const app = express();
+const PORT = process.env.PORT || 3000;
+
+app.use(express.json());
+
+const basicAuth = (req, res, next) => {
+ const authHeader = req.headers['authorization'];
+ if (!authHeader || !authHeader.startsWith('Basic ')) {
+ return res.status(401).json({ message: 'Unauthorized' });
+ }
+ next();
+};
+
+const triggerSMSNotification = async (to = {}, data = {}) => {
+ let { name, uid, phno } = to;
+ let { groupDetails, senderDetails, smsContent } = data;
+
+ if (!uid) throw new Error('Missing data.to.uid');
+ if (!smsContent) throw new Error('Missing data.smsContent');
+
+ if (groupDetails) console.log('Received webhook for group SMS notification');
+ if (senderDetails) console.log('Received webhook for one-on-one SMS notification');
+
+ if (phno == null) {
+ phno = await fetchPhoneNumberFor(uid);
+ }
+
+ if (!phno) throw new Error(`Phone number not available for uid=${uid}`);
+
+ return await sendSMS(phno, smsContent);
+};
+
+app.post('/webhook', basicAuth, (req, res) => {
+ const { trigger, data, appId, region, webhook } = req.body || {};
+ const { to } = data || {};
+
+ if (trigger !== 'sms-notification-payload-generated' || webhook !== 'custom') {
+ return res.status(400).json({ message: 'Invalid trigger or webhook type' });
+ }
+
+ console.log('Received Webhook:', JSON.stringify(req.body, null, 2));
+
+ triggerSMSNotification(to, data)
+ .then((result) => {
+ console.log('Successfully triggered SMS notification for', appId, to?.uid, result);
+ })
+ .catch((error) => {
+ console.error('Something went wrong while triggering SMS notification for', appId, to?.uid, error.message);
+ });
+
+ res.status(200).json({ message: 'Webhook received successfully' });
+});
+
+app.listen(PORT, () => {
+ console.log(`Server is running on port ${PORT}`);
+});
+```
diff --git a/notifications/sms-customization.mdx b/notifications/sms-customization.mdx
deleted file mode 100644
index fac1ed46..00000000
--- a/notifications/sms-customization.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: "Customizations"
----
-
-Customizations allow controlling notifications for message events, group events, and call events. Users can set preferences for incoming notifications based on notification schedules, Do Not Disturb (DND) mode, and the mute status of specific conversations.
-
-Additional customizations include modifications to notification templates and sounds. These options also ensure that user privacy is maintained while displaying notifications on the device.
-
-For more information, refer to [Preferences, Templates & Sounds](/notifications/preferences-templates-sounds) documentation.
diff --git a/notifications/sms-integration.mdx b/notifications/sms-integration.mdx
index ab138263..df167ff2 100644
--- a/notifications/sms-integration.mdx
+++ b/notifications/sms-integration.mdx
@@ -2,34 +2,36 @@
title: "Integration"
---
-SMS Notifications integration is possible using Twilio as a provider or a custom provider. With Custom SMS provider, you can integrate using SMS providers other than Twilio.
+Connect CometChat SMS notifications to Twilio or your own provider. Twilio is built-in; use a custom provider if you prefer another SMS gateway.
-## Twilio
+## Twilio setup
We have partnered with Twilio for sending SMS Notifications so need to set up an account on [Twilio](https://www.twilio.com/) before you start using the extension.
-### Create a new App on Twilio
+### 1. Sign up and get a phone number
-1. Once you log in to Twilio, create a new app.
-2. Make a note of **Account SID** and **Auth Token** for later use.
-3. Click on "Get a Trial number" to get the Sender number. (Use the paid number if you already have one)
-4. Make a note of the sender's **phone number** for later use.
+1. Sign up for Twilio. When prompted to select a plan, click **Continue with trial**.
+2. Get a phone number with SMS capability:
+ * In the account dashboard, click **Get a phone number**, **or**
+ * Go to **Phone Numbers > Manage > Buy a number**.
+3. From the dashboard, copy your **Account SID** and **Auth Token** for later use.
+4. Make a note of the sender **phone number** for later use.
-### Store contact details
+### 2. Store contact details
Store the phone number of your users by using our [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details).
-### Enable SMS Notifications
+### 3. Enable SMS notifications
1. Login to [CometChat](https://app.cometchat.com/login) dashboard and select your app.
-2. Navigate to **Notifications** > **Notifications** in the left-hand menu.
+2. Navigate to **Notifications** > **Settings** in the left-hand menu.
3. Enable SMS notifications feature.
-### Save Twilio credentials
+### 4. Save Twilio credentials
@@ -41,7 +43,7 @@ Save the following details:
* Twilio Auth token
* Twilio sender phone number
-### Save user's timezone
+### 5. Save user timezones
A user's timezone is required to allow them to set a schedule for receiving notifications. In case the timezone is not registered, the default timezone for
@@ -64,7 +66,7 @@ This functionality is available in the following SDK versions:
-### Receive notifications
+### 6. Receive notifications
@@ -72,210 +74,22 @@ This functionality is available in the following SDK versions:
Send a message to any user and keep the conversation unread for the designated amount of time to receive an SMS notification.
-## Custom SMS provider
-
-Custom provider allows you to make use of providers apart from Twilio for triggering SMS notifications. This is implemented using webhook URL which gets all the required details that can be used to trigger SMS notifications.
-
-#### Pre-requisite
-
-1. Your webhook endpoint must be accessible over `HTTPS`. This is essential to ensure the security and integrity of data transmission.
-2. This URL should be publicly accessible from the internet.
-3. Ensure that your endpoint supports the `HTTP POST` method. Event payloads will be delivered via `HTTP POST` requests in `JSON` format.
-4. Configure your endpoint to respond immediately to the CometChat server with a 200 OK response. The response should be sent within 2 seconds of receiving the request.
-5. For security, it is recommended to set up Basic Authentication that is usually used for server-to-server calls. This requires you to configure a username and password. Whenever your webhook URL is triggered, the HTTP Header will contain:
-
-```html
-Authorization: Basic
-```
-
-#### Add credentials
-
-1. Click on the "+ Add Credentials" button.
-2. Enable the provider.
-3. Enter the publically accessible Webhook URL.
-4. It is recommended to enable Basic Authentication.
-5. Enter the username and password.
-6. Enabling the "Trigger only if phone number is stored with CometChat" setting requires users' phone numbers to be stored with CometChat using the [Update Contact details API](https://api-explorer.cometchat.com/reference/notifications-update-contact-details). When enabled, the webhook is triggered only for those users. If this setting is disabled, the webhook triggers regardless of whether users' phone numbers are stored with CometChat.
-7. Save the credentials.
-
-
-
-
-
-#### How does it work?
-
-The Custom provider is triggered once for an event in one-on-one conversation. In case of notifying users in a group, the custom provider is triggered once for each user present in that group.
-
-
-
-```json
-{
- "trigger": "sms-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-1",
- "phno": "+919299334134", // Optional
- "name": "Andrew Joseph"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "Are we meeting on this weekend?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "📷 Has shared an image",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "senderDetails": {
- "uid": "cometchat-uid-4",
- "name": "Susan Marie",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp"
- },
- "smsContent": "You've received new messages from Susan Marie! Read them at https://your-website.com/chat."
- },
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-```json
-{
- "trigger": "sms-notification-payload-generated",
- "data": {
- "to": {
- "uid": "cometchat-uid-1",
- "phno": "+919299334134", // Optional
- "name": "Andrew Joseph"
- },
- "messages": [
- {
- "sender": {
- "uid": "cometchat-uid-5",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-5.webp",
- "name": "John Paul"
- },
- "message": "Hello all! What's up?",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- },
- {
- "sender": {
- "uid": "cometchat-uid-4",
- "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-4.webp",
- "name": "Susan Marie"
- },
- "message": "This is the place I was thinking about",
- "messageObject": {CometChat Message Object}, // Present if "Include message object" is enabled. The message object is present for new messages or in case a message was edited
- }
- ],
- "groupDetails": {
- "guid": "cometchat-guid-1",
- "name": "Hiking Group",
- "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp"
- },
- "smsContent": "You've received new messages in Hiking Group! Read them at https://your-website.com."
- },
- "appId": "app123",
- "region": "us/eu/in",
- "webhook": "custom"
-}
-```
-
-
-
-
-
-#### Sample server-side code
-
-```javascript
-const express = require('express');
-const app = express();
-const PORT = process.env.PORT || 3000;
-
-app.use(express.json());
-
-// Optional: Basic authentication middleware
-const basicAuth = (req, res, next) => {
- const authHeader = req.headers['authorization'];
- if (!authHeader || !authHeader.startsWith('Basic ')) {
- return res.status(401).json({ message: 'Unauthorized' });
- }
- next();
-};
-
-const triggerSMSNotification = async (to, data) => {
- let { name, uid, phno } = to;
- let { groupDetails, senderDetails, smsContent } = data;
-
- if (groupDetails) {
- console.log('Received webhook for group SMS notification');
- }
-
- if (senderDetails) {
- console.log('Received webhook for one-on-one SMS notification');
- }
-
- if (phno == null) {
- // Your implementation to fetch Phone number
- phno = await fetchPhoneNumberFor(uid);
- }
-
- // Your implementation for sending the SMS notification
- await sendSMS(phno, smsContent);
-};
-
-app.post('/webhook', basicAuth, (req, res) => {
- const { trigger, data, appId, region, webhook } = req.body;
-
- if (
- trigger !== 'sms-notification-payload-generated' ||
- webhook !== 'custom'
- ) {
- return res.status(400).json({ message: 'Invalid trigger or webhook type' });
- }
-
- console.log('Received Webhook:', JSON.stringify(req.body, null, 2));
-
- triggerSMSNotification(to, data)
- .then((result) => {
- console.log(
- 'Successfully triggered SMS notification for',
- appId,
- to.uid,
- result
- );
- })
- .catch((error) => {
- console.error(
- 'Something went wrong while triggering SMS notification for',
- appId,
- to.uid,
- error.message
- );
- });
-
- res.status(200).json({ message: 'Webhook received successfully' });
-});
-
-app.listen(PORT, () => {
- console.log(`Server is running on port ${PORT}`);
-});
-```
-
-## Next steps
-
-Have a look at the available [preferences](/notifications/preferences-templates-sounds#sms-notification-preferences) and [templates](/notifications/preferences-templates-sounds#sms-notification-templates) for SMS notifications.
+### Quick checklist
+
+- Twilio Account SID, Auth Token, and sender number saved in the dashboard.
+- User phone numbers stored/verified via Update Contact details API.
+- Timezones synced via `updateTimezone` so schedules/quiet hours work.
+- Templates and preferences configured (see cards below); test unread trigger and monitor logs.
+
+
+{/*
+
+ Control when and how users receive SMS notifications.
+
+
+ Customize SMS notification content.
+
+
+ Integrate SMS providers other than Twilio.
+
+ */}
diff --git a/notifications/sms-notification-extension.mdx b/notifications/sms-notification-extension.mdx
index 52bfe430..f02bdf34 100644
--- a/notifications/sms-notification-extension.mdx
+++ b/notifications/sms-notification-extension.mdx
@@ -1,5 +1,5 @@
---
-title: "SMS Notification Extension (Legacy)"
+title: "SMS Notifications (Legacy)"
---
diff --git a/notifications/sms-overview.mdx b/notifications/sms-overview.mdx
index 3f74bf37..b10b6e01 100644
--- a/notifications/sms-overview.mdx
+++ b/notifications/sms-overview.mdx
@@ -1,33 +1,31 @@
---
title: "Overview"
+description: "How CometChat sends unread-message SMS fallbacks, what to configure, and where to go next."
---
-## Introduction
+SMS is the fast fallback when push/email are missed: if a message stays unread beyond your wait window, CometChat can send a concise text that brings users back to the conversation.
-SMS notifications are useful as a re-engagement tool, prompting users to return to the app after an extended absence. These are useful for providing updates on messages that are unread while the user was away. The SMS alerts or notifications are dispatched at predetermined intervals and not in real time.
+## How it works
-## Key features
+- **Trigger**: Fires when a chat message remains unread past the wait window you set.
+- **Templates**: Keep payloads lightweight (sender, short snippet, optional deep link) in Templates & Sounds.
+- **Preferences/DND**: Honors per-user/per-conversation mutes, global DND, and quiet hours before sending.
+- **Providers**: Connect Twilio or a custom provider in the dashboard; CometChat handles fan-out.
+- **Recipients**: Store verified phone numbers via API and respect opt-in/opt-out for your region.
+- **Observability**: Use logs to confirm sends and diagnose delivery issues.
-1. **Notify users at intervals**:
+## Key capabilities
- Users who have unread messages can be notified at the specified intervals. The SMS includes a message prompting users to return to the app and are triggered for every such conversation.
+- Unread-based SMS nudges for 1:1 and group chats.
+- Template variables for sender and a short preview; optional deep links to reopen the chat.
+- Quiet hours, mutes, and DND enforcement to prevent spam.
+- Plays well with push/email as layered fallbacks.
-2. **Contacts management**
+## Setup checklist
- Once the Phone numbers are verified and vetted on your end, they can be shared with the notifications system using APIs.
-
-3. **Preferences management**:
-
- Through CometChat's Notification Preferences, users and admins have the ability to customize the notification settings, that help provide pertinent alerts while avoiding notification fatigue.
-
-4. **Ability to set up a schedule**:
-
- CometChat's notifications service ensures that the notifications are delivered based on the specified daily timetable, adhering to the user's local time zone.
-
-5. **Ability to mute notifications**:
-
- Users have the option to completely mute notifications for the app (DND mode), or selectively mute them for specific users and groups, for a designated duration.
-
-6. **Ability to set up Templates**:
-
- CometChat offers developers a set of pre-defined templates that define the content shown in SMS.
+1) Connect your SMS provider (Twilio or custom) in the dashboard.
+2) Configure SMS templates and privacy in [Templates & Sounds](/notifications/templates-and-sounds); keep bodies concise and compliant.
+3) Set wait windows, per-day caps, and overrides in [SMS Preferences](/notifications/sms-preferences); keep bodies concise and compliant.
+4) Ensure user phone numbers are stored/verified via API; honor opt-out rules.
+5) Test unread triggers and monitor delivery in [Logs](/notifications/logs).
+
diff --git a/notifications/sms-preferences.mdx b/notifications/sms-preferences.mdx
new file mode 100644
index 00000000..be2b1b23
--- /dev/null
+++ b/notifications/sms-preferences.mdx
@@ -0,0 +1,33 @@
+---
+title: "SMS Preferences"
+---
+
+Control when CometChat sends unread-message SMS and what data is included in each payload.
+
+
+
+
+
+## Delivery preferences
+
+- **Notify for unread messages only** (default: on). When enabled, send only if the conversation has unread messages.
+- **Wait time before sending the next notification (per conversation, minutes)**: default 120; min 1; max 1440.
+- **Maximum SMSes per day**: default 20.
+- **Maximum SMSes per conversation per day**: default 2.
+- **Override**: when enabled, end users can change these settings in clients that expose preferences.
+
+## SMS payload options
+
+All toggles default to off; enable per your privacy/performance needs.
+- **Include entire message object in payload**
+- **Include message metadata in payload**
+- **Include sender's metadata in payload**
+- **Include receiver's metadata in payload**
+
+Keep SMS payloads concise to avoid exceeding downstream provider limits.
+
+{/* ## Related setup
+
+- Configure templates/privacy: [Templates](/notifications/sms-templates)
+- Connect providers: [Integration](/notifications/sms-integration) or [Custom Providers](/notifications/sms-custom-providers)
+- Programmatic overrides: [Preferences](/notifications/sms-preferences) */}
\ No newline at end of file
diff --git a/notifications/sms-templates.mdx b/notifications/sms-templates.mdx
new file mode 100644
index 00000000..3db530d7
--- /dev/null
+++ b/notifications/sms-templates.mdx
@@ -0,0 +1,90 @@
+---
+title: "SMS Templates"
+---
+
+Design the SMS body users receive for unread-message alerts. Sounds do not apply to SMS. Map these payload fields into your provider template or assemble the text in your service before sending.
+
+## What to customize
+
+- **Body text** for 1:1 and group SMS (default vs privacy-friendly).
+- **Preview and counts** from `messages[]` plus `senderDetails`/`groupDetails`.
+- **Link back** to your app (add in your template).
+
+## Payload shapes
+
+Field meanings:
+- `to`: recipient identifier and phone number.
+- `messages[]`: unread messages that triggered the SMS; use for counts and previews.
+- `senderDetails`: most recent sender; helpful for subject-style copy.
+- `groupDetails`: present for group conversations.
+- `smsContent`: an optional, ready-to-send string if you want to use it directly.
+
+### One-on-one
+```json
+{
+ "to": { "uid": "cometchat-uid-1", "phno": "+919299334134", "name": "Andrew Joseph" },
+ "messages": [
+ { "sender": { "uid": "cometchat-uid-4", "name": "Susan Marie" }, "message": "Are we meeting on this weekend?" },
+ { "sender": { "uid": "cometchat-uid-4", "name": "Susan Marie" }, "message": "📷 Has shared an image" }
+ ],
+ "senderDetails": { "uid": "cometchat-uid-4", "name": "Susan Marie" },
+ "smsContent": "You've received new messages from Susan Marie! Read them at https://your-website.com/chat."
+}
+```
+
+### Group
+```json
+{
+ "to": { "uid": "cometchat-uid-1", "phno": "+919299334134", "name": "Andrew Joseph" },
+ "messages": [
+ { "sender": { "uid": "cometchat-uid-5", "name": "John Paul" }, "message": "Hello all! What's up?" },
+ { "sender": { "uid": "cometchat-uid-4", "name": "Susan Marie" }, "message": "📷 Has shared an image" }
+ ],
+ "groupDetails": { "guid": "cometchat-guid-1", "name": "Hiking Group" }
+}
+```
+
+## Template examples
+
+You can use the provided `smsContent` field or build your own using `senderDetails`, `groupDetails`, and `messages.length`. Here are default and privacy-focused templates:
+
+
+
+
+
+| Use case | Default template | Privacy template | Example result (default) |
+| --- | --- | --- | --- |
+| One-on-one notification | You've received `{{messages.length}}` message(s) from `{{senderDetails.name}}`! Read them at https://your-website.com. | You've received `{{messages.length}}` message(s) from `{{senderDetails.name}}`! Read them at https://your-website.com. | You've received 2 message(s) from Susan Marie! Read them at https://your-website.com. |
+| Group notification | You've received `{{messages.length}}` message(s) in `{{groupDetails.name}}`! Read them at https://your-website.com. | You've received `{{messages.length}}` message(s) in `{{groupDetails.name}}`! Read them at https://your-website.com. | You've received 2 message(s) in Hiking Group! Read them at https://your-website.com. |
+
+## Tips
+
+- Keep bodies short to respect SMS length/carrier rules; consider removing URLs in privacy mode.
+- Use `messages.length` for counts and `senderDetails.name`/`groupDetails.name` for context.
+- Add your app link in the template; ensure it is trackable and domain-verified per provider.
+
+{/* ## Example: using with your SMS gateway
+
+Pass payload fields into your SMS provider. Keep messages concise to respect SMS length limits and carrier rules.
+
+```js
+// payload from CometChat
+const payload = {
+ to: { phno: '+15551234567', name: 'Andrew Joseph' },
+ senderDetails: { name: 'Susan Marie' },
+ groupDetails: { name: 'Hiking Group' },
+ messages: [
+ { message: 'Are we meeting this weekend?' },
+ { message: '📷 Has shared an image' }
+ ]
+};
+
+// Build the SMS body using the default template
+const body = `You've received ${payload.messages.length} message(s) from ${payload.senderDetails.name}! Read them at https://your-website.com.`;
+
+// Example send with a generic gateway client
+await smsClient.send({
+ to: payload.to.phno,
+ body
+});
+``` */}
diff --git a/notifications/templates-and-sounds.mdx b/notifications/templates-and-sounds.mdx
new file mode 100644
index 00000000..8166ea0c
--- /dev/null
+++ b/notifications/templates-and-sounds.mdx
@@ -0,0 +1,308 @@
+---
+title: "Templates & Sounds"
+---
+
+Use this guide to configure **push** notification templates, understand placeholders, and set sounds.
+
+### Quick flow
+- Pick privacy mode (default, privacy, or default with user override).
+- Edit templates for each event type (titles/bodies for default and privacy).
+- Save sounds (chat/call).
+
+### How templates work
+
+- Templates define the push title/body per event type.
+- Placeholders map to event payload fields and are resolved before delivery.
+- Configure in **Dashboard → Notifications → Templates and Sounds**; privacy toggle is under **Preferences**.
+
+**Example payload (new message)**
+
+Key paths you can reference in placeholders are shown in this sample:
+
+```json theme={null}
+{
+ "data": {
+ "id": "17",
+ "conversationId": "group_cometchat-guid-1",
+ "sender": "cometchat-uid-2",
+ "receiverType": "group",
+ "receiver": "cometchat-guid-1",
+ "category": "message",
+ "type": "text",
+ "data": {
+ "text": "Hello! How are you?",
+ "entities": {
+ "sender": {
+ "entity": {
+ "uid": "cometchat-uid-2",
+ "name": "George Alan",
+ "role": "default",
+ "avatar": "https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp",
+ "status": "available",
+ "lastActiveAt": 1707901272
+ },
+ "entityType": "user"
+ },
+ "receiver": {
+ "entity": {
+ "guid": "cometchat-guid-1",
+ "icon": "https://assets.cometchat.io/sampleapp/v2/groups/cometchat-guid-1.webp",
+ "name": "Hiking Group",
+ "type": "public",
+ "owner": "cometchat-uid-1",
+ "createdAt": 1706014061,
+ "conversationId": "group_cometchat-guid-1",
+ "onlineMembersCount": 3
+ },
+ "entityType": "group"
+ }
+ },
+ },
+ "sentAt": 1707902030,
+ "updatedAt": 1707902030
+ }
+}
+```
+
+The sender's name lives at `message.data.entities.sender.entity.name`, so use `{{message.data.entities.sender.entity.name}}` in templates.
+
+### Templates
+
+#### 1. Text message templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | `{{message.data.text}}` | New message |
+
+#### 2. Media message templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body for Image | 📷 Has sent an image | New image message |
+| Body for Audio | 🔈 Has sent an audio | New audio message |
+| Body for Video | 🎥 Has sent a video | New video message |
+| Body for File | 📄 Has sent a file | New file message |
+
+#### 3. Custom message templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | `{{message.data.text}}` | `{{message.data.text}}` |
+| Body (Fallback) | New message | New message |
+
+**Note:** The "Body (Fallback)" value is utilized when any placeholders within the "Body" fail to resolve to an appropriate substitution value.
+
+**For example**, if `{{message.data.text}}` in the aforementioned scenario evaluates to `null` or `undefined`, the "Body (Fallback)" value will be utilized.
+
+Ideally, the "Body (Fallback)" value should not contain any placeholders to prevent additional resolution failures.
+
+#### 4. Interactive form templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | `{{data.interactiveData.title}}` | New message |
+
+#### 5. Interactive card templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | `{{message.data.interactiveData.text}}` | New message |
+
+#### 6. Interactive scheduler templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | New invite | New invite |
+
+#### 7. Custom Interactive message templates
+
+| Template for | Default template values | Privacy template values |
+| ------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| Title (One-on-one) | `{{message.data.entities.sender.entity.name}}` | `{{message.data.entities.sender.entity.name}}` |
+| Title (Group) | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` | `{{message.data.entities.sender.entity.name}}` @ `{{message.data.entities.receiver.entity.name}}` |
+| Body | New message | New message |
+
+
+### Dashboard configuration
+
+Choose how templates apply to users: **default, privacy, or default with user override** (Privacy Setting dropdown in the dashboard).
+
+#### Client-side implementation
+
+**1. Fetch privacy setting**
+
+Fetch/update the privacy toggle so users can opt in when allowed.
+
+
+
+ ```js theme={null}
+ // This is applicable for web, React native, Ionic cordova
+ const preferences = await CometChatNotifications.fetchPreferences();
+
+ // Display a toggle for use privacy option
+ const usePrivacyTemplate = preferences.getUsePrivacyTemplate();
+ ```
+
+
+
+ ```kotlin theme={null}
+ CometChatNotifications.fetchPreferences(new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(NotificationPreferences notificationPreferences) {
+ // Display a toggle for use privacy option
+ boolean usePrivacyTemplate = notificationPreferences.getUsePrivacyTemplate();
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ // Something went wrong while fetching notification preferences
+ }
+ });
+ ```
+
+
+
+ ```swift theme={null}
+ CometChatNotifications.fetchPreferences { notificationPreferences in
+ // Display a toggle for use privacy option
+ let usePrivacyTemplate = notificationPreferences.usePrivacyTemplate;
+
+ } onError: { error in
+ // Something went wrong while fetching notification preferences.
+ print("fetchPreferences: \(error.errorCode) \(error.errorDescription)");
+ }
+ ```
+
+
+
+ ```dart theme={null}
+ CometChatNotifications.fetchPreferences(
+ onSuccess: (notificationPreferences) {
+ // Display a toggle for use privacy option
+ bool? usePrivacyTemplate = notificationPreferences.usePrivacyTemplate;
+
+ },
+ onError: (e) {
+ debugPrint("fetchPreferences:error ${e.toString()}");
+ });
+ ```
+
+
+
+**2. Update privacy setting**
+
+`CometChatNotifications.updatePreferences()` method is used to update a user's notification preferences. The "**override**" toggle defined in the dashboard is crucial when updating preferences. If any preference is non-overridable, the method doesn't generate an error; it instead returns the `NotificationPreferences` object with the updated values where overrides are allowed.
+
+Use this sparingly to avoid surprising users. Set only the preferences that changed.
+
+
+
+ ```js theme={null}
+ // This is applicable for web, React native, Ionic cordova
+ // The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
+
+ // Instantiate the NotificationPreferences.
+ const updatedPreferences = new NotificationPreferences();
+
+ // To update the preference for privacy template
+ updatedPreferences.setUsePrivacyTemplate(true);
+
+ // Update the preferences and receive the updated copy.
+ const notificationPreferences = await CometChatNotifications.updatePreferences(
+ updatedPreferences
+ );
+ ```
+
+
+
+ ```kotlin theme={null}
+ // The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
+ // Instantiate the NotificationPreferences.
+ NotificationPreferences updatedPreferences = new NotificationPreferences();
+
+ // To update the preference for privacy template
+ updatedPreferences.setUsePrivacyTemplate(true);
+
+ // Update the preferences.
+ CometChatNotifications.updatePreferences(updatedPreferences, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(NotificationPreferences notificationPreferences) {
+ // Updated notificationPreferences
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ // Something went wrong
+ }
+ });
+ ```
+
+
+
+ ```swift theme={null}
+ // The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
+
+ // Instantiate the NotificationPreferences.
+ let updatedPreferences = CometChatNotifications.NotificationPreferences();
+
+ // To update the preference for privacy template
+ updatedPreferences.set(usePrivacyTemplate: true)
+
+ // Update the preferences.
+ CometChatNotifications.updatePreferences(updatedPreferences) { prefs in
+ print("updatePreferences: \(prefs)")
+ } onError: { error in
+ print("updatePreferences: \(error.errorCode) \(error.errorDescription)")
+ }
+ ```
+
+
+
+ ```dart theme={null}
+ // The example demonstrates modifying all values; however, modifying only the changed values is sufficient.
+
+ // Instantiate the NotificationPreferences
+ NotificationPreferences updatedPreferences = NotificationPreferences();
+
+ // To update the preference for privacy template
+ updatedPreferences.usePrivacyTemplate = true;
+
+ // Update the preferences.
+ CometChatNotifications.updatePreferences(updatedPreferences,
+ onSuccess: (preferencesAfterUpdate) {
+ debugPrint("updatePreferences:success");
+ // Use the preferencesAfterUpdate
+ }, onError: (e) {
+ debugPrint("updatePreferences:error: ${e.toString()}");
+ });
+ ```
+
+
+
+
+### Template tips
+
+- Keep privacy variants generic to avoid leaking message content.
+- Use `sender@group` style for group titles to disambiguate conversations.
+- Avoid placeholders in fallback text to prevent unresolved values.
+
+### Sounds
+
+Include sound files in your app bundle and set the `sound` field in the push payload to the filename (omit extension if your platform expects that).
+
+| Field | Default |
+| --- | --- |
+| Calls | default |
+| Sound | default |
diff --git a/notifications/web-push-notifications-legacy.mdx b/notifications/web-push-notifications-legacy.mdx
new file mode 100644
index 00000000..fa4cbf97
--- /dev/null
+++ b/notifications/web-push-notifications-legacy.mdx
@@ -0,0 +1,410 @@
+---
+title: "Web"
+---
+
+The Push Notification extension allows you to send push notifications to mobile apps and desktop browsers.
+
+Push notifications will work in all desktop browsers which support [Push API](https://caniuse.com/#feat=push-api). These include:
+
+1. Chrome 50+
+2. Firefox 44+
+3. Edge 17+
+4. Opera 42+
+
+
+ Push notifications sample app for Web (React)
+
+ View on Github
+
+
+## Firebase Project Setup
+
+Visit [Firebase Console](https://console.firebase.google.com) and login/signup using your Gmail ID.
+
+### Step 1: Create a new Firebase Project
+
+
+
+
+
+This is a simple 3 step process where:
+
+1. You give a name to your project
+2. Add Google Analytics to your project (Optional)
+3. Configure Google Analytics account (Optional)
+
+Click on Create and you are ready to go.
+
+### Step 2: Add Firebase to your Web App
+
+1. Click on the Web icon on the below screen and Register your app with a nickname.
+2. Once done, click on Continue to Console.
+
+
+
+
+
+### Step 3: Download the service account file
+
+
+
+
+
+## Extension settings
+
+### Step 1: Enable the extension
+
+1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
+2. Go to the Extensions section and Enable the Push Notifications.
+3. Open the settings for the extension and add all the mentioned settings and hit save.
+
+
+
+
+
+### Step 2: Save your settings
+
+On the Settings page you need to enter the following:
+
+1. **Set extension version**
+
+* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
+* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
+* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+
+2. **Select the platforms that you want to support**
+
+* Select from Web, Android, Ionic, React Native, Flutter & iOS.
+
+3. **Notification payload settings**
+
+* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
+
+4. **Push payload message options**
+
+
+
+
+
+* The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
+
+* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+
+5. **Notification Triggers**
+
+
+
+
+
+* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
+
+ 1. Message Notifications
+ 2. Call Notifications
+ 3. Group Notifications
+
+* These are pretty self-explanatory and you can toggle them as per your requirement.
+
+## Web App Setup
+
+### Step 1: Folder and files setup
+
+Create a folder with the following three files:
+
+| Files | Description |
+| ------------------------- | ------------------------------------------------------------------------------------------- |
+| index.html | Displays a simple User Login Form. |
+| PushNotification.js | File with the logic to initialize CometChat and Firebase. |
+| firebase-messaging-sw\.js | Service worker shows Push Notifications when the tab is either in the background or closed. |
+
+### Step 2: Add the Firebase Config to the HTML File
+
+1. Go to the Firebase Console and click on the Web app and open up the Settings page.
+2. Go to the "General" tab on the Settings page.
+3. Scroll down and copy the Firebase SDK snippet and paste in the \ tag of your index.html file.
+
+
+
+
+
+### Step 3: Setup index.html file
+
+1. Include the latest CometChat library using CDN.
+
+2. Register the service worker file.
+
+3. Also, include the `PushNotification.js`.
+
+4. The \ has a simple form:
+
+ 1. Text input for UID.
+ 2. Login button.
+ 3. Logout button.
+
+Once done, your `index.html` file should look like this:
+
+
+
+```html
+
+
+
+
+
+
+ Push Notification Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Push Notifications (Legacy)
+
+
+
+
+
+
+```
+
+
+
+
+
+### Step 4: Setup the service worker file
+
+1. Use `importScripts` to include the `firebase-app.js` and `firebase-messaging.js` files in the service worker.
+2. Also paste in the `FIREBASE_CONFIG` object again in this file.
+3. Initialize the Firebase object using the config.
+4. Call the messaging() on the Firebase object.
+
+Once done, your `firebase-messaging-sw.js` file should look like this:
+
+
+
+```js
+importScripts('https://www.gstatic.com/firebasejs/7.21.0/firebase-app.js');
+importScripts(
+ 'https://www.gstatic.com/firebasejs/7.21.0/firebase-messaging.js'
+);
+
+const FIREBASE_CONFIG = {
+ // Your Config
+};
+
+// Initialize firebase in the service worker.
+firebase.initializeApp(FIREBASE_CONFIG);
+
+// Start Receiving Push Notifications when
+// the browser tab is in the background or closed.
+firebase.messaging();
+```
+
+
+
+
+
+### Step 5: Setup the PushNotification.js file
+
+Now our simple web app has the following:
+
+1. Setup required to start using Firebase SDK.
+2. Service worker registration when the index.html loads for the first time.
+
+Next, we can focus on the flow to setup CometChat login process along with the steps required to setup Push Notifications using Firebase Cloud Messaging (or FCM).
+
+During login:
+
+1. Initialize CometChat.
+2. Login using CometChat user.
+3. Ask for the User's permission to show Push Notifications.
+4. If permission is granted, obtain the `FCM_TOKEN`.
+5. Register the obtained `FCM_TOKEN` with the extension.
+
+During logout:
+
+1. First delete the token using the firebase object.
+2. Logout CometChat user.
+
+The above steps have been implemented in the `login` and `logout` functions in the `PushNotifications.js` file. You can copy paste the below code. Do not forget to replace the `APP_ID`, `REGION`, `AUTH_KEY` of your app in the code below.
+
+
+
+```js
+const APP_ID = 'APP_ID';
+const REGION = 'REGION';
+const AUTH_KEY = 'AUTH_KEY';
+
+const APP_SETTING = new CometChat.AppSettingsBuilder()
+ .subscribePresenceForAllUsers()
+ .setRegion(REGION)
+ .build();
+let FCM_TOKEN = '';
+
+let loginButton;
+let logoutButton;
+
+const login = async () => {
+ const UID = document.getElementById('uid').value;
+ if (!UID) {
+ document.getElementById('uid').focus();
+ return;
+ }
+ loginButton.disabled = true;
+ console.log('Initiating login... ');
+ try {
+ // CC init
+ await CometChat.init(APP_ID, APP_SETTING);
+
+ // User login
+ const loginResponse = await CometChat.login(UID, AUTH_KEY);
+ console.log('1. User login complete', loginResponse);
+
+ CometChat.getLoggedinUser().then((user) => console.log(user.name));
+ // Change the page title
+ document.title = UID + ' logged in';
+
+ // Fetch the FCM Token
+ const messaging = firebase.messaging();
+ FCM_TOKEN = await messaging.getToken();
+ console.log('2. Received FCM Token', FCM_TOKEN);
+
+ // Register the FCM Token
+ await CometChat.registerTokenForPushNotification(FCM_TOKEN);
+ console.log('3. Registered FCM Token');
+
+ logoutButton.disabled = false;
+ } catch (error) {
+ console.error(error);
+ }
+};
+
+const logout = async () => {
+ console.log('Initiating logout...');
+ loginButton.disabled = true;
+ logoutButton.disabled = true;
+ try {
+ // Delete the token
+ const messaging = firebase.messaging();
+ await messaging.deleteToken();
+
+ // Logout the user
+ await CometChat.logout();
+ console.log('5. Logged out');
+
+ // Refresh the page.
+ init();
+ window.location.reload();
+ } catch (error) {
+ console.error(error);
+ }
+};
+
+const init = () => {
+ // Basic initialization
+ loginButton = document.getElementById('loginButton');
+ logoutButton = document.getElementById('logoutButton');
+
+ loginButton.addEventListener('click', login);
+ logoutButton.addEventListener('click', logout);
+
+ logoutButton.disabled = true;
+};
+
+window.onload = () => {
+ // Call the initialization function on load.
+ setTimeout(init, 300);
+};
+```
+
+
+
+
+
+## Start receiving Push Notifications
+
+1. You can now host the project folder using Nginx, Apache web server, or even VSCode Live server extension.
+2. Launch the web app in a browser and open the browser console to see the logs.
+3. Enter the UID of the user and click on login.
+4. When asked for permission to show notifications, click on Allow.
+5. Once you see logs saying that the FCM Token has been registered, either send the browser tab to the background or close it completely.
+6. Send a message to this logged-in user from another device (using our Sample Apps) and you should be able to see the Push Notifications.
+
+## Stop receiving Push Notifications
+
+1. Reopen the previous closed browser tab and click on logout.
+2. The `FCM_TOKEN` will be deleted on the extension's end on the `CometChat.logout()` call.
+3. As a good practice, the `FCM_TOKEN` should also be deleted using the `firebase.messaging().deleteToken()`.
+
+## Custom body for notifications
+
+To send custom body for notifications or to receive notification of `CustomMessage`, you need to set metadata while sending the `CustomMessage`.
+
+
+
+```js
+var receiverID = 'UID';
+var customData = {
+ latitude: '50.6192171633316',
+ longitude: '-72.68182268750002',
+};
+
+var customType = 'location';
+var receiverType = CometChat.RECEIVER_TYPE.USER;
+var metadata = {
+ pushNotification: 'Your Notification Message',
+};
+
+var customMessage = new CometChat.CustomMessage(
+ receiverID,
+ receiverType,
+ customType,
+ customData
+);
+
+customMessage.setMetadata(metadata);
+
+CometChat.sendCustomMessage(customMessage).then(
+ (message) => {
+ // Message sent successfully.
+ console.log('custom message sent successfully', message);
+ },
+ (error) => {
+ console.log('custom message sending failed with error', error);
+ // Handle exception.
+ }
+);
+```
+
+
+
+
diff --git a/notifications/web-push-notifications.mdx b/notifications/web-push-notifications.mdx
index ae74193e..17c583e4 100644
--- a/notifications/web-push-notifications.mdx
+++ b/notifications/web-push-notifications.mdx
@@ -1,410 +1,470 @@
---
-title: "Web"
+title: "Web Push Notifications"
+description: "Set up FCM web push for CometChat React UI Kit—service worker, VAPID keys, token registration, and foreground/background handlers."
---
-The Push Notification extension allows you to send push notifications to mobile apps and desktop browsers.
-
-Push notifications will work in all desktop browsers which support [Push API](https://caniuse.com/#feat=push-api). These include:
-
-1. Chrome 50+
-2. Firefox 44+
-3. Edge 17+
-4. Opera 42+
-
- Push notifications sample app for Web (React)
-
- View on Github
+ Reference implementation of React UI Kit, FCM and Push Notification Setup.
-## Firebase Project Setup
-
-Visit [Firebase Console](https://console.firebase.google.com) and login/signup using your Gmail ID.
-
-### Step 1: Create a new Firebase Project
-
-
-
-
-
-This is a simple 3 step process where:
-
-1. You give a name to your project
-2. Add Google Analytics to your project (Optional)
-3. Configure Google Analytics account (Optional)
-
-Click on Create and you are ready to go.
-
-### Step 2: Add Firebase to your Web App
+## What this guide covers
-1. Click on the Web icon on the below screen and Register your app with a nickname.
-2. Once done, click on Continue to Console.
+- CometChat dashboard setup (FCM provider + Provider ID) and Firebase web config + VAPID key.
+- Service worker + Firebase Messaging wiring for foreground/background pushes.
+- Token registration/unregistration with `CometChatNotifications` and navigation on notification click.
+- Testing and troubleshooting tips for web push.
-
-
-
+{/* ## What you need first
-### Step 3: Download the service account file
+- CometChat App ID, Region, Auth Key; Push Notifications enabled with an **FCM provider** (copy the Provider ID).
+- Firebase project with a **Web app** registered; Firebase config object; **VAPID key** from Cloud Messaging.
+- Node 18+ and a modern browser that supports service workers/notifications. */}
-
-
-
+## How CometChat + FCM work on web
-## Extension settings
+- **Provider ID**: Tells CometChat which Firebase credentials to use when sending to your web app.
+- **Tokens**: `getToken` returns a browser token (per origin/device). Register it after login:
+ `CometChatNotifications.registerPushToken(token, CometChatNotifications.PushPlatforms.FCM_WEB, providerId)`.
+- **Handlers**: Foreground messages come via `messaging.onMessage`; background uses `firebase-messaging-sw.js` with `onBackgroundMessage`.
+- **Navigation**: Service worker sends a postMessage or focuses the client; the app routes to the right view.
-### Step 1: Enable the extension
+## 1. Dashboard: enable push + add FCM provider
-1. Login to [CometChat](https://app.cometchat.com/login) and select your app.
-2. Go to the Extensions section and Enable the Push Notifications extension.
-3. Open the settings for the extension and add all the mentioned settings and hit save.
+1. Go to **Notifications → Settings** and enable **Push Notifications**.
+2. Add/configure an **FCM** provider and copy the **Provider ID** (used in code).
-
+
-### Step 2: Save your settings
-
-On the Settings page you need to enter the following:
-
-1. **Set extension version**
-
-* If you are setting it for the first time, Select `V2` to start using the token-based version of the Push Notification extension.
-* If you already have an app using `V1` and want to migrate your app to use `V2`, then Select `V1 & V2` option. This ensures that the users viewing the older version of your app also receive Push Notifications.
-* Eventually, when all your users are on the latest version of your app, you can change this option to `V2`, thus turning off `V1` (Topic-based) Push Notifications completely.
+## 2. Firebase setup (web app + VAPID)
-2. **Select the platforms that you want to support**
-
-* Select from Web, Android, Ionic, React Native, Flutter & iOS.
-
-3. **Notification payload settings**
-
-* You can control if the notification key should be in the Payload or not. Learn more about the FCM Messages [here](https://firebase.google.com/docs/cloud-messaging/concept-options).
-
-4. **Push payload message options**
+1. In Firebase Console, create/select a project.
+2. Add a **Web** app (`>`), copy the **Firebase config** object.
+3. In **Project settings → Cloud Messaging**, generate/copy the **VAPID key** under Web Push certificates.
-
+
-* The maximum payload size supported by FCM and APNs for push notifications is approximately 4 KB. Due to the inclusion of CometChat's message object, the payload size may exceed this limit, potentially leading to non-delivery of push notifications for certain messages. The options provided allow you to remove the sender's metadata, receiver's metadata, message metadata and trim the content of the text field.
-
-* The message metadata includes the outputs of the Thumbnail Generation, Image Moderation, and Smart Replies extensions. You may want to retain this metadata if you need to customize the notification displayed to the end user based on these outputs.
+## 3. Install dependencies
-5. **Notification Triggers**
+Install firebase SDK:
-
-
-
-
-* Select the triggers for sending Push Notifications. These triggers can be classified into 3 main categories:
-
- 1. Message Notifications
- 2. Call Notifications
- 3. Group Notifications
-
-* These are pretty self-explanatory and you can toggle them as per your requirement.
-
-## Web App Setup
-
-### Step 1: Folder and files setup
-
-Create a folder with the following three files:
+```bash
+npm install firebase@^10.3.1
+```
-| Files | Description |
-| ------------------------- | ------------------------------------------------------------------------------------------- |
-| index.html | Displays a simple User Login Form. |
-| PushNotification.js | File with the logic to initialize CometChat and Firebase. |
-| firebase-messaging-sw\.js | Service worker shows Push Notifications when the tab is either in the background or closed. |
+## 4. Constants
-### Step 2: Add the Firebase Config to the HTML File
+File: `src/AppConstants.js` (or equivalent)
-1. Go to the Firebase Console and click on the Web app and open up the Settings page.
-2. Go to the "General" tab on the Settings page.
-3. Scroll down and copy the Firebase SDK snippet and paste in the \ tag of your index.html file.
+Set the CometChat Constants from your dashboard and Firebase config + VAPID key.
-
-
-
+```js lines highlight={2-5, 9-15, 18}
+export const COMETCHAT_CONSTANTS = {
+ APP_ID: "",
+ REGION: "",
+ AUTH_KEY: "",
+ FCM_PROVIDER_ID: "",
+};
-### Step 3: Setup index.html file
+export const FIREBASE_CONFIG = {
+ apiKey: "",
+ authDomain: "",
+ projectId: "",
+ storageBucket: "",
+ messagingSenderId: "",
+ appId: "",
+ measurementId: ""
+};
-1. Include the latest CometChat library using CDN.
+export const FIREBASE_VAPID_KEY = "";
+```
-2. Register the service worker file.
+## 4. Configure Firebase (frontend)
-3. Also, include the `PushNotification.js`.
+File: `src/firebase.js` (or equivalent)
-4. The \ has a simple form:
+This code:
+- Initializes Firebase app and messaging.
+- Requests notification permission, fetches FCM token, and registers it with CometChat after login.
+- Sets up foreground message handler to show notifications.
- 1. Text input for UID.
- 2. Login button.
- 3. Logout button.
+```js lines
+import { initializeApp } from "firebase/app";
+import { getMessaging, getToken, onMessage } from "firebase/messaging";
+import { CometChatNotifications } from "@cometchat/chat-sdk-javascript";
+import { COMETCHAT_CONSTANTS, FIREBASE_CONFIG, FIREBASE_VAPID_KEY } from "./AppConstants";
-Once done, your `index.html` file should look like this:
+let messagingInstance = null;
-
-
-```html
-
-
+export default async function firebaseInitialize(navigate) {
+ try {
+ // Check if Firebase is already initialized
+ let app;
+ try {
+ app = initializeApp(FIREBASE_CONFIG);
+ } catch (error) {
+ // Firebase might already be initialized
+ const { getApps } = await import("firebase/app");
+ const apps = getApps();
+ if (apps.length > 0) {
+ app = apps[0];
+ } else {
+ throw error;
+ }
+ }
-
-
-
- Push Notification Sample
+ messagingInstance = getMessaging(app);
-
-
-
+ // Check notification permission first
+ if (Notification.permission === "default") {
+ return;
+ }
-
-
-
-
-
-
-
+ // Get FCM token
+ const currentToken = await getToken(messagingInstance, {
+ vapidKey: FIREBASE_VAPID_KEY,
+ });
+
+ if (currentToken) {
+
+ // Check if user is logged in before registering
+ const { CometChat } = await import("@cometchat/chat-sdk-javascript");
+ const loggedInUser = await CometChat.getLoggedinUser();
+
+ if (!loggedInUser) {
+ return;
+ }
+
+ // Register push token with CometChat
+ try {
+ const payload = await CometChatNotifications.registerPushToken(
+ currentToken,
+ CometChatNotifications.PushPlatforms.FCM_WEB,
+ COMETCHAT_CONSTANTS.FCM_PROVIDER_ID
+ );
+ } catch (err) {
+ console.error("Firebase initialization error:", err);
+ }
+ } else {
+ console.error("No registration token available. Request permission to generate one.");
+ }
+ } catch (err) {
+ console.error("Firebase initialization error:", err);
+ }
-
-
-
+ // Set up foreground message handler
+ if (messagingInstance) {
+ onMessage(messagingInstance, function (payload) {
+
+ // Show notification when app is in foreground
+ if (Notification.permission === "granted") {
+ const notificationTitle = payload.data?.title || "New Message";
+ let notificationBody = payload.data?.body || "";
+
+ // Handle call notifications
+ if (payload.data?.type === "call") {
+ switch (payload.data.callAction) {
+ case "cancelled":
+ notificationBody = "Call cancelled";
+ break;
+ case "initiated":
+ notificationBody = `Incoming ${payload.data.callType} call`;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const notificationOptions = {
+ body: notificationBody,
+ icon: payload.data?.senderAvatar || "/logo192.png",
+ data: payload.data,
+ tag: payload.data?.tag,
+ requireInteraction: false,
+ };
+
+ new Notification(notificationTitle, notificationOptions);
+ }
+ });
+ }
+}
-
- Push Notifications (Legacy)
-
-
-
-
+// Function to register push token after login
+export async function registerPushTokenAfterLogin() {
+ if (!messagingInstance) {
+ return;
+ }
-
+ try {
+ const currentToken = await getToken(messagingInstance, {
+ vapidKey: FIREBASE_VAPID_KEY,
+ });
+
+ if (currentToken) {
+ const payload = await CometChatNotifications.registerPushToken(
+ currentToken,
+ CometChatNotifications.PushPlatforms.FCM_WEB,
+ COMETCHAT_CONSTANTS.FCM_PROVIDER_ID
+ );
+ }
+ } catch (err) {
+ console.error("Error registering push token after login:", err);
+ }
+}
```
-
+Use your **VAPID key** in `getToken` calls.
-
+## 5. Service worker (background pushes)
-### Step 4: Setup the service worker file
+File: `public/firebase-messaging-sw.js`
-1. Use `importScripts` to include the `firebase-app.js` and `firebase-messaging.js` files in the service worker.
-2. Also paste in the `FIREBASE_CONFIG` object again in this file.
-3. Initialize the Firebase object using the config.
-4. Call the messaging() on the Firebase object.
+This code:
+- Initializes Firebase app in the service worker.
+- Handles background messages with `onBackgroundMessage`.
+- Manages notification clicks to focus the app and send data for navigation.
+- Ensure your app registers the service worker (e.g., in `index.tsx`) and listens for `message` events to navigate.
-Once done, your `firebase-messaging-sw.js` file should look like this:
+```js lines
+/* eslint-disable no-restricted-globals */
+/* eslint-disable no-undef */
+// required to setup background notification handler when browser is not in focus or in background and
+// In order to receive the onMessage event, app must define the Firebase messaging service worker
+// self.importScripts("localforage.js");
-
-
-```js
-importScripts('https://www.gstatic.com/firebasejs/7.21.0/firebase-app.js');
importScripts(
- 'https://www.gstatic.com/firebasejs/7.21.0/firebase-messaging.js'
+ "https://www.gstatic.com/firebasejs/9.15.0/firebase-app-compat.js"
+);
+importScripts(
+ "https://www.gstatic.com/firebasejs/9.15.0/firebase-messaging-compat.js"
);
+var TAG = "[Firebase-sw.js]";
+
+self.addEventListener("notificationclick", async function (event) {
+ console.log(TAG, "notificationclick", event, event.clientId);
+ if (event?.notification?.data) {
+ let data = event.notification.data;
+ event.waitUntil(
+ self.clients
+ .matchAll({ type: "window", includeUncontrolled: true })
+ .then((clientList) => {
+ if (clientList.length > 0) {
+ clientList[0].postMessage({
+ message: data,
+ });
+ return (
+ clientList[0]
+ .focus()
+ .catch((error) => {
+ console.log(error);
+ return self.clients.openWindow(clientList[0].url); // Adjust this URL as necessary for your application
+ })
+ );
+ } else {
+ // Open a new client (tab) if there are no existing clients
+ self.clients.openWindow("/");
+ setTimeout(() => {
+ self.clients
+ .matchAll({ type: "window", includeUncontrolled: true })
+ .then((clientList) => {
+ if (clientList.length > 0) {
+ clientList[0].postMessage({
+ message: {...data,fromBackground: true},
+ });
+ }
+ return;
+ });
+ }, 1500);
+ }
+ })
+ );
+ }
-const FIREBASE_CONFIG = {
- // Your Config
+ event.notification.close();
+});
+// "Default" Firebase configuration (prevents errors)
+const defaultConfig = {
+ apiKey: true,
+ projectId: true,
+ messagingSenderId: true,
+ appId: true,
};
-// Initialize firebase in the service worker.
-firebase.initializeApp(FIREBASE_CONFIG);
-
-// Start Receiving Push Notifications when
-// the browser tab is in the background or closed.
-firebase.messaging();
+// Initialize Firebase app
+firebase.initializeApp(self.firebaseConfig || defaultConfig);
+let messaging;
+try {
+ messaging = firebase.messaging();
+ // Customize background notification handling here
+ messaging.onBackgroundMessage((payload) => {
+ console.log("Background Message:", payload);
+ const notificationTitle = payload.data.title;
+ if (
+ payload.data.type === "call" &&
+ (payload.data.callAction === "unanswered" ||
+ payload.data.callAction === "busy" ||
+ payload.data.callAction === "ongoing")
+ ) {
+ return;
+ }
+ let body = payload.data.body;
+ if (payload.data.type === "call") {
+ switch (payload.data.callAction) {
+ case "cancelled":
+ body = `Call cancelled`;
+ break;
+ case "initiated":
+ body = `Incoming ${payload.data.callType} call`;
+ break;
+ default:
+ break;
+ }
+ }
+ const notificationOptions = {
+ title: payload.data.title,
+ icon: payload.data.senderAvatar,
+ data: payload.data,
+ tag: payload.data.tag,
+ body: body,
+ };
+ self.registration.showNotification(notificationTitle, notificationOptions);
+ });
+} catch (err) {
+ console.error("Failed to initialize Firebase Messaging", err);
+}
```
-
-
-
-
-### Step 5: Setup the PushNotification.js file
-
-Now our simple web app has the following:
-
-1. Setup required to start using Firebase SDK.
-2. Service worker registration when the index.html loads for the first time.
-
-Next, we can focus on the flow to setup CometChat login process along with the steps required to setup Push Notifications using Firebase Cloud Messaging (or FCM).
+Ensure your app registers the service worker (e.g., in `index.tsx`) and listens for `message` events to navigate.
-During login:
+## 6. Request permission + register token after login
-1. Initialize CometChat.
-2. Login using CometChat user.
-3. Ask for the User's permission to show Push Notifications.
-4. If permission is granted, obtain the `FCM_TOKEN`.
-5. Register the obtained `FCM_TOKEN` with the extension.
+In your app initialization (e.g., `App.tsx`):
-During logout:
+This code:
+- Requests notification permission.
+- Fetches FCM token and registers it with CometChat after user login.
+- Handles token refresh by re-registering if it changes.
-1. First delete the token using the firebase object.
-2. Logout CometChat user.
-
-The above steps have been implemented in the `login` and `logout` functions in the `PushNotifications.js` file. You can copy paste the below code. Do not forget to replace the `APP_ID`, `REGION`, `AUTH_KEY` of your app in the code below.
-
-
-
-```js
-const APP_ID = 'APP_ID';
-const REGION = 'REGION';
-const AUTH_KEY = 'AUTH_KEY';
+```ts lines
+const token = await getToken(messaging, { vapidKey: VAPID_KEY });
+await CometChatNotifications.registerPushToken(
+ token,
+ CometChatNotifications.PushPlatforms.FCM_WEB,
+ COMETCHAT_CONSTANTS.FCM_PROVIDER_ID
+);
+```
-const APP_SETTING = new CometChat.AppSettingsBuilder()
+- Run after the user logs in; retry on failure.
+- On logout: `CometChatNotifications.unregisterPushToken()` before ending the session.
+- Handle token refresh by calling `getToken` again and re-registering if it changes.
+
+**Example Implementation**:
+
+This code:
+- Initializes CometChat UI Kit.
+- Initializes Firebase messaging.
+- Logs in the user if not already logged in.
+- Mounts the React app.
+- Registers the push token after login.
+
+```tsx lines highlight={11, 13, 41, 57}
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./index.css";
+import App from "./App.tsx";
+import { COMETCHAT_CONSTANTS } from "./AppConstants.ts";
+
+import { CometChatUIKit, UIKitSettingsBuilder } from "@cometchat/chat-uikit-react";
+
+// Your firebase helper (from firebase.js)
+// Adjust the path if your file lives somewhere else.
+import firebaseInitialize, { registerPushTokenAfterLogin } from "./firebase";
+
+const UID = "cometchat-uid-1";
+
+/**
+ * Configure the CometChat UI Kit using the UIKitSettingsBuilder.
+ */
+const UIKitSettings = new UIKitSettingsBuilder()
+ .setAppId(COMETCHAT_CONSTANTS.APP_ID)
+ .setRegion(COMETCHAT_CONSTANTS.REGION)
+ .setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY)
.subscribePresenceForAllUsers()
- .setRegion(REGION)
.build();
-let FCM_TOKEN = '';
-let loginButton;
-let logoutButton;
+function mountApp() {
+ createRoot(document.getElementById("root")!).render(
+
+
+
+ );
+}
-const login = async () => {
- const UID = document.getElementById('uid').value;
- if (!UID) {
- document.getElementById('uid').focus();
- return;
- }
- loginButton.disabled = true;
- console.log('Initiating login... ');
+async function boot() {
try {
- // CC init
- await CometChat.init(APP_ID, APP_SETTING);
-
- // User login
- const loginResponse = await CometChat.login(UID, AUTH_KEY);
- console.log('1. User login complete', loginResponse);
-
- CometChat.getLoggedinUser().then((user) => console.log(user.name));
- // Change the page title
- document.title = UID + ' logged in';
-
- // Fetch the FCM Token
- const messaging = firebase.messaging();
- FCM_TOKEN = await messaging.getToken();
- console.log('2. Received FCM Token', FCM_TOKEN);
-
- // Register the FCM Token
- await CometChat.registerTokenForPushNotification(FCM_TOKEN);
- console.log('3. Registered FCM Token');
-
- logoutButton.disabled = false;
- } catch (error) {
- console.error(error);
- }
-};
-
-const logout = async () => {
- console.log('Initiating logout...');
- loginButton.disabled = true;
- logoutButton.disabled = true;
- try {
- // Delete the token
- const messaging = firebase.messaging();
- await messaging.deleteToken();
+ // 1) Init CometChat UIKit
+ await CometChatUIKit.init(UIKitSettings);
+ console.log("CometChat UI Kit initialized successfully.");
+
+ // 2) Init Firebase messaging + foreground handlers once
+ // (This sets up messagingInstance and onMessage handler in your firebase.js)
+ await firebaseInitialize();
+
+ // 3) Login if needed
+ const existing = await CometChatUIKit.getLoggedinUser();
+ if (!existing) {
+ const user = await CometChatUIKit.login(UID);
+ console.log("Login Successful:", { user });
+ } else {
+ console.log("User already logged in:", { user: existing });
+ }
- // Logout the user
- await CometChat.logout();
- console.log('5. Logged out');
+ // 4) Mount UI (don’t block UI on push)
+ mountApp();
- // Refresh the page.
- init();
- window.location.reload();
+ // 5) Register push AFTER login (requests permission if needed)
+ // Non-blocking so app loads even if user denies permission
+ void registerPushTokenAfterLogin();
} catch (error) {
- console.error(error);
+ console.error("CometChat UI Kit initialization/login failed:", error);
}
-};
-
-const init = () => {
- // Basic initialization
- loginButton = document.getElementById('loginButton');
- logoutButton = document.getElementById('logoutButton');
-
- loginButton.addEventListener('click', login);
- logoutButton.addEventListener('click', logout);
+}
- logoutButton.disabled = true;
-};
-
-window.onload = () => {
- // Call the initialization function on load.
- setTimeout(init, 300);
-};
+boot();
```
-
-
-
-
-## Start receiving Push Notifications
-
-1. You can now host the project folder using Nginx, Apache web server, or even VSCode Live server extension.
-2. Launch the web app in a browser and open the browser console to see the logs.
-3. Enter the UID of the user and click on login.
-4. When asked for permission to show notifications, click on Allow.
-5. Once you see logs saying that the FCM Token has been registered, either send the browser tab to the background or close it completely.
-6. Send a message to this logged-in user from another device (using our Sample Apps) and you should be able to see the Push Notifications.
+## 7. Foreground + background handling
-## Stop receiving Push Notifications
+- **Foreground**: `messaging.onMessage` → show a `Notification` or in-app toast; deep link using payload data.
+- **Background/killed**: service worker `onBackgroundMessage` shows the notification; `notificationclick` focuses the tab and sends a message for navigation.
+- Suppress duplicates if the conversation is already active.
-1. Reopen the previous closed browser tab and click on logout.
-2. The `FCM_TOKEN` will be deleted on the extension's end on the `CometChat.logout()` call.
-3. As a good practice, the `FCM_TOKEN` should also be deleted using the `firebase.messaging().deleteToken()`.
+## 8. Testing checklist
-## Custom body for notifications
-
-To send custom body for notifications or to receive notification of `CustomMessage`, you need to set metadata while sending the `CustomMessage`.
-
-
-
-```js
-var receiverID = 'UID';
-var customData = {
- latitude: '50.6192171633316',
- longitude: '-72.68182268750002',
-};
-
-var customType = 'location';
-var receiverType = CometChat.RECEIVER_TYPE.USER;
-var metadata = {
- pushNotification: 'Your Notification Message',
-};
-
-var customMessage = new CometChat.CustomMessage(
- receiverID,
- receiverType,
- customType,
- customData
-);
-
-customMessage.setMetadata(metadata);
-
-CometChat.sendCustomMessage(customMessage).then(
- (message) => {
- // Message sent successfully.
- console.log('custom message sent successfully', message);
- },
- (error) => {
- console.log('custom message sending failed with error', error);
- // Handle exception.
- }
-);
-```
+1. Service worker registered (DevTools → Application → Service Workers shows “activated”).
+2. Permission prompt appears and is granted (`Notification.permission === "granted"`).
+3. Login → token fetched → `registerPushToken` succeeds (check console/logs).
+4. Foreground message shows a notification; click navigates to the right chat.
+5. Background/tab inactive message shows a notification; click focuses tab and routes correctly.
+6. Logout → `unregisterPushToken` runs without errors.
-
+## 9. Troubleshooting
-
+| Symptom | Quick checks |
+| --- | --- |
+| No notification | Service worker registered? Permission granted? VAPID key matches Firebase project? FCM Provider ID set in code? |
+| Token registration fails | Run after login; confirm Provider ID; ensure correct Firebase config domain/origin; check console errors. |
+| Click does nothing | Ensure `notificationclick` handler posts a message or opens the client; app listens for postMessage to navigate. |
+| Foreground only | Verify `onBackgroundMessage` in service worker; confirm service worker file is in `/public` and registered. |
+| Wrong project | Config/VAPID from a different Firebase project will invalidate tokens—recreate tokens after updating. |