From c30810ee7d9c9ae8a17448b4b6dc5d385bf71509 Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Mon, 27 Jul 2026 11:02:05 +0530 Subject: [PATCH 01/11] Updated Migration Guide --- .../flutter-push-notifications-android.mdx | 550 ++++++++++-------- .../flutter-push-notifications-ios.mdx | 471 +++++++-------- 2 files changed, 521 insertions(+), 500 deletions(-) diff --git a/notifications/flutter-push-notifications-android.mdx b/notifications/flutter-push-notifications-android.mdx index 3a761817e..30bb8730e 100644 --- a/notifications/flutter-push-notifications-android.mdx +++ b/notifications/flutter-push-notifications-android.mdx @@ -1,39 +1,56 @@ --- title: "Flutter Push Notifications (Android)" -description: "CometChat push notifications in Flutter apps on Android using Firebase Cloud Messaging (FCM)." +description: "Add CometChat push notifications and VoIP calls to a Flutter Android app with the drop-in cometchat_push_notifications SDK and Firebase Cloud Messaging (FCM)." --- - - - Reference implementation of Flutter UI Kit, FCM and Push Notification Setup. - - + + + + The drop-in push & VoIP plugin on pub.dev. + + + Source, changelog, and example app. + + + ## 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. -- App icon badge count and grouped notifications using `unreadMessageCount` from the CometChat push payload. +- CometChat dashboard setup (enable push, add an FCM provider) with screenshots. +- Firebase/FCM wiring (service account, `google-services.json`, Gradle). +- Adding the `cometchat_push_notifications` plugin and initializing it. +- Requesting permission and registering the FCM token after login. +- Receiving pushes and letting the SDK render chat notifications and full-screen calls. +- Handling notification taps, incoming-call navigation, badge counts, and OEM permissions. +- Testing and troubleshooting. + + +The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `PNRegistry`, `flutter_callkit_incoming`, and native `MainActivity`/`CallActionReceiver` bridges. Token registration, foreground presentation, notification taps, badge management, and the full incoming-call experience (including the lock-screen call activity) are handled inside the plugin. + -{/* ## What you need first +## How FCM + CometChat + the SDK work together -- 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. */} +- **FCM's role:** Issues the Android registration token and delivers the CometChat push payload to the device as a data message. +- **CometChat's role:** The FCM provider you add in the dashboard stores your Firebase service account. When you register the FCM token after login, CometChat binds it to the logged-in user and sends pushes to FCM on your behalf. +- **The plugin's role:** `cometchat_push_notifications` retrieves the FCM token, registers it with CometChat, and — when you pass an incoming message to `handlePushNotification` — parses the payload and either shows a chat notification or drives the full incoming-call UI. It internally uses [`notification_voip_plugin`](https://pub.dev/packages/notification_voip_plugin) and [`cometchat_sdk`](https://pub.dev/packages/cometchat_sdk), which pub resolves for you. -## How FCM + CometChat work together +**Flow:** Permission (`POST_NOTIFICATIONS` on Android 13+) → Firebase returns the FCM token → after your CometChat user logs in, call `registerToken(PushPlatform.FCM_FLUTTER_ANDROID)` → CometChat sends to FCM → your `firebase_messaging` handler forwards `message.data` to `CometChatPushNotifications.handlePushNotification` → the plugin displays the notification or call → taps route through `onNotificationTap` / `handleIncomingCallLaunch`. -- **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. +## Prerequisites -## 1. Enable push and add providers (CometChat Dashboard) +- Flutter 3.3.0+ / Dart 3.10.8+, and **Kotlin 2.0 or newer** in your Android build (required by `notification_voip_plugin` 2.1.0+). +- Android `minSdkVersion 24`, `compileSdkVersion 36`. +- A Firebase project with an Android app (package name matches your `applicationId`) and Cloud Messaging enabled; `google-services.json` in `android/app/`. +- CometChat app credentials (App ID, Region, Auth Key) with **Push Notifications** enabled and an **FCM provider** configured. +- A physical Android device — full-screen call notifications and background delivery are unreliable on emulators. + +## 1. Enable push and add an FCM provider (CometChat Dashboard) 1. Go to **Notifications → Settings** and enable **Push Notifications**. @@ -41,319 +58,366 @@ description: "CometChat push notifications in Flutter apps on Android using Fire 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. +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**. Upload FCM service account JSON -Keep the provider ID—you’ll use it in `AppCredentials.fcmProviderId`. +Keep the provider ID — you'll pass it to `registerToken`. ## 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. +1. In the Firebase Console, 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. Firebase - Push Notifications -### 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: +Store your CometChat and provider values somewhere your app can read them (for example a small `AppCredentials` class): ```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"; + static const appId = "YOUR_APP_ID"; + static const region = "YOUR_REGION"; + static const authKey = "YOUR_AUTH_KEY"; + static const 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. Add the plugin and configure Gradle -### 3.2 Wire the entry points +### 3.1 Dependencies -**[`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): +Add the plugin plus Firebase Messaging (used to receive the FCM data messages) to `pubspec.yaml`: ```yaml lines dependencies: + cometchat_push_notifications: ^1.0.1 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`. +`cometchat_sdk` and `notification_voip_plugin` are pulled in transitively. Run: + +```bash +flutter pub get +``` -## 4. Configure the native Android layer +Then run `flutterfire configure` if you still need to generate `firebase_options.dart`. -### 4.1 Gradle + Firebase +### 3.2 Gradle + Kotlin -1. Add `google-services.json` to `android/app`. -2. Ensure `android/app/build.gradle` applies the plugins used in the sample: +1. Add `google-services.json` to `android/app/`. +2. Apply the Google Services plugin and ensure Kotlin is **2.0+** in `android/settings.gradle.kts`: -```gradle lines +```kotlin lines plugins { - id "com.android.application" - id "com.google.gms.google-services" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false + id("com.google.gms.google-services") version "4.4.2" apply false } ``` -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. +3. Apply the plugins in `android/app/build.gradle.kts`: -### 4.2 Manifest permissions and components +```kotlin lines +plugins { + id("com.android.application") + id("kotlin-android") + id("com.google.gms.google-services") + id("dev.flutter.flutter-gradle-plugin") +} +``` -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: +4. Set `applicationId` to your package name and keep `minSdk = 24` or higher. -- 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. + +You do **not** need to add notification, call, full-screen-intent, or lock-screen permissions to your `AndroidManifest.xml`. The plugin declares everything it needs (including the lock-screen `IncomingCallActivity` and the Decline broadcast receiver), and Gradle merges them into your app automatically. + -### 4.3 Kotlin bridge for call intents +## 4. Initialize the SDK -- [`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. +### 4.1 `main.dart` -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. +Initialize Firebase and the plugin before `runApp`, register a background message handler, and expose the `incomingCallMain` entrypoint that the plugin's lock-screen call activity runs. -## 5. Token registration and runtime events +```dart lines +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; + +/// Runs in a background isolate for data messages delivered while the app is +/// terminated or backgrounded. Must initialize the plugin before handling. +@pragma('vm:entry-point') +Future _firebaseBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(); + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + ), + ); + await CometChatPushNotifications.handlePushNotification(message.data); +} -### 5.1 FCM tokens +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + // Fires when the user accepts from the plugin's default call screen. + onCallAccepted: (call) { + // Start your CometChat call session, then: + // CometChatPushNotifications.setCallConnected(call.sessionId); + }, + // Fires on decline (including from the lock-screen notification action). + onCallDeclined: (call) { + // Reject the call server-side via the CometChat SDK. + }, + ), + ); -`FirebaseService.init` requests notification permission, sets the background handler (`firebaseMessagingBackgroundHandler`), and registers tokens: + // Forward FCM data messages to the plugin. + FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler); + FirebaseMessaging.onMessage.listen((message) { + CometChatPushNotifications.handlePushNotification(message.data); + }); -```dart lines -final token = await FirebaseMessaging.instance.getToken(); -if (token != null) { - PNRegistry.registerPNService(token, true, false); // platform: FCM_FLUTTER_ANDROID + runApp(const MyApp()); } -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. +The `CometChatNotificationConfig` also accepts: -### 5.2 Local notifications and navigation +| Field | Default | Purpose | +| --- | --- | --- | +| `customCallScreenBuilder` | `null` | Return your own incoming-call widget. When set, you own accept/reject, dismissing the call notification (`cancelCallNotification`), and popping the screen; `onCallAccepted`/`onCallDeclined` are **not** called. | +| `onCallAccepted` / `onCallDeclined` | `null` | Callbacks for the built-in default call screen. | +| `callActionHandler` | default | Subclass `CometChatCallActionHandler` to intercept mute/speaker/camera (e.g. drive your WebRTC engine). | +| `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | +| `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | -- `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. +### 4.2 The `incomingCallMain` entrypoint -### 5.3 Call events (VoIP-like pushes) +When a call arrives while the device is locked, the plugin launches a dedicated full-screen activity that runs a separate Dart entrypoint named `incomingCallMain`. Define it in `main.dart`. It talks to the plugin's native activity over the `cometchat_locked_call` method channel: -- 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. +```dart lines +import 'package:flutter/services.dart'; -## 6. Badge count and grouped notifications +@pragma('vm:entry-point') +void incomingCallMain() { + WidgetsFlutterBinding.ensureInitialized(); + runApp(const _LockedCallApp()); +} -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. You can use this to set the app icon badge and enrich local notifications. +class _LockedCallApp extends StatefulWidget { + const _LockedCallApp(); + @override + State<_LockedCallApp> createState() => _LockedCallAppState(); +} -### 6.1 Enable unread badge count on the CometChat Dashboard +class _LockedCallAppState extends State<_LockedCallApp> { + static const _channel = MethodChannel('cometchat_locked_call'); + CometChatCallDetails? _call; + + @override + void initState() { + super.initState(); + // The native activity calls "reload" for each new/duplicate call. + _channel.setMethodCallHandler((call) async { + if (call.method == 'reload') _loadCall(); + }); + _loadCall(); + } -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Scroll to the bottom and enable the **Unread Badge Count** toggle. + Future _loadCall() async { + final d = await _channel.invokeMapMethod('getCallDetails'); + if (d == null) return; + setState(() { + _call = CometChatCallDetails( + sessionId: d['callId'] as String? ?? '', + callerUid: '', + callerName: d['callerName'] as String? ?? 'Unknown', + callerAvatar: d['callerAvatar'] as String?, + callType: (d['isVideo'] as bool? ?? false) + ? CometChatCallType.video + : CometChatCallType.audio, + receiverType: CometChatReceiverType.user, + conversationId: '', + isVideo: d['isVideo'] as bool? ?? false, + ); + }); + } -This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. + @override + Widget build(BuildContext context) { + final call = _call; + return MaterialApp( + debugShowCheckedModeBanner: false, + home: call == null + ? const SizedBox.shrink() + : CometChatDefaultCallScreen( + callDetails: call, + onAccept: () async { + await _channel.invokeMethod('cancelNotification', + {'callId': call.sessionId}); + // Bring your app forward / start the call session here. + await _channel.invokeMethod('finish', {'callId': call.sessionId}); + }, + onDecline: () async { + await _channel.invokeMethod('finish', {'callId': call.sessionId}); + }, + ), + ); + } +} +``` -### 6.2 Add the `app_badge_plus` dependency + +Adapt the accept path to launch your in-call screen. The channel methods (`getCallDetails`, `cancelNotification`, `finish`, and the incoming `reload`) are the contract the plugin's `IncomingCallActivity` exposes. + -```yaml lines -dependencies: - app_badge_plus: ^1.2.6 +### 4.3 Route call navigation once the app is running + +On your first screen after login (once the navigator is ready), let the plugin replay a cold-start call launch and route subsequent call taps: + +```dart lines +final navigatorKey = GlobalKey(); + +// After the widget tree builds: +CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); ``` -Run `flutter pub get`. +Pass the same `navigatorKey` to your `MaterialApp`. -### 6.3 Expected payload format +## 5. Request permission and register the FCM token -CometChat sends FCM data messages with this structure (relevant fields): +Request notification permission early, and register the FCM token **after** your CometChat user logs in (registration binds the token to the session): -```json -{ - "data": { - "unreadMessageCount": "5", - "title": "New Message", - "alert": "John: Hello!", - "conversationId": "user_abc123", - "conversationType": "user" - } -} +```dart lines +// Ask for POST_NOTIFICATIONS (Android 13+). +await CometChatPushNotifications.requestPermission(); + +// After CometChatUIKit.login(...) / CometChat.login(...) succeeds: +CometChatPushNotifications.registerToken( + PushPlatform.FCM_FLUTTER_ANDROID, + providerId: AppCredentials.fcmProviderId, + onSuccess: (token) => debugPrint('Registered FCM token: $token'), + onError: (e) => debugPrint('Registration failed: ${e.message}'), +); ``` -`unreadMessageCount` is a string representing the total unread messages across all conversations for the logged-in user. +Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers the new token with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` if you want to log it. -### 6.4 Update the app badge from the push payload +## 6. Notification taps and navigation -Inside your local notification handler (for example `LocalNotificationService.showNotification`), parse `unreadMessageCount` and update the badge: +When the app is running, subscribe to the tap stream to open the right conversation: ```dart lines -import 'package:app_badge_plus/app_badge_plus.dart'; +CometChatPushNotifications.onNotificationTap.listen((details) { + // details.conversationId, details.sender, details.receiverType, ... + // Navigate to your messages screen for this conversation. +}); +``` -// Inside showNotification, after receiving notificationData: -final unreadCountRaw = notificationData['unreadMessageCount']; -int unreadMessageCount = int.tryParse(unreadCountRaw?.toString() ?? '') ?? 0; +For calls, coordinate with your media logic through the call-event stream: -if (unreadMessageCount >= 0) { - try { - await AppBadgePlus.updateBadge(unreadMessageCount); - } catch (e) { - debugPrint('Error updating badge count: $e'); +```dart lines +CometChatPushNotifications.onCallEvent.listen((event) { + switch (event.type) { + case CometChatCallEventType.accepted: + // Start / join the call session, then setCallConnected(event.sessionId). + break; + case CometChatCallEventType.declined: + case CometChatCallEventType.ended: + case CometChatCallEventType.timeoutEnded: + // Tear down any call state. + break; + case CometChatCallEventType.incoming: + break; } -} +}); ``` -On Android, `app_badge_plus` uses launcher-specific APIs (Samsung, Huawei, etc.) to display a badge number on the app icon. Passing `0` clears the badge. +Useful call methods: `setCallConnected(sessionId)` (after media is established), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. -### 6.5 Show grouped notifications with unread count +## 7. OEM permissions for lock-screen calls -Use `conversationId.hashCode` as the notification ID so each new message from the same conversation replaces the previous notification instead of creating a new one. Accumulate message lines per conversation for inbox-style display: +To reliably show a full-screen call over the lock screen — especially on Android 14+ and OEM skins like MIUI/Redmi/POCO — check and request the relevant permissions: ```dart lines -// Stable notification ID per conversation -final int notificationId = conversationId.isNotEmpty - ? conversationId.hashCode - : DateTime.now().microsecondsSinceEpoch.hashCode; - -// Accumulate messages for inbox-style display -static final Map> _conversationMessages = {}; -_conversationMessages.putIfAbsent(conversationId, () => []); -_conversationMessages[conversationId]!.add(messageBody); - -final messages = _conversationMessages[conversationId]!; - -// Choose notification style based on message count -StyleInformation styleInformation; -if (messages.length == 1) { - styleInformation = const DefaultStyleInformation(false, false); -} else { - styleInformation = InboxStyleInformation( - messages, - contentTitle: title, - summaryText: unreadMessageCount > 0 - ? '$unreadMessageCount unread ${unreadMessageCount == 1 ? 'message' : 'messages'}' - : '${messages.length} messages', - ); -} +final perms = await CometChatPushNotifications.checkCallPermissions(); +// perms: { fullScreenIntent, overlay, batteryOptimized } -final androidDetails = AndroidNotificationDetails( - notificationChannelId, - notificationChannelName, - importance: Importance.max, - priority: Priority.high, - icon: 'ic_launcher', - styleInformation: styleInformation, - subText: unreadMessageCount > 0 - ? '$unreadMessageCount unread ${unreadMessageCount == 1 ? 'message' : 'messages'}' - : null, - number: messages.length > 1 ? messages.length : null, -); +if (perms['fullScreenIntent'] == false) { + await CometChatPushNotifications.openFullScreenIntentSettings(); +} +if (perms['overlay'] == false) { + await CometChatPushNotifications.openOverlaySettings(); // MIUI / Xiaomi +} +if (perms['batteryOptimized'] == true) { + await CometChatPushNotifications.openBatteryOptimizationSettings(); +} +// Xiaomi / Oppo / Vivo / Huawei / Samsung autostart: +await CometChatPushNotifications.openAutoStartSettings(); ``` -- `subText` shows the unread count below the notification title on most Android devices. -- `number` displays a count badge on the notification icon (Samsung One UI). -- When the user opens a conversation, clear its accumulated messages by removing the key from `_conversationMessages`. +On iOS and web these checks return "granted" and the open-settings calls are no-ops. -### 6.6 Clear badge and notifications when the app opens +## 8. Badge count -Clear the badge count and dismiss all local notifications when the app launches and every time it resumes from the background. Add `WidgetsBindingObserver` to your root widget and register/remove the observer in `initState()` and `dispose()`: +CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (the total unread across all conversations). The plugin exposes badge helpers directly — no extra dependency needed. -```dart lines -Future _clearBadge() async { - try { - LocalNotificationService.flutterLocalNotificationsPlugin.cancelAll(); - await AppBadgePlus.updateBadge(0); - debugPrint("The badge was cleared"); - } catch (e) { - debugPrint("Error in clearing the badge value $e"); - } -} +### 8.1 Enable unread badge count on the dashboard -@override -void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - _clearBadge(); -} +1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. +2. Enable the **Unread Badge Count** toggle. -@override -void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - _clearBadge(); - } -} +This includes `unreadMessageCount` in every push payload. -@override -void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); -} +### 8.2 Update and clear the badge + +Set the badge from the payload (for example inside a foreground handler), and clear it when the app opens: + +```dart lines +// From an incoming message payload: +final count = int.tryParse('${message.data['unreadMessageCount'] ?? ''}') ?? 0; +await CometChatPushNotifications.setBadgeCount(count); + +// On app launch / resume: +await CometChatPushNotifications.setBadgeCount(0); +await CometChatPushNotifications.clearAll(); // dismiss stale banners ``` -`cancelAll()` removes stale notification banners from the tray so they stay in sync with the badge count. +Add a `WidgetsBindingObserver` to your root widget and clear the badge in `initState` and on `AppLifecycleState.resumed`. -## 7. Testing checklist +## 9. 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. + - Foreground: no banner unless `showInAppNotifications: true` (and you're not already in that chat). + - Background: an FCM notification appears; tapping opens the right conversation via `onNotificationTap`. +3. Force-quit the app, send another message, tap the notification, and confirm it navigates to the conversation. +4. Trigger an incoming CometChat call and confirm: + - The full-screen call UI shows the caller with Accept/Decline, even on the lock screen. + - Accepting starts the call session and dismisses the notification. + - Declining from the notification rejects the call server-side (via your `onCallDeclined`). +5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. -## 8. Troubleshooting tips +## 10. Troubleshooting | 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). | +| No notifications received | Confirm `google-services.json` is in `android/app/`, the package name matches Firebase, notification permission is granted (Android 13+), and your `firebase_messaging` handlers call `handlePushNotification(message.data)`. | +| Build fails after adding the plugin | Ensure Kotlin is **2.0+** in `android/settings.gradle.kts` and `minSdk = 24`. | +| Token registration errors | Verify `providerId` matches the FCM provider ID exactly and that `registerToken` runs **after** login. | +| Full-screen call UI not showing | Check `checkCallPermissions()` and request full-screen-intent / overlay / battery / autostart via the helpers in section 7. | +| Lock-screen call is a black screen | Make sure `incomingCallMain` is defined in `main.dart` and annotated with `@pragma('vm:entry-point')`. | +| Tapping a call from a killed app does nothing | Call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready, and pass the same key to `MaterialApp`. | diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx index 2e020f564..5a16c1b80 100644 --- a/notifications/flutter-push-notifications-ios.mdx +++ b/notifications/flutter-push-notifications-ios.mdx @@ -1,37 +1,56 @@ --- title: "Flutter Push Notifications (iOS)" -description: "CometChat push notifications in Flutter apps on iOS using Apple Push Notification service (APNs)." +description: "Add CometChat push notifications and VoIP calls to a Flutter iOS app with the drop-in cometchat_push_notifications SDK, APNs, and PushKit." --- - - Reference implementation of Flutter UI Kit, APNs and Push Notification Setup. - + + + The drop-in push & VoIP plugin on pub.dev. + + + Source, changelog, and example app. + + ## 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. -- App icon badge count using `unreadMessageCount` from the CometChat push payload. +- CometChat dashboard setup (enable push, add APNs + APNs VoIP providers) with screenshots. +- Apple setup (APNs `.p8` key, entitlements, capabilities). +- Adding the `cometchat_push_notifications` plugin and initializing it. +- Requesting permission and registering the APNs and VoIP tokens after login. +- Handling notification taps, incoming calls via PushKit/CallKit, and badge counts. +- Testing and troubleshooting. -{/* ## What you need first + +The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `CometChatPushRegistry`, `flutter_callkit_incoming`, and a custom native `AppDelegate` PushKit/CallKit bridge. The plugin (via [`notification_voip_plugin`](https://pub.dev/packages/notification_voip_plugin)) handles PushKit registration and CallKit reporting natively — your `AppDelegate` stays minimal. + -- 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 APNs + CometChat + the SDK work together -## How FCM + CometChat work together +- **APNs is primary on iOS:** Apple issues the device (APNs) token and, via PushKit, the VoIP token. APNs delivers chat pushes; PushKit delivers call pushes. +- **CometChat's role:** The APNs and APNs VoIP providers you create in the dashboard hold your Apple credentials. When you register the tokens after login, CometChat binds them to the logged-in user and sends pushes on your behalf. +- **The plugin's role:** `cometchat_push_notifications` retrieves the APNs and VoIP tokens, registers them with CometChat, and drives the incoming-call experience through CallKit. `cometchat_sdk` and `notification_voip_plugin` are resolved for you. -- **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`. +**Flow:** Permission prompt → APNs + VoIP tokens issued → after your CometChat user logs in, register both tokens with `registerToken` → CometChat sends to APNs/PushKit → APNs delivers the chat alert (iOS displays it) and PushKit delivers the call (the plugin presents CallKit) → taps and call actions surface on the plugin's streams. + + +On iOS you don't need Firebase. This guide uses **pure APNs + PushKit**. If you specifically want to register FCM tokens on iOS, add an FCM provider and `firebase_messaging` as well, but that's optional. + + +## Prerequisites + +- Flutter 3.3.0+ / Dart 3.10.8+; iOS 13.0+ (set the Podfile platform to **iOS 14.0** for VoIP/CallKit). +- An Apple Developer account with Push Notifications and Background Modes (Remote notifications + Voice over IP) enabled for your bundle ID, and an APNs Auth Key (`.p8`). +- CometChat app credentials (App ID, Region, Auth Key) with **Push Notifications** enabled and an **APNs provider** (plus an **APNs VoIP** provider for calls). +- A physical iPhone or iPad — simulators cannot receive VoIP pushes or present CallKit UI. ## 1. Enable push and add providers (CometChat Dashboard) @@ -41,327 +60,265 @@ description: "CometChat push notifications in Flutter apps on iOS using Apple Pu 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. +2. Click **Add Credentials**, choose **APNs** (and **APNs VoIP** for in-call pushes), upload your `.p8` key, and copy each **Provider ID**. Upload APNs credentials -3. (Optional) Add an **FCM** provider if you plan to register FCM tokens on iOS. - - - Upload FCM service account JSON - - -Keep the provider IDs—you’ll set them in `CometChatConfig`. - -## 2. Prepare Apple + Firebase credentials +Keep both provider IDs — you'll pass them to `registerToken`. -### 2.1 Apple Developer portal +## 2. Apple Developer setup 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. Enable **Push Notifications** plus **Background Modes → Remote notifications** and **Voice over IP** on the bundle ID. -**`.p12` certificates are deprecated.** Apple recommends using `.p8` Auth Keys for push notifications. `.p8` keys never expire and work across all your apps. Migrate to `.p8` if you haven't already. +**`.p12` certificates are deprecated.** Apple recommends `.p8` Auth Keys — they never expire and work across all your apps. Migrate to `.p8` if you haven't already. -### 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*. - - - Firebase - Push Notifications - - -## 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) (or your own config file) so it exposes: +Store your credentials and provider IDs somewhere your app can read them: ```dart lines -class CometChatConfig { +class AppCredentials { static const appId = "YOUR_APP_ID"; - static const region = "YOUR_APP_REGION"; + static const region = "YOUR_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 + static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; } ``` -## 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)** +## 3. Add the plugin and configure iOS -- 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. +### 3.1 Dependencies -### 4.3 Align dependencies and configuration - -Mirror the sample `pubspec.yaml` versions (update as needed when newer releases ship): +Add the plugin to `pubspec.yaml`: ```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 + cometchat_push_notifications: ^1.0.1 ``` -Run `flutter pub get`, then install the native pods: +`cometchat_sdk` and `notification_voip_plugin` are pulled in transitively. Then: ```bash +flutter pub get cd ios && pod install && cd .. ``` -Then run `flutterfire configure` if you still need to generate `firebase_options.dart`. +### 3.2 Podfile + +Set the deployment target in `ios/Podfile`: -## 5. Configure the native iOS layer +```ruby +platform :ios, '14.0' +``` -### 5.1 Capabilities and Info.plist +### 3.3 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. +2. Under **Signing & Capabilities**, add **Push Notifications** and **Background Modes** → check **Remote notifications** and **Voice over IP**. +3. Set the development team that owns the APNs/VoIP key. Enable Push Notifications and Background Modes for APNs -### 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 -) +Add the background modes and permission strings to `ios/Runner/Info.plist`: + +```xml lines +UIBackgroundModes + + voip + remote-notification + +NSMicrophoneUsageDescription +Microphone access is required for calls. +NSCameraUsageDescription +Camera access is required for video calls. ``` -Handle at least `getAppInfo`, `endCall`, `onCallAcceptedFromNative`, and `onCallEndedFromNative`, mirroring the Dart side (`APNSService.setupNativeCallListener`). +### 3.4 AppDelegate — forward the APNs token -- **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. +The plugin handles PushKit and CallKit natively, so no PushKit or CallKit code is needed in your `AppDelegate`. Forward the APNs device token to the underlying plugin so it can be registered: -If you change the MethodChannel name in Swift, remember to update `APNSService.platform` in Dart to match. +```swift lines +import Flutter +import UIKit +import notification_voip_plugin + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } -## 6. Token registration and runtime events + override func application( + _ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data + ) { + NotificationVoipPlugin.shared?.setAPNsToken(deviceToken) + } +} +``` -### 6.1 Standard APNs tokens +## 4. Initialize the SDK -`APNSService` hooks into `FirebaseMessaging.instance.getAPNSToken()` (and `onTokenRefresh`) before calling: +In `main.dart`, initialize the plugin before `runApp`. Provide a call screen and callbacks as needed: ```dart lines -await CometChatPushRegistry.register( - token: token, - isFcm: false, - isVoip: false, -); +import 'package:flutter/material.dart'; +import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + onCallAccepted: (call) { + // Start your CometChat call session, then: + // CometChatPushNotifications.setCallConnected(call.sessionId); + }, + onCallDeclined: (call) { + // Reject the call server-side via the CometChat SDK. + }, + ), + ); + + runApp(const MyApp()); +} ``` -This registers the device token against the APNs provider selected in `CometChatConfig.apnProviderId`. - -### 6.2 VoIP tokens +The `CometChatNotificationConfig` also accepts: -- 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);`. +| Field | Default | Purpose | +| --- | --- | --- | +| `customCallScreenBuilder` | `null` | Return your own incoming-call widget. When set, you own accept/reject, dismissing the call notification, and popping the screen; `onCallAccepted`/`onCallDeclined` are **not** called. | +| `onCallAccepted` / `onCallDeclined` | `null` | Callbacks for the built-in default call screen. | +| `callActionHandler` | default | Subclass `CometChatCallActionHandler` to intercept mute/speaker/camera. | +| `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | +| `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | -### 6.3 Local notifications and navigation +Once the navigator is ready on your first screen after login, replay any cold-start call launch and route subsequent call taps: -- `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. +```dart lines +final navigatorKey = GlobalKey(); - -**Cold-start VoIP call handling:** When the app is killed (not running) and a VoIP push arrives, the standard CometChat call listeners (`onOutgoingCallAccepted`, `onIncomingCallReceived`, etc.) will **not** fire because the Flutter engine is not yet initialized. In this scenario, you need a **native-to-Flutter bridge** that: +// After the widget tree builds: +CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); +``` -1. Handles the VoIP push entirely in native Swift code (`AppDelegate` / `PushKit` handler). -2. Presents the CallKit UI natively. -3. When the user accepts, initializes the CometChat SDK from the native side using `CometChat.generateToken()` and `CometChat.startSession()`. -4. Bridges the call state back to Flutter via a MethodChannel once the Flutter engine is ready. +Pass the same `navigatorKey` to your `MaterialApp`. -The standard `CometChatCallingExtension` call listeners only work when the app is already running (foreground or background with an active Flutter engine). See the [sample app's `AppDelegate.swift`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/ios/Runner/AppDelegate.swift) for a reference implementation of this native bridge pattern. - +## 5. Request permission and register tokens -## 7. Badge count using `unreadMessageCount` +Request permission early, and register **both** the APNs device token and the VoIP token **after** your CometChat user logs in. Each call includes both tokens in the server request, so register them with their matching provider IDs: -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field representing the total unread messages across all conversations for the logged-in user. On iOS the badge can be updated from both the client side and the server side. +```dart lines +await CometChatPushNotifications.requestPermission(); -If you use only APNs (no FCM on iOS), the badge is handled entirely by the server. CometChat sets the `aps.badge` value in the push payload, and iOS updates the app icon badge automatically when the notification is delivered. No additional dependency or client-side code is required — you can skip straight to section 7.5 to handle clearing the badge when the app opens. +// After CometChatUIKit.login(...) / CometChat.login(...) succeeds: -### 7.1 Enable unread badge count on the CometChat Dashboard +// APNs device token (chat/alert pushes) +CometChatPushNotifications.registerToken( + PushPlatform.APNS_FLUTTER_DEVICE, + providerId: AppCredentials.apnProviderId, + onSuccess: (token) => debugPrint('APNs registered: $token'), + onError: (e) => debugPrint('APNs failed: ${e.message}'), +); -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Scroll to the bottom and enable the **Unread Badge Count** toggle. - -This ensures CometChat includes the `unreadMessageCount` field in every push payload (and sets `aps.badge` for APNs) sent to your app. - -### 7.2 Expected payload format - -CometChat sends APNs payloads with this structure (relevant fields): - -```json -{ - "aps": { - "alert": { - "title": "New Message", - "body": "John: Hello!" - }, - "badge": 5, - "sound": "default" - }, - "unreadMessageCount": 5, - "conversationId": "user_abc123" -} +// VoIP (PushKit) token — required for calls +CometChatPushNotifications.registerToken( + PushPlatform.APNS_FLUTTER_VOIP, + providerId: AppCredentials.apnVoipProviderId, + onSuccess: (token) => debugPrint('VoIP registered: $token'), + onError: (e) => debugPrint('VoIP failed: ${e.message}'), +); ``` -The `aps.badge` field is set by CometChat server-side. iOS updates the app icon badge automatically when the push is delivered — no client code is needed for this path. - -### 7.3 Client-side badge via FCM on iOS +Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` to log it. -The steps below apply only if you register FCM tokens on iOS and show local notifications from Flutter. If you use pure APNs, skip to section 7.3. +## 6. Notification taps and call events -Add the `app_badge_plus` dependency: +Subscribe to the streams to navigate and coordinate call state: -```yaml lines -dependencies: - app_badge_plus: ^1.2.6 +```dart lines +CometChatPushNotifications.onNotificationTap.listen((details) { + // details.conversationId, details.sender, details.receiverType, ... + // Navigate to your messages screen for this conversation. +}); + +CometChatPushNotifications.onCallEvent.listen((event) { + switch (event.type) { + case CometChatCallEventType.accepted: + // Start / join the call, then setCallConnected(event.sessionId). + break; + case CometChatCallEventType.declined: + case CometChatCallEventType.ended: + case CometChatCallEventType.timeoutEnded: + // Tear down any call state. + break; + case CometChatCallEventType.incoming: + break; + } +}); ``` -Run `flutter pub get`, then install the native pods: - -```bash -cd ios && pod install && cd .. -``` +Useful call methods: `setCallConnected(sessionId)` (after media is established — critical for connecting calls from a terminated state), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. -### 7.4 Update the badge from local notifications + +**Cold-start VoIP handling:** when the app is killed and a VoIP push arrives, the plugin presents CallKit natively via PushKit before the Flutter engine is ready. When the user answers, `handleIncomingCallLaunch` routes to your call screen and the `onCallEvent` / `onCallAccepted` handlers fire — set up your CometChat call session there and call `setCallConnected`. + -Inside your notification handler (for example `APNSService._showNotification`), parse `unreadMessageCount` and set the badge: +## 7. Badge count -```dart lines -import 'package:app_badge_plus/app_badge_plus.dart'; +CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field. On iOS, with pure APNs the badge is handled **server-side**: CometChat sets `aps.badge` in the payload and iOS updates the app icon automatically — no client code required to display it. -final unreadCountRaw = notificationData['unreadMessageCount']; -int unreadMessageCount = int.tryParse(unreadCountRaw?.toString() ?? '') ?? 0; +### 7.1 Enable unread badge count on the dashboard -if (unreadMessageCount >= 0) { - try { - await AppBadgePlus.updateBadge(unreadMessageCount); - } catch (e) { - debugPrint('Error updating badge count: $e'); - } -} -``` +1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. +2. Enable the **Unread Badge Count** toggle. -On iOS, `app_badge_plus` sets the native `UIApplication.shared.applicationIconBadgeNumber`. Passing `0` clears the badge. +This sets `aps.badge` on every push. -### 7.5 Clear badge and notifications when the app opens +### 7.2 Clear the badge when the app opens -This is required regardless of whether you use APNs or FCM. Clear the badge count and dismiss all local notifications when the app launches and every time it resumes from the background. Add `WidgetsBindingObserver` to your root widget and register/remove the observer in `initState()` and `dispose()`: +Clear the badge and dismiss stale notifications on launch and every resume: ```dart lines -Future _clearBadge() async { - try { - LocalNotificationService.flutterLocalNotificationsPlugin.cancelAll(); - await AppBadgePlus.updateBadge(0); - debugPrint("The badge was cleared"); - } catch (e) { - debugPrint("Error in clearing the badge value $e"); - } -} - -@override -void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - _clearBadge(); -} - -@override -void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - _clearBadge(); - } -} - -@override -void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); -} +await CometChatPushNotifications.setBadgeCount(0); +await CometChatPushNotifications.clearAll(); ``` -`cancelAll()` removes stale notification banners from the tray so they stay in sync with the badge count. +Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and on `AppLifecycleState.resumed`. If you also register FCM tokens on iOS and show local notifications yourself, use `setBadgeCount(count)` with the payload's `unreadMessageCount` instead of relying on the server value. ## 8. Testing checklist -1. Run the app on a physical device in debug first. Grant notification, microphone, camera, and Bluetooth permissions when prompted. +1. Run on a physical device in debug. Grant notification, microphone, and camera 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. + - Foreground: no banner unless `showInAppNotifications: true` (and you're not already in that chat). + - Background: an APNs alert appears; tapping opens the right conversation via `onNotificationTap`. +3. Force-quit the app, send another message, tap the notification, and confirm it navigates to the conversation. +4. Trigger an incoming CometChat call and confirm: + - CallKit shows the caller name, call type, and Accept/Decline. + - Accepting starts the call session (via `onCallAccepted`) and dismisses the CallKit UI when the call ends. + - Declining cleans up both CallKit and your call state. +5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. -## 9. Troubleshooting tips +## 9. Troubleshooting | 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 - - - - */} +| No VoIP pushes | Ensure Push Notifications + Background Modes (Voice over IP) are enabled, the bundle ID matches the CometChat APNs VoIP provider, and you registered `PushPlatform.APNS_FLUTTER_VOIP`. | +| APNs token never registers | Confirm the `AppDelegate` forwards the token via `NotificationVoipPlugin.shared?.setAPNsToken(deviceToken)` and that permission was granted. | +| Token registration errors | Verify `apnProviderId` / `apnVoipProviderId` match the dashboard exactly and that `registerToken` runs **after** login. | +| CallKit UI never dismisses | Call `endCall(sessionId)` (or `endAllCalls()`) when your call session ends so the plugin reports it to CallKit. | +| Notification taps ignored | Subscribe to `onNotificationTap`, and call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready with the same key passed to `MaterialApp`. | +| Badge not updating | Enable the **Unread Badge Count** toggle on the dashboard; with pure APNs the badge is set server-side via `aps.badge`. | From d3364a246d896a0254d78c0ead284828d35f8828 Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Mon, 27 Jul 2026 18:44:03 +0530 Subject: [PATCH 02/11] Updated Push Notifications section --- notifications/android-push-notifications.mdx | 62 +++----- .../flutter-push-notifications-android.mdx | 68 +++------ .../flutter-push-notifications-ios.mdx | 63 +++----- notifications/ios-apns-push-notifications.mdx | 66 ++------- notifications/ios-fcm-push-notifications.mdx | 84 ++--------- notifications/push-overview.mdx | 72 ++++++++- ...eact-native-push-notifications-android.mdx | 88 +++++------ .../react-native-push-notifications-ios.mdx | 140 ++++-------------- notifications/web-push-notifications.mdx | 39 ++--- 9 files changed, 240 insertions(+), 442 deletions(-) diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index 017536b07..67f1c6871 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -35,31 +35,11 @@ description: "Setup FCM and CometChat for message and call push notifications on - **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 + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. + -1. **Firebase Console**: Add your Android package, download `google-services.json` into the app module, and enable Cloud Messaging. - - - Firebase - Push Notifications - - -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. - - - Enable Push Notifications - - -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: - - - Upload FCM service account JSON - - -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. - -## 2. Add dependencies (Gradle) +## 1. Add dependencies (Gradle) 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. @@ -150,7 +130,7 @@ dependencies { - 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 +## 2. 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): @@ -198,7 +178,7 @@ Start from the sample [`AndroidManifest.xml`](https://github.com/cometchat/comet - 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). -## 4. Application wiring, sample code, and callbacks +## 3. Application wiring, sample code, and callbacks - 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): @@ -415,7 +395,7 @@ State to set at runtime: - `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. -## 5. Application wiring and permissions +## 4. Application wiring and permissions - [`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: @@ -495,7 +475,7 @@ private fun handleDeepLinking() { 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. -## 6. Register the FCM token after login +## 5. Register the FCM token after login Call registration right after `CometChatUIKit.login()` succeeds: @@ -541,7 +521,7 @@ override fun onNewToken(token: String) { Ensures a rotated FCM token is re-bound to the logged-in user; without this, pushes will stop after Firebase refreshes the token. -## 7. Unregister the token on logout +## 6. Unregister the token on logout ```kotlin lines CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener() { @@ -551,18 +531,18 @@ CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener `ShortcutBadger` uses launcher-specific APIs (Samsung, Huawei, Xiaomi, etc.) to display a badge number on the app icon. Passing `0` clears the badge. -### 8.5 Show unread count in the notification +### 7.5 Show unread count in the notification Update your notification builder to display the unread count in the notification itself: @@ -623,7 +603,7 @@ mNotificationBuilder.setSubText("$count unread messages") - `setNumber(count)` displays a count badge on the notification icon. - `setSubText()` shows the unread count below the notification title. -### 8.6 Clear badge when the app opens +### 7.6 Clear badge when the app opens Clear the badge count when the app launches and every time it resumes from the background. Override `onResume()` in your main activity: @@ -665,14 +645,14 @@ Payload keys delivered to `onMessageReceived` (adapted from the shared push inte Use `message` for deep links (`CometChatHelper.processMessage`) and `type/callAction` to branch chat vs call flows. */} -## 9. Handle message pushes +## 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. -## 10. Handle call pushes (ConnectionService) +## 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. @@ -681,7 +661,7 @@ Use `message` for deep links (`CometChatHelper.processMessage`) and `type/callAc - 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. -## 11. Customize notification text or parse payloads +## 10. Customize notification text or parse payloads Parse the push into a `BaseMessage` for deep links: @@ -704,9 +684,9 @@ CometChat.sendCustomMessage(customMessage, object : CallbackListener - 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**. - - - Upload FCM service account JSON - - -Keep the provider ID — you'll pass it to `registerToken`. - -## 2. Prepare Firebase and credentials - -1. In the Firebase Console, 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. + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. + - - Firebase - Push Notifications - +## 1. Store your credentials -Store your CometChat and provider values somewhere your app can read them (for example a small `AppCredentials` class): +Keep your CometChat App ID, Region, and FCM **Provider ID** (from the shared setup) somewhere your app can read them — for example a small `AppCredentials` class: ```dart lines class AppCredentials { @@ -86,9 +66,9 @@ class AppCredentials { } ``` -## 3. Add the plugin and configure Gradle +## 2. Add the plugin and configure Gradle -### 3.1 Dependencies +### 2.1 Dependencies Add the plugin plus Firebase Messaging (used to receive the FCM data messages) to `pubspec.yaml`: @@ -99,7 +79,7 @@ dependencies: firebase_messaging: ^15.1.6 ``` -`cometchat_sdk` and `notification_voip_plugin` are pulled in transitively. Run: +`cometchat_sdk` is pulled in transitively. Run: ```bash flutter pub get @@ -107,7 +87,7 @@ flutter pub get Then run `flutterfire configure` if you still need to generate `firebase_options.dart`. -### 3.2 Gradle + Kotlin +### 2.2 Gradle + Kotlin 1. Add `google-services.json` to `android/app/`. 2. Apply the Google Services plugin and ensure Kotlin is **2.0+** in `android/settings.gradle.kts`: @@ -138,9 +118,9 @@ plugins { You do **not** need to add notification, call, full-screen-intent, or lock-screen permissions to your `AndroidManifest.xml`. The plugin declares everything it needs (including the lock-screen `IncomingCallActivity` and the Decline broadcast receiver), and Gradle merges them into your app automatically. -## 4. Initialize the SDK +## 3. Initialize the SDK -### 4.1 `main.dart` +### 3.1 `main.dart` Initialize Firebase and the plugin before `runApp`, register a background message handler, and expose the `incomingCallMain` entrypoint that the plugin's lock-screen call activity runs. @@ -204,7 +184,7 @@ The `CometChatNotificationConfig` also accepts: | `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | | `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | -### 4.2 The `incomingCallMain` entrypoint +### 3.2 The `incomingCallMain` entrypoint When a call arrives while the device is locked, the plugin launches a dedicated full-screen activity that runs a separate Dart entrypoint named `incomingCallMain`. Define it in `main.dart`. It talks to the plugin's native activity over the `cometchat_locked_call` method channel: @@ -284,7 +264,7 @@ class _LockedCallAppState extends State<_LockedCallApp> { Adapt the accept path to launch your in-call screen. The channel methods (`getCallDetails`, `cancelNotification`, `finish`, and the incoming `reload`) are the contract the plugin's `IncomingCallActivity` exposes. -### 4.3 Route call navigation once the app is running +### 3.3 Route call navigation once the app is running On your first screen after login (once the navigator is ready), let the plugin replay a cold-start call launch and route subsequent call taps: @@ -297,7 +277,7 @@ CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); Pass the same `navigatorKey` to your `MaterialApp`. -## 5. Request permission and register the FCM token +## 4. Request permission and register the FCM token Request notification permission early, and register the FCM token **after** your CometChat user logs in (registration binds the token to the session): @@ -316,7 +296,7 @@ CometChatPushNotifications.registerToken( Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers the new token with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` if you want to log it. -## 6. Notification taps and navigation +## 5. Notification taps and navigation When the app is running, subscribe to the tap stream to open the right conversation: @@ -348,7 +328,7 @@ CometChatPushNotifications.onCallEvent.listen((event) { Useful call methods: `setCallConnected(sessionId)` (after media is established), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. -## 7. OEM permissions for lock-screen calls +## 6. OEM permissions for lock-screen calls To reliably show a full-screen call over the lock screen — especially on Android 14+ and OEM skins like MIUI/Redmi/POCO — check and request the relevant permissions: @@ -371,18 +351,18 @@ await CometChatPushNotifications.openAutoStartSettings(); On iOS and web these checks return "granted" and the open-settings calls are no-ops. -## 8. Badge count +## 7. Badge count CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (the total unread across all conversations). The plugin exposes badge helpers directly — no extra dependency needed. -### 8.1 Enable unread badge count on the dashboard +### 7.1 Enable unread badge count on the dashboard 1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. 2. Enable the **Unread Badge Count** toggle. This includes `unreadMessageCount` in every push payload. -### 8.2 Update and clear the badge +### 7.2 Update and clear the badge Set the badge from the payload (for example inside a foreground handler), and clear it when the app opens: @@ -398,7 +378,7 @@ await CometChatPushNotifications.clearAll(); // dismiss stale banners Add a `WidgetsBindingObserver` to your root widget and clear the badge in `initState` and on `AppLifecycleState.resumed`. -## 9. Testing checklist +## 8. 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: @@ -411,7 +391,7 @@ Add a `WidgetsBindingObserver` to your root widget and clear the badge in `initS - Declining from the notification rejects the call server-side (via your `onCallDeclined`). 5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. -## 10. Troubleshooting +## 9. Troubleshooting | Symptom | Quick checks | | --- | --- | diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx index 5a16c1b80..c2f6ecba7 100644 --- a/notifications/flutter-push-notifications-ios.mdx +++ b/notifications/flutter-push-notifications-ios.mdx @@ -22,22 +22,20 @@ description: "Add CometChat push notifications and VoIP calls to a Flutter iOS a ## What this guide covers -- CometChat dashboard setup (enable push, add APNs + APNs VoIP providers) with screenshots. -- Apple setup (APNs `.p8` key, entitlements, capabilities). - Adding the `cometchat_push_notifications` plugin and initializing it. - Requesting permission and registering the APNs and VoIP tokens after login. - Handling notification taps, incoming calls via PushKit/CallKit, and badge counts. - Testing and troubleshooting. -The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `CometChatPushRegistry`, `flutter_callkit_incoming`, and a custom native `AppDelegate` PushKit/CallKit bridge. The plugin (via [`notification_voip_plugin`](https://pub.dev/packages/notification_voip_plugin)) handles PushKit registration and CallKit reporting natively — your `AppDelegate` stays minimal. +The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `CometChatPushRegistry`, `flutter_callkit_incoming`, and a custom native `AppDelegate` PushKit/CallKit bridge. The plugin handles PushKit registration and CallKit reporting natively — your `AppDelegate` stays minimal. ## How APNs + CometChat + the SDK work together - **APNs is primary on iOS:** Apple issues the device (APNs) token and, via PushKit, the VoIP token. APNs delivers chat pushes; PushKit delivers call pushes. - **CometChat's role:** The APNs and APNs VoIP providers you create in the dashboard hold your Apple credentials. When you register the tokens after login, CometChat binds them to the logged-in user and sends pushes on your behalf. -- **The plugin's role:** `cometchat_push_notifications` retrieves the APNs and VoIP tokens, registers them with CometChat, and drives the incoming-call experience through CallKit. `cometchat_sdk` and `notification_voip_plugin` are resolved for you. +- **The plugin's role:** `cometchat_push_notifications` retrieves the APNs and VoIP tokens, registers them with CometChat, and drives the incoming-call experience through CallKit. `cometchat_sdk` is resolved for you. **Flow:** Permission prompt → APNs + VoIP tokens issued → after your CometChat user logs in, register both tokens with `registerToken` → CometChat sends to APNs/PushKit → APNs delivers the chat alert (iOS displays it) and PushKit delivers the call (the plugin presents CallKit) → taps and call actions surface on the plugin's streams. @@ -52,32 +50,13 @@ On iOS you don't need Firebase. This guide uses **pure APNs + PushKit**. If you - CometChat app credentials (App ID, Region, Auth Key) with **Push Notifications** enabled and an **APNs provider** (plus an **APNs VoIP** provider for calls). - A physical iPhone or iPad — simulators cannot receive VoIP pushes or present CallKit UI. -## 1. Enable push and add providers (CometChat Dashboard) - -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -2. Click **Add Credentials**, choose **APNs** (and **APNs VoIP** for in-call pushes), upload your `.p8` key, and copy each **Provider ID**. - - - Upload APNs credentials - - -Keep both provider IDs — you'll pass them to `registerToken`. - -## 2. Apple Developer setup - -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. + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. + - -**`.p12` certificates are deprecated.** Apple recommends `.p8` Auth Keys — they never expire and work across all your apps. Migrate to `.p8` if you haven't already. - +## 1. Store your credentials -Store your credentials and provider IDs somewhere your app can read them: +Keep your CometChat App ID, Region, and the **APNs** / **APNs VoIP** Provider IDs (from the shared setup) somewhere your app can read them: ```dart lines class AppCredentials { @@ -89,9 +68,9 @@ class AppCredentials { } ``` -## 3. Add the plugin and configure iOS +## 2. Add the plugin and configure iOS -### 3.1 Dependencies +### 2.1 Dependencies Add the plugin to `pubspec.yaml`: @@ -100,14 +79,14 @@ dependencies: cometchat_push_notifications: ^1.0.1 ``` -`cometchat_sdk` and `notification_voip_plugin` are pulled in transitively. Then: +`cometchat_sdk` is pulled in transitively. Then: ```bash flutter pub get cd ios && pod install && cd .. ``` -### 3.2 Podfile +### 2.2 Podfile Set the deployment target in `ios/Podfile`: @@ -115,7 +94,7 @@ Set the deployment target in `ios/Podfile`: platform :ios, '14.0' ``` -### 3.3 Capabilities and Info.plist +### 2.3 Capabilities and Info.plist 1. Open `ios/Runner.xcworkspace` in Xcode. 2. Under **Signing & Capabilities**, add **Push Notifications** and **Background Modes** → check **Remote notifications** and **Voice over IP**. @@ -139,7 +118,7 @@ Add the background modes and permission strings to `ios/Runner/Info.plist`: Camera access is required for video calls. ``` -### 3.4 AppDelegate — forward the APNs token +### 2.4 AppDelegate — forward the APNs token The plugin handles PushKit and CallKit natively, so no PushKit or CallKit code is needed in your `AppDelegate`. Forward the APNs device token to the underlying plugin so it can be registered: @@ -167,7 +146,7 @@ import notification_voip_plugin } ``` -## 4. Initialize the SDK +## 3. Initialize the SDK In `main.dart`, initialize the plugin before `runApp`. Provide a call screen and callbacks as needed: @@ -217,7 +196,7 @@ CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); Pass the same `navigatorKey` to your `MaterialApp`. -## 5. Request permission and register tokens +## 4. Request permission and register tokens Request permission early, and register **both** the APNs device token and the VoIP token **after** your CometChat user logs in. Each call includes both tokens in the server request, so register them with their matching provider IDs: @@ -245,7 +224,7 @@ CometChatPushNotifications.registerToken( Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` to log it. -## 6. Notification taps and call events +## 5. Notification taps and call events Subscribe to the streams to navigate and coordinate call state: @@ -277,18 +256,18 @@ Useful call methods: `setCallConnected(sessionId)` (after media is established **Cold-start VoIP handling:** when the app is killed and a VoIP push arrives, the plugin presents CallKit natively via PushKit before the Flutter engine is ready. When the user answers, `handleIncomingCallLaunch` routes to your call screen and the `onCallEvent` / `onCallAccepted` handlers fire — set up your CometChat call session there and call `setCallConnected`. -## 7. Badge count +## 6. Badge count CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field. On iOS, with pure APNs the badge is handled **server-side**: CometChat sets `aps.badge` in the payload and iOS updates the app icon automatically — no client code required to display it. -### 7.1 Enable unread badge count on the dashboard +### 6.1 Enable unread badge count on the dashboard 1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. 2. Enable the **Unread Badge Count** toggle. This sets `aps.badge` on every push. -### 7.2 Clear the badge when the app opens +### 6.2 Clear the badge when the app opens Clear the badge and dismiss stale notifications on launch and every resume: @@ -299,7 +278,7 @@ await CometChatPushNotifications.clearAll(); Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and on `AppLifecycleState.resumed`. If you also register FCM tokens on iOS and show local notifications yourself, use `setBadgeCount(count)` with the payload's `unreadMessageCount` instead of relying on the server value. -## 8. Testing checklist +## 7. Testing checklist 1. Run on a physical device in debug. Grant notification, microphone, and camera permissions when prompted. 2. Send a message from another user: @@ -312,7 +291,7 @@ Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and - Declining cleans up both CallKit and your call state. 5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. -## 9. Troubleshooting +## 8. Troubleshooting | Symptom | Quick checks | | --- | --- | diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx index 509dde513..8e8fa1919 100644 --- a/notifications/ios-apns-push-notifications.mdx +++ b/notifications/ios-apns-push-notifications.mdx @@ -13,7 +13,7 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in ## What this guide covers -- CometChat dashboard setup (enable push, add APNs Device + APNs VoIP providers) with screenshots. +- Prerequisite: the shared dashboard + Apple/APNs setup in the [overview](/notifications/push-overview#shared-setup). - APNs + PushKit/CallKit wiring (tokens, delegates, CallKit). - Incoming message/call handling and deep links. - Badge count and grouped notifications. @@ -31,47 +31,11 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in - **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) + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. + -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -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. - - -**`.p12` certificates are deprecated.** Apple recommends using `.p8` Auth Keys for push notifications. `.p8` keys are simpler to manage (one key works for all your apps), never expire, and are the only format actively supported going forward. If you are still using `.p12`, migrate to `.p8` at your earliest convenience. - - - - Add APNs credentials - - -Keep the provider IDs—you’ll paste them into your app constants. - -## 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. - - - Enable Push Notifications and Background Modes for APNs - - -## 3. Wiring APNs + PushKit/CallKit +## 1. Wiring APNs + PushKit/CallKit - 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. @@ -1131,7 +1095,7 @@ UserDefaults.standard.set(REGION, forKey: "region") -## 4. Register APNs device + VoIP tokens with CometChat +## 2. 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. @@ -1186,7 +1150,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } ``` -## 5. Unregister the token on logout +## 3. Unregister the token on logout Before logging the user out, unregister the push token so the device stops receiving notifications for that user. @@ -1208,18 +1172,18 @@ CometChatNotifications.unregisterPushToken( Always call `CometChatNotifications.unregisterPushToken()` **before** `CometChatUIKit.logout()`. If you skip this step, the device may continue to receive pushes for the logged-out user. -## 6. Badge count +## 4. Badge count CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. You can use this to set the app icon badge. -### 6.1 Enable unread badge count on the CometChat Dashboard +### 4.1 Enable unread badge count on the CometChat Dashboard 1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. 2. Scroll to the bottom and enable the **Unread Badge Count** toggle. This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. -### 6.2 Expected payload format +### 4.2 Expected payload format CometChat sends APNs payloads with this structure (relevant fields): @@ -1235,7 +1199,7 @@ CometChat sends APNs payloads with this structure (relevant fields): `unreadMessageCount` is a string representing the total unread messages across all conversations for the logged-in user. -### 6.3 Update the app badge from the push payload +### 4.3 Update the app badge from the push payload Inside your `UNUserNotificationCenterDelegate` method (for example `willPresent` or a Notification Service Extension), parse `unreadMessageCount` and update the badge: @@ -1255,7 +1219,7 @@ if let unreadCountStr = userInfo["unreadMessageCount"] as? String, Setting `applicationIconBadgeNumber` to `0` clears the badge. -### 6.4 Clear badge when the app opens +### 4.4 Clear badge when the app opens Clear the badge count when the app launches and every time it returns to the foreground. In your `SceneDelegate` or `AppDelegate`: @@ -1267,7 +1231,7 @@ func sceneDidBecomeActive(_ scene: UIScene) { This keeps the badge in sync with the actual unread state. -## 7. Navigation from notifications +## 5. Navigation from notifications When the user taps a notification, use `userNotificationCenter(_:didReceive:withCompletionHandler:)` to extract conversation details and navigate to the correct screen. @@ -1313,7 +1277,7 @@ func userNotificationCenter( The `navigateToViewController` helper (shown in `CometChatPNHelper.swift` above) pushes the `MessagesVC` onto the navigation stack. Ensure the root view controller is ready before navigation -- if the app was terminated, wait until login completes before routing. -## 8. Testing checklist +## 6. 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). @@ -1323,7 +1287,7 @@ The `navigateToViewController` helper (shown in `CometChatPNHelper.swift` above) 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 +## Troubleshooting | Symptom | Quick checks | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx index 9bf7987e1..b831b0a2b 100644 --- a/notifications/ios-fcm-push-notifications.mdx +++ b/notifications/ios-fcm-push-notifications.mdx @@ -15,8 +15,8 @@ description: "Guide to integrating Firebase Cloud Messaging (FCM) push notificat ## What this guide covers -- CometChat dashboard setup (enable push, add FCM iOS + APNs providers) with screenshots. -- Firebase/FCM + CometChat provider wiring (credentials, Podfile, capabilities). +- Prerequisite: the shared dashboard + Firebase/APNs setup in the [overview](/notifications/push-overview#shared-setup). +- Podfile + AppDelegate wiring for Firebase Messaging on iOS. - Token registration/rotation with CometChat Push Notifications (FCM iOS provider). - Incoming message/call handling and deep links. - Badge count and grouped notifications. @@ -34,67 +34,11 @@ description: "Guide to integrating Firebase Cloud Messaging (FCM) push notificat - **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. -## 1. Enable push and add providers (CometChat Dashboard) + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. + -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -2. On Firebase: - - Go to **Project Settings → Service accounts** and generate a new private key; download the JSON file. - - - Add FCM credentials - - -3. On CometChat Dashboard: - - Add an **FCM iOS provider** and upload your Firebase service account JSON; copy the Provider ID. - - - Add FCM credentials - - -## 2. Upload your APNs Certificates - -1. In the **Firebase Console**, go to **Project Settings → Cloud Messaging**. -2. Under **iOS app configuration**, upload your APNs authentication key or certificates. - - - Upload APNs Certificates in Firebase Console - - -## 3. Prepare Firebase and CometChat - -1. Firebase Console: download and add `GoogleService-Info.plist` to your target. - - - Upload APNs Certificates in Firebase Console - - -2. Xcode capabilities: Push Notifications, Background Modes → Remote notifications. - - - Enable Push Notifications and Background Modes for FCM - - -## 4. Add dependencies (Podfile) +## 1. Add dependencies (Podfile) ```ruby lines target 'YourApp' do @@ -108,7 +52,7 @@ end Run `pod install`. -## 5. Wire AppDelegate (Firebase + delegates) +## 2. Wire AppDelegate (Firebase + delegates) ```swift lines highlight={15, 17, 19-37} import UIKit @@ -272,7 +216,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD - 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. -## 6. Create FCM helper +## 3. Create FCM helper Create `CometChatFCMHelper.swift` and add: @@ -317,11 +261,11 @@ final class CometChatFCMHelper { - 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. Badge count and grouped notifications +## 4. Badge count and grouped notifications CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. Use this to set the app icon badge and enrich local notifications. -### 7.1 Enable unread badge count on the CometChat Dashboard +### 4.1 Enable unread badge count on the CometChat Dashboard @@ -335,7 +279,7 @@ CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. -### 7.2 Clear badge count when app becomes active +### 4.2 Clear badge count when app becomes active Add the following to your `SceneDelegate.swift` to reset the badge when the user opens the app: @@ -347,7 +291,7 @@ func sceneDidBecomeActive(_ scene: UIScene) { } ``` -### 7.3 Update badge count from push notifications +### 4.3 Update badge count from push notifications Update your `AppDelegate.swift` to handle badge count updates in both foreground and background states: @@ -409,7 +353,7 @@ func application( badge. -## 8. Testing checklist +## 5. Testing checklist 1. `FirebaseApp.configure()` runs; FCM token logs after login; registration with CometChat succeeds. 2. Message from another user: @@ -418,7 +362,7 @@ func application( 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. -## 9. Troubleshooting +## 6. Troubleshooting | Symptom | Quick checks | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | diff --git a/notifications/push-overview.mdx b/notifications/push-overview.mdx index b6d372453..374295d90 100644 --- a/notifications/push-overview.mdx +++ b/notifications/push-overview.mdx @@ -32,14 +32,78 @@ CometChat listens for chat and call events, assembles payloads from your templat **Call notifications and group calls:** Call-related push notifications (e.g., initiated calls, missed calls) are sent as **custom messages**, not as group action messages. The **production mode toggle** in the dashboard only affects APNs endpoint routing (sandbox vs production) — it does not change how the Notification Engine constructs the push payload. If you experience APNs errors (e.g., 400 status) for call pushes, check your APNs provider credentials and certificate configuration rather than the production mode toggle. -## Before you integrate +## Shared setup -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. +The three steps below are common to every platform. Complete them once, then follow your platform guide for app-specific wiring (Gradle, Podfile, manifest, service worker, token registration). Each platform guide links back here. + + +Which providers and credentials you need depends on your platform: **Android / Web / React Native (Android)** use **FCM**; **iOS / React Native (iOS) / Flutter (iOS)** use **APNs** (plus **APNs VoIP** for calls). iOS can optionally use FCM as a bridge. + + +### 1. CometChat dashboard setup + +1. Go to **Notifications → Settings** and enable **Push Notifications**. + + + Enable Push Notifications + + +2. Click **Add Credentials** and add the provider(s) your platform needs: + + - **FCM** (Android, Web, React Native Android, Flutter Android, and optional iOS): upload the Firebase **service account JSON** (Firebase → Project settings → Service accounts → Generate new private key). + - **APNs Device** (iOS alerts): upload your `.p8` Auth Key with its **Key ID**, **Team ID**, and **Bundle ID**. + - **APNs VoIP** (iOS calls): add a second provider with the same `.p8` (recommended for CallKit reliability). + + + Upload FCM service account JSON + + +3. Copy each **Provider ID** and keep your **App ID**, **Region**, and **Auth Key** handy — you'll add these to your app's constants when registering tokens. + +### 2. Firebase configuration + +Required for FCM (Android, Web, React Native Android, Flutter Android) and for iOS when you use FCM or upload your APNs key to Firebase. + +1. In the [Firebase Console](https://console.firebase.google.com/), create or select a project and enable **Cloud Messaging**. +2. Register the app for your platform and download its config file: + + + + Register your Android package name (the same as `applicationId` in `android/app/build.gradle`) and download **`google-services.json`** into `android/app/`. + + + Register your iOS bundle ID, download **`GoogleService-Info.plist`** into your app target, and upload your APNs Auth Key under **Project settings → Cloud Messaging → Apple app configuration**. + + + Add a **Web** app (``), copy the **Firebase config** object, and under **Project settings → Cloud Messaging → Web Push certificates** generate/copy the **VAPID key**. + + + +3. For the CometChat **FCM** provider, generate a **service account JSON** under **Project settings → Service accounts → Generate new private key** and upload it in step 1. + + + Firebase - Push Notifications + + +### 3. iOS (Apple) setup + +Required for any iOS target (native iOS, React Native iOS, Flutter iOS). + +1. **APNs Auth Key (`.p8`)** — in the [Apple Developer Member Center](https://developer.apple.com/membercenter) → **Certificates, Identifiers & Profiles → Keys**, click **+**, enable **Apple Push Notification service (APNs)**, register, and download the `.p8`. Note the **Key ID**, **Team ID**, and your **Bundle ID** — you'll upload these to the CometChat APNs provider(s) in step 1. +2. **Capabilities** — in Xcode, enable **Push Notifications** and **Background Modes → Remote notifications** (add **Voice over IP** as well if you support calls). Add microphone/camera usage strings to `Info.plist` for CallKit. + + +**`.p12` certificates are deprecated.** Apple recommends `.p8` Auth Keys — one key works for all your apps, they never expire, and they are the only format actively supported going forward. Migrate to `.p8` if you haven't already. + + + + Enable Push Notifications and Background Modes for APNs + ## Choose your platform +After completing the shared setup above, follow the guide for your platform to wire the client, register tokens after login, and handle taps/calls/badges. + } href="/notifications/android-push-notifications"> diff --git a/notifications/react-native-push-notifications-android.mdx b/notifications/react-native-push-notifications-android.mdx index a51e7a886..b783029a3 100644 --- a/notifications/react-native-push-notifications-android.mdx +++ b/notifications/react-native-push-notifications-android.mdx @@ -25,8 +25,7 @@ description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP c ## What this guide covers -- CometChat Dashboard setup (enable push, add FCM providers). -- Platform credentials (Firebase). +- Prerequisite: the shared dashboard + Firebase setup in the [overview](/notifications/push-overview#shared-setup). - Copying the sample notification stack and aligning IDs/provider IDs. - Native glue for Android (manifest permissions). - VoIP call alerts with FCM data-only pushes + CallKeep native dialer. @@ -44,32 +43,11 @@ description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP c - **CometChat provider holds your credentials:** The FCM provider you add (for React Native Android) stores your Firebase service account JSON. - **Registration flow:** Request permission → Android returns the FCM token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `FCM_REACT_NATIVE_ANDROID` → CometChat sends pushes to FCM on your behalf → the app handles taps/foreground events via Notifee. -## 1. Enable push and add providers (CometChat Dashboard) + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the React Native app wiring. + -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -2. Add an **FCM** provider for React Native Android; upload the Firebase service account JSON and copy the Provider ID. - - - Upload FCM service account JSON - - -## 2. Prepare platform credentials - -### 2.1 Firebase Console - -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. - - - Firebase - Push Notifications - - -## 3. Local configuration +## 1. Local configuration - Update `src/utils/AppConstants.tsx` with `appId`, `authKey`, `region`, and `fcmProviderId`. - Keep `app.json` name consistent with your bundle ID / applicationId. @@ -81,7 +59,7 @@ const REGION = ""; const DEMO_UID = "cometchat-uid-1"; ``` -### 3.1 Dependencies snapshot (from Sample App) +### 1.1 Dependencies snapshot (from Sample App) Install these dependencies in your React Native app: @@ -100,9 +78,9 @@ npm install \ Match these or newer compatible versions in your app. -## 4. Android App Setup +## 2. Android App Setup -### 4.1 Configure Firebase with Android credentials +### 2.1 Configure Firebase with Android credentials 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. @@ -124,7 +102,7 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' ``` -### 4.2 Configure required permissions in `AndroidManifest.xml` as shown. +### 2.2 Configure required permissions in `AndroidManifest.xml` as shown. ```xml lines @@ -181,7 +159,7 @@ import { PermissionsAndroid, Platform } from "react-native"; } ``` -### 4.3 Register FCM token with CometChat +### 2.3 Register FCM token with CometChat 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: @@ -206,16 +184,16 @@ CometChatNotifications.registerPushToken( }); ``` -### 4.4 Unregister FCM token on logout +### 2.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. -## 5. VoIP call notifications +## 3. VoIP call notifications These steps are Android-only—copy/paste and fill your IDs. -### 5.1 Add CallKeep services to `android/app/src/main/AndroidManifest.xml` +### 3.1 Add CallKeep services to `android/app/src/main/AndroidManifest.xml` Inside the `` tag add: ```xml lines @@ -232,7 +210,7 @@ Inside the `` tag add: ``` -### 5.2 Background handler for call pushes (`index.js`) +### 3.2 Background handler for call pushes (`index.js`) Data-only FCM calls show the native dialer even when the app is killed. ```js lines @@ -279,7 +257,7 @@ if (Platform.OS === "android") { } ``` -### 5.3 Drop in `VoipNotificationHandler.ts` +### 3.3 Drop in `VoipNotificationHandler.ts` Handles CallKeep setup, shows the incoming call UI, accepts/rejects via CometChat, and defers acceptance if login/navigation isn’t ready. ```ts lines @@ -484,7 +462,7 @@ class VoipNotificationHandler { export const voipHandler = new VoipNotificationHandler(); ``` -### 5.4 Add `PendingCallManager.ts` +### 3.4 Add `PendingCallManager.ts` Stores an answered call during cold-start so you can accept it once login/navigation is ready. ```ts lines @@ -528,7 +506,7 @@ export function isPendingStale(p: PendingAnsweredCallPayload, maxAgeMs = 2 * 60 } ``` -### 5.5 Wire `App.tsx` to init VoIP + consume pending accepts +### 3.5 Wire `App.tsx` to init VoIP + consume pending accepts Add this after CometChat init/login: ```ts lines @@ -570,7 +548,7 @@ useEffect(() => { }, []); ``` -### 5.6 Call push payload (FCM data) +### 3.6 Call push payload (FCM data) Send a data-only FCM message like: ```json @@ -587,7 +565,7 @@ Send a data-only FCM message like: } ``` -### 5.7 Local notification helper (`LocalNotificationHandler.ts`) +### 3.7 Local notification helper (`LocalNotificationHandler.ts`) > Ensure `@notifee/react-native` is installed (listed in Dependencies above). Add this helper next to your `index.js` to show local alerts for non-call pushes: @@ -640,18 +618,18 @@ export async function displayLocalNotification(remoteMessage: any) { ``` - For a proper notification icon, create a dedicated `ic_notification.xml` (vector) or PNG in `android/app/src/main/res/drawable/`; Android expects a white glyph with transparency for best results. -## 6. Handling notification taps and navigation +## 4. 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. */} -## 7. Badge Count Implementation +## 5. Badge Count Implementation CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field that represents the total number of unread messages across all conversations for the logged-in user. You can use this value to update the app icon badge, providing users with a visual indicator of unread messages. -### 7.1 Enable Unread Badge Count on the CometChat Dashboard +### 5.1 Enable Unread Badge Count on the CometChat Dashboard @@ -664,7 +642,7 @@ CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` Once enabled, CometChat automatically includes the `unreadMessageCount` field in every push payload sent to your app. -### 7.2 Expected Payload Format +### 5.2 Expected Payload Format CometChat sends push notifications with the following structure: @@ -685,7 +663,7 @@ CometChat sends push notifications with the following structure: The `unreadMessageCount` field is a **string** representing the total unread messages across all conversations for the logged-in user. -### 7.3 Handle Badge Count in Background Messages +### 5.3 Handle Badge Count in Background Messages Update your FCM background message handler in `index.js` to extract and set the badge count: @@ -715,7 +693,7 @@ messaging().setBackgroundMessageHandler(async (remoteMessage) => { }); ``` -### 7.4 Handle Badge Count in Foreground Messages +### 5.4 Handle Badge Count in Foreground Messages In your `App.tsx`, set up a listener for foreground FCM messages: @@ -749,7 +727,7 @@ useEffect(() => { }, []); ``` -### 7.5 Display Local Notification with Badge Count +### 5.5 Display Local Notification with Badge Count Update your notification display function to include the badge count: @@ -810,7 +788,7 @@ export async function displayLocalNotification(remoteMessage: any) { } ``` -### 7.6 Clear Badge When App Becomes Active +### 5.6 Clear Badge When App Becomes Active Clear all notifications and reset the badge when the app returns to the foreground: @@ -832,7 +810,7 @@ useEffect(() => { }, []); ``` -### 7.7 Clear Badge on Logout +### 5.7 Clear Badge on Logout When a user logs out, clear the badge so it doesn't show a stale count on the login screen or for the next user: @@ -854,7 +832,7 @@ const handleLogout = async () => { }; ``` -### 7.8 Clear Badge on Fresh Install / No Logged-In User +### 5.8 Clear Badge on Fresh Install / No Logged-In User Clear the badge during app initialization when no user is logged in. This handles cases where badge count may persist after app reinstall: @@ -879,7 +857,7 @@ const initializeApp = async () => { }; ``` -### 7.9 Clear Badge in Login Listener (Safety Net) +### 5.9 Clear Badge in Login Listener (Safety Net) Register a login listener to clear the badge on logout as a backup mechanism: @@ -908,7 +886,7 @@ useEffect(() => { }, []); ``` -### 7.10 Key Implementation Notes +### 5.10 Key Implementation Notes | Consideration | Details | | --- | --- | @@ -920,7 +898,7 @@ useEffect(() => { | **Login listener safety net** | Use CometChat's login listener as a backup to ensure badge is cleared on logout. | | **Title enhancement** | Optionally display the unread count in the notification title (e.g., "John (5 unread)") for devices that don't support app icon badges. | -## 8. Testing Checklist +## 6. Testing Checklist 1. Install on a physical Android device, grant `POST_NOTIFICATIONS` permission, log in, and verify FCM token registration succeeds. 2. Send a message from another user: @@ -929,7 +907,7 @@ useEffect(() => { 3. **VoIP call:** Send a `callAction=initiated` push; expect the native dialer to appear. Answer and verify the call connects; send `callAction=ended` to dismiss it. 4. Rotate tokens (reinstall or revoke) and confirm `onTokenRefresh` re-registers the new token. -## 9. Troubleshooting +## 7. Troubleshooting | Symptom | Quick Checks | | --- | --- | diff --git a/notifications/react-native-push-notifications-ios.mdx b/notifications/react-native-push-notifications-ios.mdx index f5489fc6e..f448f2401 100644 --- a/notifications/react-native-push-notifications-ios.mdx +++ b/notifications/react-native-push-notifications-ios.mdx @@ -25,8 +25,7 @@ description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP ## What this guide covers -- CometChat Dashboard setup (enable push, add APNs provider). -- Platform credentials (Apple entitlements). +- Prerequisite: the shared dashboard + Apple/APNs setup in the [overview](/notifications/push-overview#shared-setup). - Copying the sample notification stack and aligning IDs/provider IDs. - Native glue for iOS (capabilities + PushKit/CallKit for VoIP). - Token registration, navigation from pushes, testing, and troubleshooting. @@ -43,86 +42,11 @@ description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP - **CometChat provider holds your credentials:** The APNs provider you add stores your `.p8` key/cert. - **Registration flow:** Request permission → APNs returns token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `APNS_REACT_NATIVE_DEVICE` → CometChat sends pushes to APNs on your behalf → the app handles taps/foreground events via `PushNotificationIOS`. -## 1. Enable push and add providers (CometChat Dashboard) + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs** (and **APNs VoIP**) providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the React Native app wiring. + -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -2. Add an **APNs** provider for iOS and copy the Provider ID. - - - Upload APNs credentials - - -## 2. Prepare platform credentials - -### Apple Developer portal - -For iOS we use Apple Push Notification service (APNs) for both standard and VoIP pushes. Follow these steps to create the credentials you’ll upload to CometChat. - - - - 1. Open **Keychain Access** → Certificate Assistant → *Request a Certificate From a Certificate Authority*.
- - Apple Developer portal screenshot - - 2. In **Certificate Information**, enter your Apple Developer email and a common name; choose **Saved to disk**, then **Continue**. - 3. Save the CSR file locally—this contains your public/private key pair. -
- - - 1. Sign in to the [Apple Developer Member Center](https://developer.apple.com/membercenter) → **Certificates, Identifiers & Profiles**.
- - Apple Developer portal screenshot - - 2. Click **+** to add a certificate.
- - Apple Developer portal screenshot - - 3. Under **Services**, pick **Apple Push Notification service SSL (Sandbox & Production)**.
- - Apple Developer portal screenshot - - 4. Select your App ID, upload the CSR, continue, and download the generated `.cer` file.
- - Apple Developer portal screenshot - - - & - - - Apple Developer portal screenshot - - - & - - - Apple Developer portal screenshot - -
- - - 1. In **Certificates, IDs & Profiles**, open **Keys** → click **+**. - 2. Enter a key name, check **Apple Push Notification service (APNs)**, then **Continue** → **Register**. - 3. Download the `.p8` file and note the **Key ID**, **Team ID**, and your **Bundle ID**—you’ll enter these in CometChat. - 4. *(Optional)* If you still use `.p12`, export it from the downloaded key without an export password; keep it handy for upload. - - - **`.p12` certificates are deprecated.** Apple recommends using `.p8` Auth Keys for push notifications. `.p8` keys are simpler to manage (one key works for all your apps), never expire, and are the only format actively supported going forward. Migrate to `.p8` if you haven't already. - - -
- -Enable **Push Notifications** plus **Background Modes → Remote notifications** on the bundle ID. - - - Enable Push Notifications and Background Modes for APNs - - -## 3. Local configuration +## 1. Local configuration - Update `src/utils/AppConstants.tsx` with `appId`, `authKey`, `region`, and `apnProviderId`. - Keep `app.json` name consistent with your bundle ID / applicationId. @@ -134,7 +58,7 @@ const REGION = ""; const DEMO_UID = "cometchat-uid-1"; ``` -### 3.1 Dependencies snapshot (from Sample App) +### 1.1 Dependencies snapshot (from Sample App) Install these dependencies in your React Native app: @@ -153,9 +77,9 @@ npm install \ Match these or newer compatible versions in your app. -## 4. iOS App setup +## 2. iOS App setup -### 4.1 Project Setup +### 2.1 Project Setup Enable **Push Notifications** and **Background Modes** (Remote notifications) in Xcode. @@ -163,7 +87,7 @@ Enable **Push Notifications** and **Background Modes** (Remote notifications) in Enable Push Notifications -### 4.2 Install dependencies + pods +### 2.2 Install dependencies + pods After running the npm install above, install pods from the `ios` directory: ```bash lines @@ -171,7 +95,7 @@ cd ios pod install ``` -### 4.3 AppDelegate.swift modifications: +### 2.3 AppDelegate.swift modifications: Add imports at the top: ```swift lines @@ -250,7 +174,7 @@ pod install open YourProjectName.xcworkspace ``` -### 4.4 App.tsx modifications: +### 2.4 App.tsx modifications: Import CometChatNotifications and PushNotificationIOS: @@ -311,16 +235,16 @@ Prior to logout, unregister the APNs token: await CometChatNotifications.unregisterPushToken(); ``` -## 5. VoIP call notifications (iOS) +## 3. VoIP call notifications (iOS) These steps are iOS-only—copy/paste and fill your IDs. -### 5.1 Enable capabilities in Xcode +### 3.1 Enable capabilities in Xcode - Target ➜ Signing & Capabilities: add **Push Notifications**. - Add **Background Modes** → enable **Voice over IP** and **Remote notifications**. - Run on a real device (PushKit/CallKit don’t work on the simulator). -### 5.2 AppDelegate.swift (PushKit + CallKit bridge) +### 3.2 AppDelegate.swift (PushKit + CallKit bridge) Update your `AppDelegate` to register for VoIP pushes ASAP and forward events to JS/CallKeep: ```swift lines @@ -355,7 +279,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } ``` -### 5.3 Drop in `VoipNotificationHandler.ts` +### 3.3 Drop in `VoipNotificationHandler.ts` Handles CallKeep UI, defers acceptance until login, and listens for PushKit events. ```ts lines @@ -474,7 +398,7 @@ class VoipNotificationHandler { export const voipHandler = new VoipNotificationHandler(); ``` -### 5.4 Add `PendingCallManager.ts` +### 3.4 Add `PendingCallManager.ts` Stores an answered call during cold start so you can accept it after login/navigation is ready. ```ts lines @@ -499,7 +423,7 @@ export function isPendingStale(p: PendingAnsweredCallPayload, maxAgeMs = 2 * 60 } ``` -### 5.5 Wire `App.tsx` for APNs + VoIP token registration and handler init +### 3.5 Wire `App.tsx` for APNs + VoIP token registration and handler init ```tsx lines import PushNotificationIOS from "@react-native-community/push-notification-ios"; @@ -553,7 +477,7 @@ useEffect(() => { }, [loggedIn]); ``` -### 5.6 VoIP push payload (APNs / PushKit) +### 3.6 VoIP push payload (APNs / PushKit) Send a VoIP push with `push_type=voip` via APNs using a payload shaped like: ```json @@ -567,18 +491,18 @@ Send a VoIP push with `push_type=voip` via APNs using a payload shaped like: } ``` -## 6. Handling notification taps and navigation +## 4. 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. */} -## 7. Badge Count Implementation +## 5. Badge Count Implementation CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field that represents the total number of unread messages across all conversations for the logged-in user. You can use this value to update the app icon badge, providing users with a visual indicator of unread messages. -### 7.1 Enable Unread Badge Count on the CometChat Dashboard +### 5.1 Enable Unread Badge Count on the CometChat Dashboard @@ -591,7 +515,7 @@ CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` Once enabled, CometChat automatically includes the `unreadMessageCount` field in every push payload sent to your app. -### 7.2 Expected Payload Format +### 5.2 Expected Payload Format CometChat sends APNs payloads with the following structure: @@ -614,7 +538,7 @@ CometChat sends APNs payloads with the following structure: The `aps.badge` field is set server-side by CometChat. iOS automatically updates the app icon badge when the push notification is delivered. -### 7.3 Handle Badge Count from Notifications +### 5.3 Handle Badge Count from Notifications Update your iOS notification handler to set the badge count programmatically: @@ -645,7 +569,7 @@ export async function onRemoteNotificationIOS(notification: any) { } ``` -### 7.4 Register Notification Listener +### 5.4 Register Notification Listener In your `App.tsx`, set up the notification listener: @@ -671,7 +595,7 @@ useEffect(() => { }, []); ``` -### 7.5 Clear Badge When App Becomes Active +### 5.5 Clear Badge When App Becomes Active Clear the badge count when the app launches or returns to the foreground: @@ -692,7 +616,7 @@ useEffect(() => { }, []); ``` -### 7.6 Clear Badge on Logout +### 5.6 Clear Badge on Logout When a user logs out, clear the badge so it doesn't show a stale count on the login screen or for the next user: @@ -713,7 +637,7 @@ const handleLogout = async () => { }; ``` -### 7.7 Clear Badge on Fresh Install / No Logged-In User +### 5.7 Clear Badge on Fresh Install / No Logged-In User On iOS, the badge count may persist after app uninstall and reinstall in certain scenarios. Clear the badge during app initialization when no user is logged in: @@ -737,7 +661,7 @@ const initializeApp = async () => { }; ``` -### 7.8 Clear Badge in Login Listener (Safety Net) +### 5.8 Clear Badge in Login Listener (Safety Net) Register a login listener to clear the badge on logout as a backup mechanism: @@ -765,7 +689,7 @@ useEffect(() => { }, []); ``` -### 7.9 Key Implementation Notes +### 5.9 Key Implementation Notes | Consideration | Details | | --- | --- | @@ -777,7 +701,7 @@ useEffect(() => { | **Login listener safety net** | Use CometChat's login listener as a backup to ensure badge is cleared on logout. | | **Title enhancement** | Optionally display the unread count in the notification title (e.g., "John (5 unread)") for additional visibility. | -### 7.10 Cross-Platform App State Handler +### 5.10 Cross-Platform App State Handler If you're building a cross-platform app, use this combined handler for both iOS and Android: @@ -807,7 +731,7 @@ useEffect(() => { }, []); ``` -## 8. Testing Checklist +## 6. Testing Checklist 1. Install on a physical iOS device, log in, and verify APNs token registration succeeds. 2. Send a message from another user: @@ -816,7 +740,7 @@ useEffect(() => { 3. **VoIP:** Send a PushKit VoIP push (payload above); expect CallKit incoming UI; answer and confirm CometChat call connects; end clears the dialer. 4. Rotate tokens (reinstall or revoke) and confirm `onTokenRefresh` re-registers the new token. -## 9. Troubleshooting +## 7. Troubleshooting | Symptom | Quick Checks | | --- | --- | diff --git a/notifications/web-push-notifications.mdx b/notifications/web-push-notifications.mdx index 17c583e43..3a2904c65 100644 --- a/notifications/web-push-notifications.mdx +++ b/notifications/web-push-notifications.mdx @@ -13,7 +13,7 @@ description: "Set up FCM web push for CometChat React UI Kit—service worker, V ## What this guide covers -- CometChat dashboard setup (FCM provider + Provider ID) and Firebase web config + VAPID key. +- Prerequisite: the shared dashboard + Firebase web config/VAPID setup in the [overview](/notifications/push-overview#shared-setup). - 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. @@ -32,26 +32,11 @@ description: "Set up FCM web push for CometChat React UI Kit—service worker, V - **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. -## 1. Dashboard: enable push + add FCM provider + +**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and set up your Firebase **web app config** and **VAPID key**. This guide covers only the web app wiring. + -1. Go to **Notifications → Settings** and enable **Push Notifications**. -2. Add/configure an **FCM** provider and copy the **Provider ID** (used in code). - - - Firebase - Push Notifications - - -## 2. Firebase setup (web app + VAPID) - -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. - - - Firebase - Push Notifications - - -## 3. Install dependencies +## 1. Install dependencies Install firebase SDK: @@ -59,7 +44,7 @@ Install firebase SDK: npm install firebase@^10.3.1 ``` -## 4. Constants +## 2. Constants File: `src/AppConstants.js` (or equivalent) @@ -86,7 +71,7 @@ export const FIREBASE_CONFIG = { export const FIREBASE_VAPID_KEY = ""; ``` -## 4. Configure Firebase (frontend) +## 3. Configure Firebase (frontend) File: `src/firebase.js` (or equivalent) @@ -226,7 +211,7 @@ export async function registerPushTokenAfterLogin() { Use your **VAPID key** in `getToken` calls. -## 5. Service worker (background pushes) +## 4. Service worker (background pushes) File: `public/firebase-messaging-sw.js` @@ -347,7 +332,7 @@ try { Ensure your app registers the service worker (e.g., in `index.tsx`) and listens for `message` events to navigate. -## 6. Request permission + register token after login +## 5. Request permission + register token after login In your app initialization (e.g., `App.tsx`): @@ -444,13 +429,13 @@ async function boot() { boot(); ``` -## 7. Foreground + background handling +## 6. Foreground + background handling - **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. -## 8. Testing checklist +## 7. Testing checklist 1. Service worker registered (DevTools → Application → Service Workers shows “activated”). 2. Permission prompt appears and is granted (`Notification.permission === "granted"`). @@ -459,7 +444,7 @@ boot(); 5. Background/tab inactive message shows a notification; click focuses tab and routes correctly. 6. Logout → `unregisterPushToken` runs without errors. -## 9. Troubleshooting +## 8. Troubleshooting | Symptom | Quick checks | | --- | --- | From 1ac92511f422528961455c10681964d8863f5d23 Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Mon, 27 Jul 2026 19:02:29 +0530 Subject: [PATCH 03/11] Added getting started --- docs.json | 3 +- notifications/android-push-notifications.mdx | 2 +- .../flutter-push-notifications-android.mdx | 2 +- .../flutter-push-notifications-ios.mdx | 2 +- notifications/ios-apns-push-notifications.mdx | 4 +- notifications/ios-fcm-push-notifications.mdx | 4 +- notifications/push-getting-started.mdx | 110 ++++++++++++++++++ notifications/push-overview.mdx | 108 +---------------- ...eact-native-push-notifications-android.mdx | 4 +- .../react-native-push-notifications-ios.mdx | 4 +- notifications/web-push-notifications.mdx | 4 +- 11 files changed, 127 insertions(+), 120 deletions(-) create mode 100644 notifications/push-getting-started.mdx diff --git a/docs.json b/docs.json index 316b5d28f..85d770a9e 100644 --- a/docs.json +++ b/docs.json @@ -6480,8 +6480,9 @@ "tab": "Push", "pages": [ "notifications/push-overview", + "notifications/push-getting-started", { - "group": "Getting Started", + "group": "Platforms", "pages": [ "notifications/android-push-notifications", "notifications/ios-apns-push-notifications", diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index 67f1c6871..44528ab75 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -36,7 +36,7 @@ description: "Setup FCM and CometChat for message and call push notifications on - **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. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. ## 1. Add dependencies (Gradle) diff --git a/notifications/flutter-push-notifications-android.mdx b/notifications/flutter-push-notifications-android.mdx index c4fa81970..4bfc9eb4c 100644 --- a/notifications/flutter-push-notifications-android.mdx +++ b/notifications/flutter-push-notifications-android.mdx @@ -50,7 +50,7 @@ The `cometchat_push_notifications` plugin replaces the previous approach of copy - A physical Android device — full-screen call notifications and background delivery are unreliable on emulators. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. ## 1. Store your credentials diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx index c2f6ecba7..2a5c05da2 100644 --- a/notifications/flutter-push-notifications-ios.mdx +++ b/notifications/flutter-push-notifications-ios.mdx @@ -51,7 +51,7 @@ On iOS you don't need Firebase. This guide uses **pure APNs + PushKit**. If you - A physical iPhone or iPad — simulators cannot receive VoIP pushes or present CallKit UI. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. ## 1. Store your credentials diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx index 8e8fa1919..b2499d402 100644 --- a/notifications/ios-apns-push-notifications.mdx +++ b/notifications/ios-apns-push-notifications.mdx @@ -13,7 +13,7 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in ## What this guide covers -- Prerequisite: the shared dashboard + Apple/APNs setup in the [overview](/notifications/push-overview#shared-setup). +- Prerequisite: the shared dashboard + Apple/APNs setup in the [Getting Started](/notifications/push-getting-started) guide. - APNs + PushKit/CallKit wiring (tokens, delegates, CallKit). - Incoming message/call handling and deep links. - Badge count and grouped notifications. @@ -32,7 +32,7 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in - **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. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. ## 1. Wiring APNs + PushKit/CallKit diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx index b831b0a2b..c3ebfb03d 100644 --- a/notifications/ios-fcm-push-notifications.mdx +++ b/notifications/ios-fcm-push-notifications.mdx @@ -15,7 +15,7 @@ description: "Guide to integrating Firebase Cloud Messaging (FCM) push notificat ## What this guide covers -- Prerequisite: the shared dashboard + Firebase/APNs setup in the [overview](/notifications/push-overview#shared-setup). +- Prerequisite: the shared dashboard + Firebase/APNs setup in the [Getting Started](/notifications/push-getting-started) guide. - Podfile + AppDelegate wiring for Firebase Messaging on iOS. - Token registration/rotation with CometChat Push Notifications (FCM iOS provider). - Incoming message/call handling and deep links. @@ -35,7 +35,7 @@ description: "Guide to integrating Firebase Cloud Messaging (FCM) push notificat - **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. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. ## 1. Add dependencies (Podfile) diff --git a/notifications/push-getting-started.mdx b/notifications/push-getting-started.mdx new file mode 100644 index 000000000..0576eac02 --- /dev/null +++ b/notifications/push-getting-started.mdx @@ -0,0 +1,110 @@ +--- +title: "Getting Started" +description: "Shared setup for CometChat Push Notifications — enable push, add providers, configure Firebase and APNs, then pick your platform." +--- + +The steps below are common to every platform. Complete them once, then follow your platform guide for app-specific wiring (Gradle, Podfile, manifest, service worker, token registration). Each platform guide links back here. + + +Which providers and credentials you need depends on your platform: **Android / Web / React Native (Android)** use **FCM**; **iOS / React Native (iOS) / Flutter (iOS)** use **APNs** (plus **APNs VoIP** for calls). iOS can optionally use FCM as a bridge. + + +## CometChat dashboard setup + +1. Go to **Notifications → Settings** and enable **Push Notifications**. + + + Enable Push Notifications + + +2. Click **Add Credentials** and add the provider(s) your platform needs: + + - **FCM** (Android, Web, React Native Android, Flutter Android, and optional iOS): upload the Firebase **service account JSON** (Firebase → Project settings → Service accounts → Generate new private key). + - **APNs Device** (iOS alerts): upload your `.p8` Auth Key with its **Key ID**, **Team ID**, and **Bundle ID**. + - **APNs VoIP** (iOS calls): add a second provider with the same `.p8` (recommended for CallKit reliability). + + + Upload FCM service account JSON + + +3. Copy each **Provider ID** and keep your **App ID**, **Region**, and **Auth Key** handy — you'll add these to your app's constants when registering tokens. + +## Firebase configuration + +Required for FCM (Android, Web, React Native Android, Flutter Android) and for iOS when you use FCM or upload your APNs key to Firebase. + +1. In the [Firebase Console](https://console.firebase.google.com/), create or select a project and enable **Cloud Messaging**. +2. Register the app for your platform and download its config file: + + + + Register your Android package name (the same as `applicationId` in `android/app/build.gradle`) and download **`google-services.json`** into `android/app/`. + + + Register your iOS bundle ID, download **`GoogleService-Info.plist`** into your app target, and upload your APNs Auth Key under **Project settings → Cloud Messaging → Apple app configuration**. + + + Add a **Web** app (``), copy the **Firebase config** object, and under **Project settings → Cloud Messaging → Web Push certificates** generate/copy the **VAPID key**. + + + +3. For the CometChat **FCM** provider, generate a **service account JSON** under **Project settings → Service accounts → Generate new private key** and upload it in the dashboard step above. + + + Firebase - Push Notifications + + +## iOS (Apple) setup + +Required for any iOS target (native iOS, React Native iOS, Flutter iOS). + +1. **APNs Auth Key (`.p8`)** — in the [Apple Developer Member Center](https://developer.apple.com/membercenter) → **Certificates, Identifiers & Profiles → Keys**, click **+**, enable **Apple Push Notification service (APNs)**, register, and download the `.p8`. Note the **Key ID**, **Team ID**, and your **Bundle ID** — you'll upload these to the CometChat APNs provider(s) in the dashboard step above. +2. **Capabilities** — in Xcode, enable **Push Notifications** and **Background Modes → Remote notifications** (add **Voice over IP** as well if you support calls). Add microphone/camera usage strings to `Info.plist` for CallKit. + + +**`.p12` certificates are deprecated.** Apple recommends `.p8` Auth Keys — one key works for all your apps, they never expire, and they are the only format actively supported going forward. Migrate to `.p8` if you haven't already. + + + + Enable Push Notifications and Background Modes for APNs + + +## Choose your platform + +After completing the setup above, follow the guide for your platform to wire the client, register tokens after login, and handle taps/calls/badges. + + + +} href="/notifications/android-push-notifications"> +UI Kit implementation + + +} href="/notifications/ios-apns-push-notifications"> +UI Kit implementation + + +} href="/notifications/ios-fcm-push-notifications"> +UI Kit implementation + + +} href="/notifications/flutter-push-notifications-android"> +UI Kit implementation + + +} href="/notifications/flutter-push-notifications-ios"> +UI Kit implementation + + +} href="/notifications/react-native-push-notifications-android"> +UI Kit implementation + + +} href="/notifications/react-native-push-notifications-ios"> +UI Kit implementation + + +} href="/notifications/web-push-notifications"> +UI Kit implementation + + + diff --git a/notifications/push-overview.mdx b/notifications/push-overview.mdx index 374295d90..90e3825a6 100644 --- a/notifications/push-overview.mdx +++ b/notifications/push-overview.mdx @@ -32,110 +32,6 @@ CometChat listens for chat and call events, assembles payloads from your templat **Call notifications and group calls:** Call-related push notifications (e.g., initiated calls, missed calls) are sent as **custom messages**, not as group action messages. The **production mode toggle** in the dashboard only affects APNs endpoint routing (sandbox vs production) — it does not change how the Notification Engine constructs the push payload. If you experience APNs errors (e.g., 400 status) for call pushes, check your APNs provider credentials and certificate configuration rather than the production mode toggle. -## Shared setup +## Next: Getting Started -The three steps below are common to every platform. Complete them once, then follow your platform guide for app-specific wiring (Gradle, Podfile, manifest, service worker, token registration). Each platform guide links back here. - - -Which providers and credentials you need depends on your platform: **Android / Web / React Native (Android)** use **FCM**; **iOS / React Native (iOS) / Flutter (iOS)** use **APNs** (plus **APNs VoIP** for calls). iOS can optionally use FCM as a bridge. - - -### 1. CometChat dashboard setup - -1. Go to **Notifications → Settings** and enable **Push Notifications**. - - - Enable Push Notifications - - -2. Click **Add Credentials** and add the provider(s) your platform needs: - - - **FCM** (Android, Web, React Native Android, Flutter Android, and optional iOS): upload the Firebase **service account JSON** (Firebase → Project settings → Service accounts → Generate new private key). - - **APNs Device** (iOS alerts): upload your `.p8` Auth Key with its **Key ID**, **Team ID**, and **Bundle ID**. - - **APNs VoIP** (iOS calls): add a second provider with the same `.p8` (recommended for CallKit reliability). - - - Upload FCM service account JSON - - -3. Copy each **Provider ID** and keep your **App ID**, **Region**, and **Auth Key** handy — you'll add these to your app's constants when registering tokens. - -### 2. Firebase configuration - -Required for FCM (Android, Web, React Native Android, Flutter Android) and for iOS when you use FCM or upload your APNs key to Firebase. - -1. In the [Firebase Console](https://console.firebase.google.com/), create or select a project and enable **Cloud Messaging**. -2. Register the app for your platform and download its config file: - - - - Register your Android package name (the same as `applicationId` in `android/app/build.gradle`) and download **`google-services.json`** into `android/app/`. - - - Register your iOS bundle ID, download **`GoogleService-Info.plist`** into your app target, and upload your APNs Auth Key under **Project settings → Cloud Messaging → Apple app configuration**. - - - Add a **Web** app (``), copy the **Firebase config** object, and under **Project settings → Cloud Messaging → Web Push certificates** generate/copy the **VAPID key**. - - - -3. For the CometChat **FCM** provider, generate a **service account JSON** under **Project settings → Service accounts → Generate new private key** and upload it in step 1. - - - Firebase - Push Notifications - - -### 3. iOS (Apple) setup - -Required for any iOS target (native iOS, React Native iOS, Flutter iOS). - -1. **APNs Auth Key (`.p8`)** — in the [Apple Developer Member Center](https://developer.apple.com/membercenter) → **Certificates, Identifiers & Profiles → Keys**, click **+**, enable **Apple Push Notification service (APNs)**, register, and download the `.p8`. Note the **Key ID**, **Team ID**, and your **Bundle ID** — you'll upload these to the CometChat APNs provider(s) in step 1. -2. **Capabilities** — in Xcode, enable **Push Notifications** and **Background Modes → Remote notifications** (add **Voice over IP** as well if you support calls). Add microphone/camera usage strings to `Info.plist` for CallKit. - - -**`.p12` certificates are deprecated.** Apple recommends `.p8` Auth Keys — one key works for all your apps, they never expire, and they are the only format actively supported going forward. Migrate to `.p8` if you haven't already. - - - - Enable Push Notifications and Background Modes for APNs - - -## Choose your platform - -After completing the shared setup above, follow the guide for your platform to wire the client, register tokens after login, and handle taps/calls/badges. - - - -} href="/notifications/android-push-notifications"> -UI Kit implementation - - -} href="/notifications/ios-apns-push-notifications"> -UI Kit implementation - - -} href="/notifications/ios-fcm-push-notifications"> -UI Kit implementation - - -} href="/notifications/flutter-push-notifications-android"> -UI Kit implementation - - -} href="/notifications/flutter-push-notifications-ios"> -UI Kit implementation - - -} href="/notifications/react-native-push-notifications-android"> -UI Kit implementation - - -} href="/notifications/react-native-push-notifications-ios"> -UI Kit implementation - - -} href="/notifications/web-push-notifications"> -UI Kit implementation - - - +Ready to integrate? Head to **[Getting Started](/notifications/push-getting-started)** to enable push, add your providers, configure Firebase/APNs, and pick your platform guide. diff --git a/notifications/react-native-push-notifications-android.mdx b/notifications/react-native-push-notifications-android.mdx index b783029a3..e32b277a1 100644 --- a/notifications/react-native-push-notifications-android.mdx +++ b/notifications/react-native-push-notifications-android.mdx @@ -25,7 +25,7 @@ description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP c ## What this guide covers -- Prerequisite: the shared dashboard + Firebase setup in the [overview](/notifications/push-overview#shared-setup). +- Prerequisite: the shared dashboard + Firebase setup in the [Getting Started](/notifications/push-getting-started) guide. - Copying the sample notification stack and aligning IDs/provider IDs. - Native glue for Android (manifest permissions). - VoIP call alerts with FCM data-only pushes + CallKeep native dialer. @@ -44,7 +44,7 @@ description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP c - **Registration flow:** Request permission → Android returns the FCM token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `FCM_REACT_NATIVE_ANDROID` → CometChat sends pushes to FCM on your behalf → the app handles taps/foreground events via Notifee. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the React Native app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the React Native app wiring. ## 1. Local configuration diff --git a/notifications/react-native-push-notifications-ios.mdx b/notifications/react-native-push-notifications-ios.mdx index f448f2401..7075d0781 100644 --- a/notifications/react-native-push-notifications-ios.mdx +++ b/notifications/react-native-push-notifications-ios.mdx @@ -25,7 +25,7 @@ description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP ## What this guide covers -- Prerequisite: the shared dashboard + Apple/APNs setup in the [overview](/notifications/push-overview#shared-setup). +- Prerequisite: the shared dashboard + Apple/APNs setup in the [Getting Started](/notifications/push-getting-started) guide. - Copying the sample notification stack and aligning IDs/provider IDs. - Native glue for iOS (capabilities + PushKit/CallKit for VoIP). - Token registration, navigation from pushes, testing, and troubleshooting. @@ -43,7 +43,7 @@ description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP - **Registration flow:** Request permission → APNs returns token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `APNS_REACT_NATIVE_DEVICE` → CometChat sends pushes to APNs on your behalf → the app handles taps/foreground events via `PushNotificationIOS`. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your **APNs** (and **APNs VoIP**) providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the React Native app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs** (and **APNs VoIP**) providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the React Native app wiring. ## 1. Local configuration diff --git a/notifications/web-push-notifications.mdx b/notifications/web-push-notifications.mdx index 3a2904c65..5d6ffbd32 100644 --- a/notifications/web-push-notifications.mdx +++ b/notifications/web-push-notifications.mdx @@ -13,7 +13,7 @@ description: "Set up FCM web push for CometChat React UI Kit—service worker, V ## What this guide covers -- Prerequisite: the shared dashboard + Firebase web config/VAPID setup in the [overview](/notifications/push-overview#shared-setup). +- Prerequisite: the shared dashboard + Firebase web config/VAPID setup in the [Getting Started](/notifications/push-getting-started) guide. - 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. @@ -33,7 +33,7 @@ description: "Set up FCM web push for CometChat React UI Kit—service worker, V - **Navigation**: Service worker sends a postMessage or focuses the client; the app routes to the right view. -**Complete the [shared setup](/notifications/push-overview#shared-setup) first** — enable Push Notifications, add your FCM provider, and set up your Firebase **web app config** and **VAPID key**. This guide covers only the web app wiring. +**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and set up your Firebase **web app config** and **VAPID key**. This guide covers only the web app wiring. ## 1. Install dependencies From 83e29ad04bde6208d38d29b1076c31e30778e8ab Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Mon, 27 Jul 2026 19:10:26 +0530 Subject: [PATCH 04/11] Updated code --- notifications/android-push-notifications.mdx | 2 +- notifications/flutter-push-notifications-android.mdx | 4 ++-- notifications/flutter-push-notifications-ios.mdx | 4 ++-- notifications/ios-apns-push-notifications.mdx | 2 +- notifications/ios-fcm-push-notifications.mdx | 2 +- notifications/push-getting-started.mdx | 2 +- notifications/react-native-push-notifications-android.mdx | 2 +- notifications/react-native-push-notifications-ios.mdx | 2 +- notifications/web-push-notifications.mdx | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index 44528ab75..d5f708f3b 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -36,7 +36,7 @@ description: "Setup FCM and CometChat for message and call push notifications on - **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. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. ## 1. Add dependencies (Gradle) diff --git a/notifications/flutter-push-notifications-android.mdx b/notifications/flutter-push-notifications-android.mdx index 4bfc9eb4c..19a8b32b6 100644 --- a/notifications/flutter-push-notifications-android.mdx +++ b/notifications/flutter-push-notifications-android.mdx @@ -50,12 +50,12 @@ The `cometchat_push_notifications` plugin replaces the previous approach of copy - A physical Android device — full-screen call notifications and background delivery are unreliable on emulators. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. ## 1. Store your credentials -Keep your CometChat App ID, Region, and FCM **Provider ID** (from the shared setup) somewhere your app can read them — for example a small `AppCredentials` class: +Keep your CometChat App ID, Region, and FCM **Provider ID** (from the Getting Started guide) somewhere your app can read them — for example a small `AppCredentials` class: ```dart lines class AppCredentials { diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx index 2a5c05da2..9e3be4717 100644 --- a/notifications/flutter-push-notifications-ios.mdx +++ b/notifications/flutter-push-notifications-ios.mdx @@ -51,12 +51,12 @@ On iOS you don't need Firebase. This guide uses **pure APNs + PushKit**. If you - A physical iPhone or iPad — simulators cannot receive VoIP pushes or present CallKit UI. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. ## 1. Store your credentials -Keep your CometChat App ID, Region, and the **APNs** / **APNs VoIP** Provider IDs (from the shared setup) somewhere your app can read them: +Keep your CometChat App ID, Region, and the **APNs** / **APNs VoIP** Provider IDs (from the Getting Started guide) somewhere your app can read them: ```dart lines class AppCredentials { diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx index b2499d402..5a2728d6e 100644 --- a/notifications/ios-apns-push-notifications.mdx +++ b/notifications/ios-apns-push-notifications.mdx @@ -32,7 +32,7 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in - **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. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. ## 1. Wiring APNs + PushKit/CallKit diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx index c3ebfb03d..3dec0b426 100644 --- a/notifications/ios-fcm-push-notifications.mdx +++ b/notifications/ios-fcm-push-notifications.mdx @@ -35,7 +35,7 @@ description: "Guide to integrating Firebase Cloud Messaging (FCM) push notificat - **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. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. ## 1. Add dependencies (Podfile) diff --git a/notifications/push-getting-started.mdx b/notifications/push-getting-started.mdx index 0576eac02..bb89db7f1 100644 --- a/notifications/push-getting-started.mdx +++ b/notifications/push-getting-started.mdx @@ -1,6 +1,6 @@ --- title: "Getting Started" -description: "Shared setup for CometChat Push Notifications — enable push, add providers, configure Firebase and APNs, then pick your platform." +description: "Getting started with CometChat Push Notifications — enable push, add providers, configure Firebase and APNs, then pick your platform." --- The steps below are common to every platform. Complete them once, then follow your platform guide for app-specific wiring (Gradle, Podfile, manifest, service worker, token registration). Each platform guide links back here. diff --git a/notifications/react-native-push-notifications-android.mdx b/notifications/react-native-push-notifications-android.mdx index e32b277a1..236316a07 100644 --- a/notifications/react-native-push-notifications-android.mdx +++ b/notifications/react-native-push-notifications-android.mdx @@ -44,7 +44,7 @@ description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP c - **Registration flow:** Request permission → Android returns the FCM token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `FCM_REACT_NATIVE_ANDROID` → CometChat sends pushes to FCM on your behalf → the app handles taps/foreground events via Notifee. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the React Native app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the React Native app wiring. ## 1. Local configuration diff --git a/notifications/react-native-push-notifications-ios.mdx b/notifications/react-native-push-notifications-ios.mdx index 7075d0781..34bc18625 100644 --- a/notifications/react-native-push-notifications-ios.mdx +++ b/notifications/react-native-push-notifications-ios.mdx @@ -43,7 +43,7 @@ description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP - **Registration flow:** Request permission → APNs returns token → after `CometChat.login`, register with `CometChatNotifications.registerPushToken(token, platform, providerId)` using `APNS_REACT_NATIVE_DEVICE` → CometChat sends pushes to APNs on your behalf → the app handles taps/foreground events via `PushNotificationIOS`. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your **APNs** (and **APNs VoIP**) providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the React Native app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs** (and **APNs VoIP**) providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the React Native app wiring. ## 1. Local configuration diff --git a/notifications/web-push-notifications.mdx b/notifications/web-push-notifications.mdx index 5d6ffbd32..e69a56736 100644 --- a/notifications/web-push-notifications.mdx +++ b/notifications/web-push-notifications.mdx @@ -33,7 +33,7 @@ description: "Set up FCM web push for CometChat React UI Kit—service worker, V - **Navigation**: Service worker sends a postMessage or focuses the client; the app routes to the right view. -**Complete the [shared setup](/notifications/push-getting-started) first** — enable Push Notifications, add your FCM provider, and set up your Firebase **web app config** and **VAPID key**. This guide covers only the web app wiring. +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and set up your Firebase **web app config** and **VAPID key**. This guide covers only the web app wiring. ## 1. Install dependencies From ae2715343a2357e4afe5a45e43d3e7245289f73e Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Mon, 27 Jul 2026 20:18:07 +0530 Subject: [PATCH 05/11] Merged Flutter android and ios --- docs.json | 30 +- notifications.mdx | 3 +- notifications/android-push-notifications.mdx | 2 +- .../flutter-push-notifications-android.mdx | 403 -------------- .../flutter-push-notifications-ios.mdx | 303 ----------- notifications/flutter-push-notifications.mdx | 501 ++++++++++++++++++ notifications/ios-apns-push-notifications.mdx | 2 +- notifications/ios-fcm-push-notifications.mdx | 2 +- notifications/push-getting-started.mdx | 8 +- ...eact-native-push-notifications-android.mdx | 2 +- .../react-native-push-notifications-ios.mdx | 2 +- notifications/web-push-notifications.mdx | 2 +- 12 files changed, 529 insertions(+), 731 deletions(-) delete mode 100644 notifications/flutter-push-notifications-android.mdx delete mode 100644 notifications/flutter-push-notifications-ios.mdx create mode 100644 notifications/flutter-push-notifications.mdx diff --git a/docs.json b/docs.json index 85d770a9e..050eaa71f 100644 --- a/docs.json +++ b/docs.json @@ -6479,23 +6479,27 @@ { "tab": "Push", "pages": [ - "notifications/push-overview", - "notifications/push-getting-started", { - "group": "Platforms", + "group": "Overview", + "pages": [ + "notifications/push-overview", + "notifications/push-getting-started" + ] + }, + { + "group": "Platform Integrations", "pages": [ "notifications/android-push-notifications", "notifications/ios-apns-push-notifications", "notifications/ios-fcm-push-notifications", - "notifications/flutter-push-notifications-android", - "notifications/flutter-push-notifications-ios", + "notifications/flutter-push-notifications", "notifications/react-native-push-notifications-android", "notifications/react-native-push-notifications-ios", "notifications/web-push-notifications" ] }, { - "group": " ", + "group": "Settings", "pages": [ "notifications/preferences", "notifications/templates-and-sounds", @@ -7105,7 +7109,15 @@ }, { "source": "/extensions/flutter-push-notifications", - "destination": "/notifications/flutter-push-notifications-android" + "destination": "/notifications/flutter-push-notifications" + }, + { + "source": "/notifications/flutter-push-notifications-android", + "destination": "/notifications/flutter-push-notifications" + }, + { + "source": "/notifications/flutter-push-notifications-ios", + "destination": "/notifications/flutter-push-notifications" }, { "source": "/extensions/react-native-push-notifications", @@ -8455,10 +8467,6 @@ "destination": "https://assets.cometchat.io/legacy-docs/ai-chatbots/ai-bots/bots.html", "source": "/ai-chatbots/ai-bots/bots" }, - { - "destination": "https://assets.cometchat.io/legacy-docs/notifications/push-notifications-extension-legacy.html", - "source": "/notifications/flutter-push-notifications" - }, { "destination": "https://assets.cometchat.io/legacy-docs/sdk/ionic-legacy/additional-message-filtering.html", "source": "/sdk/ionic-legacy/additional-message-filtering" diff --git a/notifications.mdx b/notifications.mdx index 0eb31ff02..ada60c136 100644 --- a/notifications.mdx +++ b/notifications.mdx @@ -64,8 +64,7 @@ canonical: "https://cometchat.com/docs" } href="/notifications/ios-apns-push-notifications" horizontal /> } href="/notifications/ios-fcm-push-notifications" horizontal /> - } href="/notifications/flutter-push-notifications-android" horizontal /> - } href="/notifications/flutter-push-notifications-ios" horizontal /> + } href="/notifications/flutter-push-notifications" horizontal /> } href="/notifications/react-native-push-notifications-android" horizontal /> } href="/notifications/react-native-push-notifications-ios" horizontal /> } href="/notifications/web-push-notifications" horizontal /> diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index d5f708f3b..33b0d5a59 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -1,5 +1,5 @@ --- -title: "Android Push Notifications" +title: "Android" description: "Setup FCM and CometChat for message and call push notifications on Android." --- diff --git a/notifications/flutter-push-notifications-android.mdx b/notifications/flutter-push-notifications-android.mdx deleted file mode 100644 index 19a8b32b6..000000000 --- a/notifications/flutter-push-notifications-android.mdx +++ /dev/null @@ -1,403 +0,0 @@ ---- -title: "Flutter Push Notifications (Android)" -description: "Add CometChat push notifications and VoIP calls to a Flutter Android app with the drop-in cometchat_push_notifications SDK and Firebase Cloud Messaging (FCM)." ---- - - - - The drop-in push & VoIP plugin on pub.dev. - - - Source, changelog, and example app. - - - -## What this guide covers - -- Gradle + Firebase wiring (`google-services.json`, Google Services plugin, Kotlin 2.0+). -- Adding the `cometchat_push_notifications` plugin and initializing it. -- Requesting permission and registering the FCM token after login. -- Receiving pushes and letting the SDK render chat notifications and full-screen calls. -- Handling notification taps, incoming-call navigation, badge counts, and OEM permissions. -- Testing and troubleshooting. - - -The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `PNRegistry`, `flutter_callkit_incoming`, and native `MainActivity`/`CallActionReceiver` bridges. Token registration, foreground presentation, notification taps, badge management, and the full incoming-call experience (including the lock-screen call activity) are handled inside the plugin. - - -## How FCM + CometChat + the SDK work together - -- **FCM's role:** Issues the Android registration token and delivers the CometChat push payload to the device as a data message. -- **CometChat's role:** The FCM provider you add in the dashboard stores your Firebase service account. When you register the FCM token after login, CometChat binds it to the logged-in user and sends pushes to FCM on your behalf. -- **The plugin's role:** `cometchat_push_notifications` retrieves the FCM token, registers it with CometChat, and — when you pass an incoming message to `handlePushNotification` — parses the payload and either shows a chat notification or drives the full incoming-call UI. It builds on [`cometchat_sdk`](https://pub.dev/packages/cometchat_sdk), which pub resolves for you. - -**Flow:** Permission (`POST_NOTIFICATIONS` on Android 13+) → Firebase returns the FCM token → after your CometChat user logs in, call `registerToken(PushPlatform.FCM_FLUTTER_ANDROID)` → CometChat sends to FCM → your `firebase_messaging` handler forwards `message.data` to `CometChatPushNotifications.handlePushNotification` → the plugin displays the notification or call → taps route through `onNotificationTap` / `handleIncomingCallLaunch`. - -## Prerequisites - -- Flutter 3.3.0+ / Dart 3.10.8+, and **Kotlin 2.0 or newer** in your Android build. -- Android `minSdkVersion 24`, `compileSdkVersion 36`. -- A Firebase project with an Android app (package name matches your `applicationId`) and Cloud Messaging enabled; `google-services.json` in `android/app/`. -- CometChat app credentials (App ID, Region, Auth Key) with **Push Notifications** enabled and an **FCM provider** configured. -- A physical Android device — full-screen call notifications and background delivery are unreliable on emulators. - - -**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Flutter app wiring. - - -## 1. Store your credentials - -Keep your CometChat App ID, Region, and FCM **Provider ID** (from the Getting Started guide) somewhere your app can read them — for example a small `AppCredentials` class: - -```dart lines -class AppCredentials { - static const appId = "YOUR_APP_ID"; - static const region = "YOUR_REGION"; - static const authKey = "YOUR_AUTH_KEY"; - static const fcmProviderId = "FCM-PROVIDER-ID"; -} -``` - -## 2. Add the plugin and configure Gradle - -### 2.1 Dependencies - -Add the plugin plus Firebase Messaging (used to receive the FCM data messages) to `pubspec.yaml`: - -```yaml lines -dependencies: - cometchat_push_notifications: ^1.0.1 - firebase_core: ^3.9.0 - firebase_messaging: ^15.1.6 -``` - -`cometchat_sdk` is pulled in transitively. Run: - -```bash -flutter pub get -``` - -Then run `flutterfire configure` if you still need to generate `firebase_options.dart`. - -### 2.2 Gradle + Kotlin - -1. Add `google-services.json` to `android/app/`. -2. Apply the Google Services plugin and ensure Kotlin is **2.0+** in `android/settings.gradle.kts`: - -```kotlin lines -plugins { - id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.11.1" apply false - id("org.jetbrains.kotlin.android") version "2.1.0" apply false - id("com.google.gms.google-services") version "4.4.2" apply false -} -``` - -3. Apply the plugins in `android/app/build.gradle.kts`: - -```kotlin lines -plugins { - id("com.android.application") - id("kotlin-android") - id("com.google.gms.google-services") - id("dev.flutter.flutter-gradle-plugin") -} -``` - -4. Set `applicationId` to your package name and keep `minSdk = 24` or higher. - - -You do **not** need to add notification, call, full-screen-intent, or lock-screen permissions to your `AndroidManifest.xml`. The plugin declares everything it needs (including the lock-screen `IncomingCallActivity` and the Decline broadcast receiver), and Gradle merges them into your app automatically. - - -## 3. Initialize the SDK - -### 3.1 `main.dart` - -Initialize Firebase and the plugin before `runApp`, register a background message handler, and expose the `incomingCallMain` entrypoint that the plugin's lock-screen call activity runs. - -```dart lines -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/material.dart'; -import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; - -/// Runs in a background isolate for data messages delivered while the app is -/// terminated or backgrounded. Must initialize the plugin before handling. -@pragma('vm:entry-point') -Future _firebaseBackgroundHandler(RemoteMessage message) async { - await Firebase.initializeApp(); - await CometChatPushNotifications.init( - CometChatNotificationConfig( - appId: AppCredentials.appId, - region: AppCredentials.region, - ), - ); - await CometChatPushNotifications.handlePushNotification(message.data); -} - -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - await Firebase.initializeApp(); - - await CometChatPushNotifications.init( - CometChatNotificationConfig( - appId: AppCredentials.appId, - region: AppCredentials.region, - // Fires when the user accepts from the plugin's default call screen. - onCallAccepted: (call) { - // Start your CometChat call session, then: - // CometChatPushNotifications.setCallConnected(call.sessionId); - }, - // Fires on decline (including from the lock-screen notification action). - onCallDeclined: (call) { - // Reject the call server-side via the CometChat SDK. - }, - ), - ); - - // Forward FCM data messages to the plugin. - FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler); - FirebaseMessaging.onMessage.listen((message) { - CometChatPushNotifications.handlePushNotification(message.data); - }); - - runApp(const MyApp()); -} -``` - -The `CometChatNotificationConfig` also accepts: - -| Field | Default | Purpose | -| --- | --- | --- | -| `customCallScreenBuilder` | `null` | Return your own incoming-call widget. When set, you own accept/reject, dismissing the call notification (`cancelCallNotification`), and popping the screen; `onCallAccepted`/`onCallDeclined` are **not** called. | -| `onCallAccepted` / `onCallDeclined` | `null` | Callbacks for the built-in default call screen. | -| `callActionHandler` | default | Subclass `CometChatCallActionHandler` to intercept mute/speaker/camera (e.g. drive your WebRTC engine). | -| `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | -| `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | - -### 3.2 The `incomingCallMain` entrypoint - -When a call arrives while the device is locked, the plugin launches a dedicated full-screen activity that runs a separate Dart entrypoint named `incomingCallMain`. Define it in `main.dart`. It talks to the plugin's native activity over the `cometchat_locked_call` method channel: - -```dart lines -import 'package:flutter/services.dart'; - -@pragma('vm:entry-point') -void incomingCallMain() { - WidgetsFlutterBinding.ensureInitialized(); - runApp(const _LockedCallApp()); -} - -class _LockedCallApp extends StatefulWidget { - const _LockedCallApp(); - @override - State<_LockedCallApp> createState() => _LockedCallAppState(); -} - -class _LockedCallAppState extends State<_LockedCallApp> { - static const _channel = MethodChannel('cometchat_locked_call'); - CometChatCallDetails? _call; - - @override - void initState() { - super.initState(); - // The native activity calls "reload" for each new/duplicate call. - _channel.setMethodCallHandler((call) async { - if (call.method == 'reload') _loadCall(); - }); - _loadCall(); - } - - Future _loadCall() async { - final d = await _channel.invokeMapMethod('getCallDetails'); - if (d == null) return; - setState(() { - _call = CometChatCallDetails( - sessionId: d['callId'] as String? ?? '', - callerUid: '', - callerName: d['callerName'] as String? ?? 'Unknown', - callerAvatar: d['callerAvatar'] as String?, - callType: (d['isVideo'] as bool? ?? false) - ? CometChatCallType.video - : CometChatCallType.audio, - receiverType: CometChatReceiverType.user, - conversationId: '', - isVideo: d['isVideo'] as bool? ?? false, - ); - }); - } - - @override - Widget build(BuildContext context) { - final call = _call; - return MaterialApp( - debugShowCheckedModeBanner: false, - home: call == null - ? const SizedBox.shrink() - : CometChatDefaultCallScreen( - callDetails: call, - onAccept: () async { - await _channel.invokeMethod('cancelNotification', - {'callId': call.sessionId}); - // Bring your app forward / start the call session here. - await _channel.invokeMethod('finish', {'callId': call.sessionId}); - }, - onDecline: () async { - await _channel.invokeMethod('finish', {'callId': call.sessionId}); - }, - ), - ); - } -} -``` - - -Adapt the accept path to launch your in-call screen. The channel methods (`getCallDetails`, `cancelNotification`, `finish`, and the incoming `reload`) are the contract the plugin's `IncomingCallActivity` exposes. - - -### 3.3 Route call navigation once the app is running - -On your first screen after login (once the navigator is ready), let the plugin replay a cold-start call launch and route subsequent call taps: - -```dart lines -final navigatorKey = GlobalKey(); - -// After the widget tree builds: -CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); -``` - -Pass the same `navigatorKey` to your `MaterialApp`. - -## 4. Request permission and register the FCM token - -Request notification permission early, and register the FCM token **after** your CometChat user logs in (registration binds the token to the session): - -```dart lines -// Ask for POST_NOTIFICATIONS (Android 13+). -await CometChatPushNotifications.requestPermission(); - -// After CometChatUIKit.login(...) / CometChat.login(...) succeeds: -CometChatPushNotifications.registerToken( - PushPlatform.FCM_FLUTTER_ANDROID, - providerId: AppCredentials.fcmProviderId, - onSuccess: (token) => debugPrint('Registered FCM token: $token'), - onError: (e) => debugPrint('Registration failed: ${e.message}'), -); -``` - -Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers the new token with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` if you want to log it. - -## 5. Notification taps and navigation - -When the app is running, subscribe to the tap stream to open the right conversation: - -```dart lines -CometChatPushNotifications.onNotificationTap.listen((details) { - // details.conversationId, details.sender, details.receiverType, ... - // Navigate to your messages screen for this conversation. -}); -``` - -For calls, coordinate with your media logic through the call-event stream: - -```dart lines -CometChatPushNotifications.onCallEvent.listen((event) { - switch (event.type) { - case CometChatCallEventType.accepted: - // Start / join the call session, then setCallConnected(event.sessionId). - break; - case CometChatCallEventType.declined: - case CometChatCallEventType.ended: - case CometChatCallEventType.timeoutEnded: - // Tear down any call state. - break; - case CometChatCallEventType.incoming: - break; - } -}); -``` - -Useful call methods: `setCallConnected(sessionId)` (after media is established), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. - -## 6. OEM permissions for lock-screen calls - -To reliably show a full-screen call over the lock screen — especially on Android 14+ and OEM skins like MIUI/Redmi/POCO — check and request the relevant permissions: - -```dart lines -final perms = await CometChatPushNotifications.checkCallPermissions(); -// perms: { fullScreenIntent, overlay, batteryOptimized } - -if (perms['fullScreenIntent'] == false) { - await CometChatPushNotifications.openFullScreenIntentSettings(); -} -if (perms['overlay'] == false) { - await CometChatPushNotifications.openOverlaySettings(); // MIUI / Xiaomi -} -if (perms['batteryOptimized'] == true) { - await CometChatPushNotifications.openBatteryOptimizationSettings(); -} -// Xiaomi / Oppo / Vivo / Huawei / Samsung autostart: -await CometChatPushNotifications.openAutoStartSettings(); -``` - -On iOS and web these checks return "granted" and the open-settings calls are no-ops. - -## 7. Badge count - -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (the total unread across all conversations). The plugin exposes badge helpers directly — no extra dependency needed. - -### 7.1 Enable unread badge count on the dashboard - -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Enable the **Unread Badge Count** toggle. - -This includes `unreadMessageCount` in every push payload. - -### 7.2 Update and clear the badge - -Set the badge from the payload (for example inside a foreground handler), and clear it when the app opens: - -```dart lines -// From an incoming message payload: -final count = int.tryParse('${message.data['unreadMessageCount'] ?? ''}') ?? 0; -await CometChatPushNotifications.setBadgeCount(count); - -// On app launch / resume: -await CometChatPushNotifications.setBadgeCount(0); -await CometChatPushNotifications.clearAll(); // dismiss stale banners -``` - -Add a `WidgetsBindingObserver` to your root widget and clear the badge in `initState` and on `AppLifecycleState.resumed`. - -## 8. 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: no banner unless `showInAppNotifications: true` (and you're not already in that chat). - - Background: an FCM notification appears; tapping opens the right conversation via `onNotificationTap`. -3. Force-quit the app, send another message, tap the notification, and confirm it navigates to the conversation. -4. Trigger an incoming CometChat call and confirm: - - The full-screen call UI shows the caller with Accept/Decline, even on the lock screen. - - Accepting starts the call session and dismisses the notification. - - Declining from the notification rejects the call server-side (via your `onCallDeclined`). -5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. - -## 9. Troubleshooting - -| Symptom | Quick checks | -| --- | --- | -| No notifications received | Confirm `google-services.json` is in `android/app/`, the package name matches Firebase, notification permission is granted (Android 13+), and your `firebase_messaging` handlers call `handlePushNotification(message.data)`. | -| Build fails after adding the plugin | Ensure Kotlin is **2.0+** in `android/settings.gradle.kts` and `minSdk = 24`. | -| Token registration errors | Verify `providerId` matches the FCM provider ID exactly and that `registerToken` runs **after** login. | -| Full-screen call UI not showing | Check `checkCallPermissions()` and request full-screen-intent / overlay / battery / autostart via the helpers in section 7. | -| Lock-screen call is a black screen | Make sure `incomingCallMain` is defined in `main.dart` and annotated with `@pragma('vm:entry-point')`. | -| Tapping a call from a killed app does nothing | Call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready, and pass the same key to `MaterialApp`. | diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx deleted file mode 100644 index 9e3be4717..000000000 --- a/notifications/flutter-push-notifications-ios.mdx +++ /dev/null @@ -1,303 +0,0 @@ ---- -title: "Flutter Push Notifications (iOS)" -description: "Add CometChat push notifications and VoIP calls to a Flutter iOS app with the drop-in cometchat_push_notifications SDK, APNs, and PushKit." ---- - - - - The drop-in push & VoIP plugin on pub.dev. - - - Source, changelog, and example app. - - - -## What this guide covers - -- Adding the `cometchat_push_notifications` plugin and initializing it. -- Requesting permission and registering the APNs and VoIP tokens after login. -- Handling notification taps, incoming calls via PushKit/CallKit, and badge counts. -- Testing and troubleshooting. - - -The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `CometChatPushRegistry`, `flutter_callkit_incoming`, and a custom native `AppDelegate` PushKit/CallKit bridge. The plugin handles PushKit registration and CallKit reporting natively — your `AppDelegate` stays minimal. - - -## How APNs + CometChat + the SDK work together - -- **APNs is primary on iOS:** Apple issues the device (APNs) token and, via PushKit, the VoIP token. APNs delivers chat pushes; PushKit delivers call pushes. -- **CometChat's role:** The APNs and APNs VoIP providers you create in the dashboard hold your Apple credentials. When you register the tokens after login, CometChat binds them to the logged-in user and sends pushes on your behalf. -- **The plugin's role:** `cometchat_push_notifications` retrieves the APNs and VoIP tokens, registers them with CometChat, and drives the incoming-call experience through CallKit. `cometchat_sdk` is resolved for you. - -**Flow:** Permission prompt → APNs + VoIP tokens issued → after your CometChat user logs in, register both tokens with `registerToken` → CometChat sends to APNs/PushKit → APNs delivers the chat alert (iOS displays it) and PushKit delivers the call (the plugin presents CallKit) → taps and call actions surface on the plugin's streams. - - -On iOS you don't need Firebase. This guide uses **pure APNs + PushKit**. If you specifically want to register FCM tokens on iOS, add an FCM provider and `firebase_messaging` as well, but that's optional. - - -## Prerequisites - -- Flutter 3.3.0+ / Dart 3.10.8+; iOS 13.0+ (set the Podfile platform to **iOS 14.0** for VoIP/CallKit). -- An Apple Developer account with Push Notifications and Background Modes (Remote notifications + Voice over IP) enabled for your bundle ID, and an APNs Auth Key (`.p8`). -- CometChat app credentials (App ID, Region, Auth Key) with **Push Notifications** enabled and an **APNs provider** (plus an **APNs VoIP** provider for calls). -- A physical iPhone or iPad — simulators cannot receive VoIP pushes or present CallKit UI. - - -**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the Flutter app wiring. - - -## 1. Store your credentials - -Keep your CometChat App ID, Region, and the **APNs** / **APNs VoIP** Provider IDs (from the Getting Started guide) somewhere your app can read them: - -```dart lines -class AppCredentials { - static const appId = "YOUR_APP_ID"; - static const region = "YOUR_REGION"; - static const authKey = "YOUR_AUTH_KEY"; - static const apnProviderId = "APNS-PROVIDER-ID"; - static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; -} -``` - -## 2. Add the plugin and configure iOS - -### 2.1 Dependencies - -Add the plugin to `pubspec.yaml`: - -```yaml lines -dependencies: - cometchat_push_notifications: ^1.0.1 -``` - -`cometchat_sdk` is pulled in transitively. Then: - -```bash -flutter pub get -cd ios && pod install && cd .. -``` - -### 2.2 Podfile - -Set the deployment target in `ios/Podfile`: - -```ruby -platform :ios, '14.0' -``` - -### 2.3 Capabilities and Info.plist - -1. Open `ios/Runner.xcworkspace` in Xcode. -2. Under **Signing & Capabilities**, add **Push Notifications** and **Background Modes** → check **Remote notifications** and **Voice over IP**. -3. Set the development team that owns the APNs/VoIP key. - - - Enable Push Notifications and Background Modes for APNs - - -Add the background modes and permission strings to `ios/Runner/Info.plist`: - -```xml lines -UIBackgroundModes - - voip - remote-notification - -NSMicrophoneUsageDescription -Microphone access is required for calls. -NSCameraUsageDescription -Camera access is required for video calls. -``` - -### 2.4 AppDelegate — forward the APNs token - -The plugin handles PushKit and CallKit natively, so no PushKit or CallKit code is needed in your `AppDelegate`. Forward the APNs device token to the underlying plugin so it can be registered: - -```swift lines -import Flutter -import UIKit -import notification_voip_plugin - -@main -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } - - override func application( - _ application: UIApplication, - didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data - ) { - NotificationVoipPlugin.shared?.setAPNsToken(deviceToken) - } -} -``` - -## 3. Initialize the SDK - -In `main.dart`, initialize the plugin before `runApp`. Provide a call screen and callbacks as needed: - -```dart lines -import 'package:flutter/material.dart'; -import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; - -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - - await CometChatPushNotifications.init( - CometChatNotificationConfig( - appId: AppCredentials.appId, - region: AppCredentials.region, - onCallAccepted: (call) { - // Start your CometChat call session, then: - // CometChatPushNotifications.setCallConnected(call.sessionId); - }, - onCallDeclined: (call) { - // Reject the call server-side via the CometChat SDK. - }, - ), - ); - - runApp(const MyApp()); -} -``` - -The `CometChatNotificationConfig` also accepts: - -| Field | Default | Purpose | -| --- | --- | --- | -| `customCallScreenBuilder` | `null` | Return your own incoming-call widget. When set, you own accept/reject, dismissing the call notification, and popping the screen; `onCallAccepted`/`onCallDeclined` are **not** called. | -| `onCallAccepted` / `onCallDeclined` | `null` | Callbacks for the built-in default call screen. | -| `callActionHandler` | default | Subclass `CometChatCallActionHandler` to intercept mute/speaker/camera. | -| `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | -| `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | - -Once the navigator is ready on your first screen after login, replay any cold-start call launch and route subsequent call taps: - -```dart lines -final navigatorKey = GlobalKey(); - -// After the widget tree builds: -CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); -``` - -Pass the same `navigatorKey` to your `MaterialApp`. - -## 4. Request permission and register tokens - -Request permission early, and register **both** the APNs device token and the VoIP token **after** your CometChat user logs in. Each call includes both tokens in the server request, so register them with their matching provider IDs: - -```dart lines -await CometChatPushNotifications.requestPermission(); - -// After CometChatUIKit.login(...) / CometChat.login(...) succeeds: - -// APNs device token (chat/alert pushes) -CometChatPushNotifications.registerToken( - PushPlatform.APNS_FLUTTER_DEVICE, - providerId: AppCredentials.apnProviderId, - onSuccess: (token) => debugPrint('APNs registered: $token'), - onError: (e) => debugPrint('APNs failed: ${e.message}'), -); - -// VoIP (PushKit) token — required for calls -CometChatPushNotifications.registerToken( - PushPlatform.APNS_FLUTTER_VOIP, - providerId: AppCredentials.apnVoipProviderId, - onSuccess: (token) => debugPrint('VoIP registered: $token'), - onError: (e) => debugPrint('VoIP failed: ${e.message}'), -); -``` - -Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` to log it. - -## 5. Notification taps and call events - -Subscribe to the streams to navigate and coordinate call state: - -```dart lines -CometChatPushNotifications.onNotificationTap.listen((details) { - // details.conversationId, details.sender, details.receiverType, ... - // Navigate to your messages screen for this conversation. -}); - -CometChatPushNotifications.onCallEvent.listen((event) { - switch (event.type) { - case CometChatCallEventType.accepted: - // Start / join the call, then setCallConnected(event.sessionId). - break; - case CometChatCallEventType.declined: - case CometChatCallEventType.ended: - case CometChatCallEventType.timeoutEnded: - // Tear down any call state. - break; - case CometChatCallEventType.incoming: - break; - } -}); -``` - -Useful call methods: `setCallConnected(sessionId)` (after media is established — critical for connecting calls from a terminated state), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. - - -**Cold-start VoIP handling:** when the app is killed and a VoIP push arrives, the plugin presents CallKit natively via PushKit before the Flutter engine is ready. When the user answers, `handleIncomingCallLaunch` routes to your call screen and the `onCallEvent` / `onCallAccepted` handlers fire — set up your CometChat call session there and call `setCallConnected`. - - -## 6. Badge count - -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field. On iOS, with pure APNs the badge is handled **server-side**: CometChat sets `aps.badge` in the payload and iOS updates the app icon automatically — no client code required to display it. - -### 6.1 Enable unread badge count on the dashboard - -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Enable the **Unread Badge Count** toggle. - -This sets `aps.badge` on every push. - -### 6.2 Clear the badge when the app opens - -Clear the badge and dismiss stale notifications on launch and every resume: - -```dart lines -await CometChatPushNotifications.setBadgeCount(0); -await CometChatPushNotifications.clearAll(); -``` - -Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and on `AppLifecycleState.resumed`. If you also register FCM tokens on iOS and show local notifications yourself, use `setBadgeCount(count)` with the payload's `unreadMessageCount` instead of relying on the server value. - -## 7. Testing checklist - -1. Run on a physical device in debug. Grant notification, microphone, and camera permissions when prompted. -2. Send a message from another user: - - Foreground: no banner unless `showInAppNotifications: true` (and you're not already in that chat). - - Background: an APNs alert appears; tapping opens the right conversation via `onNotificationTap`. -3. Force-quit the app, send another message, tap the notification, and confirm it navigates to the conversation. -4. Trigger an incoming CometChat call and confirm: - - CallKit shows the caller name, call type, and Accept/Decline. - - Accepting starts the call session (via `onCallAccepted`) and dismisses the CallKit UI when the call ends. - - Declining cleans up both CallKit and your call state. -5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. - -## 8. Troubleshooting - -| Symptom | Quick checks | -| --- | --- | -| No VoIP pushes | Ensure Push Notifications + Background Modes (Voice over IP) are enabled, the bundle ID matches the CometChat APNs VoIP provider, and you registered `PushPlatform.APNS_FLUTTER_VOIP`. | -| APNs token never registers | Confirm the `AppDelegate` forwards the token via `NotificationVoipPlugin.shared?.setAPNsToken(deviceToken)` and that permission was granted. | -| Token registration errors | Verify `apnProviderId` / `apnVoipProviderId` match the dashboard exactly and that `registerToken` runs **after** login. | -| CallKit UI never dismisses | Call `endCall(sessionId)` (or `endAllCalls()`) when your call session ends so the plugin reports it to CallKit. | -| Notification taps ignored | Subscribe to `onNotificationTap`, and call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready with the same key passed to `MaterialApp`. | -| Badge not updating | Enable the **Unread Badge Count** toggle on the dashboard; with pure APNs the badge is set server-side via `aps.badge`. | diff --git a/notifications/flutter-push-notifications.mdx b/notifications/flutter-push-notifications.mdx new file mode 100644 index 000000000..4287b4fc4 --- /dev/null +++ b/notifications/flutter-push-notifications.mdx @@ -0,0 +1,501 @@ +--- +title: "Flutter" +description: "Add CometChat push notifications and VoIP calls to a Flutter app (Android + iOS) with the drop-in cometchat_push_notifications SDK." +--- + + + + The drop-in push & VoIP plugin on pub.dev. + + + Source, changelog, and example app. + + + +## What this guide covers + +- Adding the `cometchat_push_notifications` plugin and initializing it. +- Platform wiring: Gradle/Firebase on Android, Podfile/capabilities/AppDelegate on iOS. +- Requesting permission and registering tokens (FCM on Android, APNs + VoIP on iOS) after login. +- Receiving pushes and letting the SDK render chat notifications and full-screen calls. +- Handling notification taps, incoming-call navigation, badge counts, and Android OEM permissions. +- Testing and troubleshooting. + + +The `cometchat_push_notifications` plugin replaces the previous approach of copying the UI Kit sample's `lib/notifications` stack and hand-wiring `PNRegistry` / `CometChatPushRegistry`, `flutter_callkit_incoming`, and native `MainActivity` / `AppDelegate` bridges. Token registration, foreground presentation, notification taps, badge management, and the full incoming-call experience (the Android lock-screen call activity and iOS CallKit) are handled inside the plugin. + + +## How it works + +- **Android (FCM):** Firebase issues the registration token and delivers the CometChat payload as a data message. Your `firebase_messaging` handler forwards `message.data` to `handlePushNotification`, and the plugin shows the notification or full-screen call. +- **iOS (APNs + PushKit):** Apple issues the APNs device token (chat alerts) and the VoIP token (calls). APNs alerts are shown by the system; VoIP pushes are presented through CallKit by the plugin. +- **CometChat's role:** The providers you add in the dashboard bind your registered tokens to the logged-in user so CometChat can route pushes on your behalf. +- **The plugin's role:** `cometchat_push_notifications` retrieves the tokens, registers them with CometChat, parses payloads, and drives the call UI. It builds on [`cometchat_sdk`](https://pub.dev/packages/cometchat_sdk), which pub resolves for you. + +## Prerequisites + +- The providers, Firebase project, and Apple/APNs credentials from **[Getting Started](/notifications/push-getting-started)** (this guide assumes those are done). +- Flutter 3.3.0+ / Dart 3.10.8+. +- **Android:** Kotlin **2.0+**, `minSdkVersion 24`, `compileSdkVersion 36`. +- **iOS:** iOS 13.0+ (set the Podfile platform to **14.0** for VoIP/CallKit). +- A physical device — background delivery, full-screen calls, and VoIP pushes are unreliable on emulators/simulators. + + +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your providers (FCM for Android, APNs + APNs VoIP for iOS), and finish the Firebase/Apple setup. This guide covers only the Flutter app wiring. + + +## 1. Store your credentials + +Keep the values from Getting Started somewhere your app can read them. Only the fields for the platforms you ship are needed: + +```dart lines +class AppCredentials { + static const appId = "YOUR_APP_ID"; + static const region = "YOUR_REGION"; + static const authKey = "YOUR_AUTH_KEY"; + + // Android + static const fcmProviderId = "FCM-PROVIDER-ID"; + + // iOS + static const apnProviderId = "APNS-PROVIDER-ID"; + static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; +} +``` + +## 2. Add the plugin and configure the platform + +Add the plugin to `pubspec.yaml`. On Android also add Firebase Messaging (used to receive the FCM data messages): + +```yaml lines +dependencies: + cometchat_push_notifications: ^1.0.1 + # Android only — to receive FCM data messages: + firebase_core: ^3.9.0 + firebase_messaging: ^15.1.6 +``` + +`cometchat_sdk` is pulled in transitively. Run `flutter pub get`. + + + + With `google-services.json` already in `android/app/` (from [Getting Started](/notifications/push-getting-started)): + + 1. Apply the Google Services plugin and ensure Kotlin is **2.0+** in `android/settings.gradle.kts`: + + ```kotlin lines + plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false + id("com.google.gms.google-services") version "4.4.2" apply false + } + ``` + + 2. Apply the plugins in `android/app/build.gradle.kts`: + + ```kotlin lines + plugins { + id("com.android.application") + id("kotlin-android") + id("com.google.gms.google-services") + id("dev.flutter.flutter-gradle-plugin") + } + ``` + + 3. Set `applicationId` to your package name and keep `minSdk = 24` or higher. Run `flutterfire configure` if you still need `firebase_options.dart`. + + + You do **not** need to add notification, call, full-screen-intent, or lock-screen permissions to your `AndroidManifest.xml`. The plugin declares everything it needs (including the lock-screen `IncomingCallActivity` and the Decline broadcast receiver), and Gradle merges them into your app automatically. + + + + 1. Set the deployment target in `ios/Podfile`, then install pods: + + ```ruby + platform :ios, '14.0' + ``` + + ```bash + cd ios && pod install && cd .. + ``` + + 2. Open `ios/Runner.xcworkspace` in Xcode and set the development team that owns the APNs/VoIP key. (The **Push Notifications** / **Background Modes** capabilities and `Info.plist` usage strings come from [Getting Started](/notifications/push-getting-started).) + + 3. The plugin handles PushKit and CallKit natively, so no PushKit/CallKit code is needed in your `AppDelegate` — but you must forward the APNs device token so it can be registered: + + ```swift lines + import Flutter + import UIKit + import notification_voip_plugin + + @main + @objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + override func application( + _ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data + ) { + NotificationVoipPlugin.shared?.setAPNsToken(deviceToken) + } + } + ``` + + + +## 3. Initialize the SDK + +Initialize the plugin before `runApp`. On Android you also initialize Firebase and forward FCM messages to the plugin (including a background isolate handler). + + + + ```dart lines + import 'package:firebase_core/firebase_core.dart'; + import 'package:firebase_messaging/firebase_messaging.dart'; + import 'package:flutter/material.dart'; + import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; + + /// Runs in a background isolate for data messages delivered while the app is + /// terminated or backgrounded. Must initialize the plugin before handling. + @pragma('vm:entry-point') + Future _firebaseBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(); + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + ), + ); + await CometChatPushNotifications.handlePushNotification(message.data); + } + + Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + onCallAccepted: (call) { + // Start your CometChat call session, then: + // CometChatPushNotifications.setCallConnected(call.sessionId); + }, + onCallDeclined: (call) { + // Reject the call server-side via the CometChat SDK. + }, + ), + ); + + // Forward FCM data messages to the plugin. + FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler); + FirebaseMessaging.onMessage.listen((message) { + CometChatPushNotifications.handlePushNotification(message.data); + }); + + runApp(const MyApp()); + } + ``` + + + ```dart lines + import 'package:flutter/material.dart'; + import 'package:cometchat_push_notifications/cometchat_push_notifications.dart'; + + Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await CometChatPushNotifications.init( + CometChatNotificationConfig( + appId: AppCredentials.appId, + region: AppCredentials.region, + onCallAccepted: (call) { + // Start your CometChat call session, then: + // CometChatPushNotifications.setCallConnected(call.sessionId); + }, + onCallDeclined: (call) { + // Reject the call server-side via the CometChat SDK. + }, + ), + ); + + runApp(const MyApp()); + } + ``` + + + +The `CometChatNotificationConfig` also accepts: + +| Field | Default | Purpose | +| --- | --- | --- | +| `customCallScreenBuilder` | `null` | Return your own incoming-call widget. When set, you own accept/reject, dismissing the call notification (`cancelCallNotification`), and popping the screen; `onCallAccepted`/`onCallDeclined` are **not** called. | +| `onCallAccepted` / `onCallDeclined` | `null` | Callbacks for the built-in default call screen. | +| `callActionHandler` | default | Subclass `CometChatCallActionHandler` to intercept mute/speaker/camera (e.g. drive your WebRTC engine). | +| `showInAppNotifications` | `false` | Show chat pushes as in-app banners when the app is foregrounded. | +| `showInAppVoIP` | `false` | Show the incoming-call UI for pushes received in the foreground (leave `false` if your UI Kit's `CallEventService` already handles foreground calls over WebSocket). | + +Once the navigator is ready on your first screen after login, replay any cold-start call launch and route subsequent call taps: + +```dart lines +final navigatorKey = GlobalKey(); + +// After the widget tree builds: +CometChatPushNotifications.handleIncomingCallLaunch(navigatorKey); +``` + +Pass the same `navigatorKey` to your `MaterialApp`. + +### Android: lock-screen call entrypoint + +When a call arrives while the device is locked, the plugin launches a dedicated full-screen activity that runs a separate Dart entrypoint named `incomingCallMain`. Define it in `main.dart`. It talks to the plugin's native activity over the `cometchat_locked_call` method channel: + +```dart lines +import 'package:flutter/services.dart'; + +@pragma('vm:entry-point') +void incomingCallMain() { + WidgetsFlutterBinding.ensureInitialized(); + runApp(const _LockedCallApp()); +} + +class _LockedCallApp extends StatefulWidget { + const _LockedCallApp(); + @override + State<_LockedCallApp> createState() => _LockedCallAppState(); +} + +class _LockedCallAppState extends State<_LockedCallApp> { + static const _channel = MethodChannel('cometchat_locked_call'); + CometChatCallDetails? _call; + + @override + void initState() { + super.initState(); + // The native activity calls "reload" for each new/duplicate call. + _channel.setMethodCallHandler((call) async { + if (call.method == 'reload') _loadCall(); + }); + _loadCall(); + } + + Future _loadCall() async { + final d = await _channel.invokeMapMethod('getCallDetails'); + if (d == null) return; + setState(() { + _call = CometChatCallDetails( + sessionId: d['callId'] as String? ?? '', + callerUid: '', + callerName: d['callerName'] as String? ?? 'Unknown', + callerAvatar: d['callerAvatar'] as String?, + callType: (d['isVideo'] as bool? ?? false) + ? CometChatCallType.video + : CometChatCallType.audio, + receiverType: CometChatReceiverType.user, + conversationId: '', + isVideo: d['isVideo'] as bool? ?? false, + ); + }); + } + + @override + Widget build(BuildContext context) { + final call = _call; + return MaterialApp( + debugShowCheckedModeBanner: false, + home: call == null + ? const SizedBox.shrink() + : CometChatDefaultCallScreen( + callDetails: call, + onAccept: () async { + await _channel.invokeMethod('cancelNotification', + {'callId': call.sessionId}); + // Bring your app forward / start the call session here. + await _channel.invokeMethod('finish', {'callId': call.sessionId}); + }, + onDecline: () async { + await _channel.invokeMethod('finish', {'callId': call.sessionId}); + }, + ), + ); + } +} +``` + + +Adapt the accept path to launch your in-call screen. The channel methods (`getCallDetails`, `cancelNotification`, `finish`, and the incoming `reload`) are the contract the plugin's `IncomingCallActivity` exposes. + + +## 4. Request permission and register tokens + +Request permission early, and register tokens **after** your CometChat user logs in (registration binds the token to the session): + +```dart lines +await CometChatPushNotifications.requestPermission(); +``` + + + + ```dart lines + // After CometChatUIKit.login(...) / CometChat.login(...) succeeds: + CometChatPushNotifications.registerToken( + PushPlatform.FCM_FLUTTER_ANDROID, + providerId: AppCredentials.fcmProviderId, + onSuccess: (token) => debugPrint('Registered FCM token: $token'), + onError: (e) => debugPrint('Registration failed: ${e.message}'), + ); + ``` + + + Register **both** the APNs device token and the VoIP token. Each call includes both tokens in the server request, so register them with their matching provider IDs: + + ```dart lines + // After CometChatUIKit.login(...) / CometChat.login(...) succeeds: + + // APNs device token (chat/alert pushes) + CometChatPushNotifications.registerToken( + PushPlatform.APNS_FLUTTER_DEVICE, + providerId: AppCredentials.apnProviderId, + onSuccess: (token) => debugPrint('APNs registered: $token'), + onError: (e) => debugPrint('APNs failed: ${e.message}'), + ); + + // VoIP (PushKit) token — required for calls + CometChatPushNotifications.registerToken( + PushPlatform.APNS_FLUTTER_VOIP, + providerId: AppCredentials.apnVoipProviderId, + onSuccess: (token) => debugPrint('VoIP registered: $token'), + onError: (e) => debugPrint('VoIP failed: ${e.message}'), + ); + ``` + + + +Token refreshes are handled for you — the plugin listens for `onTokenRefresh` and automatically re-registers the new token with CometChat. Subscribe to `CometChatPushNotifications.onTokenRefresh` if you want to log it. + +## 5. Notification taps and call events + +Subscribe to the streams to navigate and coordinate call state (same on both platforms): + +```dart lines +CometChatPushNotifications.onNotificationTap.listen((details) { + // details.conversationId, details.sender, details.receiverType, ... + // Navigate to your messages screen for this conversation. +}); + +CometChatPushNotifications.onCallEvent.listen((event) { + switch (event.type) { + case CometChatCallEventType.accepted: + // Start / join the call session, then setCallConnected(event.sessionId). + break; + case CometChatCallEventType.declined: + case CometChatCallEventType.ended: + case CometChatCallEventType.timeoutEnded: + // Tear down any call state. + break; + case CometChatCallEventType.incoming: + break; + } +}); +``` + +Useful call methods: `setCallConnected(sessionId)` (after media is established — critical for connecting calls from a terminated state), `endCall(sessionId)`, `endAllCalls()` (on logout), and `getActiveCallIds()`. + + +**Cold-start VoIP handling (iOS):** when the app is killed and a VoIP push arrives, the plugin presents CallKit natively via PushKit before the Flutter engine is ready. When the user answers, `handleIncomingCallLaunch` routes to your call screen and the `onCallEvent` / `onCallAccepted` handlers fire — set up your CometChat call session there and call `setCallConnected`. + + +## 6. Android: OEM permissions for lock-screen calls + +To reliably show a full-screen call over the lock screen — especially on Android 14+ and OEM skins like MIUI/Redmi/POCO — check and request the relevant permissions: + +```dart lines +final perms = await CometChatPushNotifications.checkCallPermissions(); +// perms: { fullScreenIntent, overlay, batteryOptimized } + +if (perms['fullScreenIntent'] == false) { + await CometChatPushNotifications.openFullScreenIntentSettings(); +} +if (perms['overlay'] == false) { + await CometChatPushNotifications.openOverlaySettings(); // MIUI / Xiaomi +} +if (perms['batteryOptimized'] == true) { + await CometChatPushNotifications.openBatteryOptimizationSettings(); +} +// Xiaomi / Oppo / Vivo / Huawei / Samsung autostart: +await CometChatPushNotifications.openAutoStartSettings(); +``` + +On iOS and web these checks return "granted" and the open-settings calls are no-ops. + +## 7. Badge count + +CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (the total unread across all conversations). Enable it once on the dashboard: + +1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. +2. Enable the **Unread Badge Count** toggle. + + + + Set the badge from the payload (for example inside a foreground handler) using the plugin's badge helper — no extra dependency needed: + + ```dart lines + final count = int.tryParse('${message.data['unreadMessageCount'] ?? ''}') ?? 0; + await CometChatPushNotifications.setBadgeCount(count); + ``` + + + With pure APNs the badge is handled **server-side**: CometChat sets `aps.badge` in the payload and iOS updates the app icon automatically — no client code required to display it. + + + +Clear the badge and dismiss stale notifications when the app opens and every resume: + +```dart lines +await CometChatPushNotifications.setBadgeCount(0); +await CometChatPushNotifications.clearAll(); +``` + +Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and on `AppLifecycleState.resumed`. + +## 8. Testing checklist + +1. Run on a physical device. Grant notification, microphone, and camera permissions when prompted (Android 13+ requires `POST_NOTIFICATIONS`). +2. Send a message from another user: + - Foreground: no banner unless `showInAppNotifications: true` (and you're not already in that chat). + - Background: a notification appears; tapping opens the right conversation via `onNotificationTap`. +3. Force-quit the app, send another message, tap the notification, and confirm it navigates to the conversation. +4. Trigger an incoming CometChat call and confirm: + - The full-screen call UI (Android) / CallKit (iOS) shows the caller with Accept/Decline, even on the lock screen. + - Accepting starts the call session (via `onCallAccepted`) and dismisses the notification when the call ends. + - Declining rejects the call server-side (via your `onCallDeclined`). +5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes. + +## 9. Troubleshooting + +| Symptom | Platform | Quick checks | +| --- | --- | --- | +| No notifications received | Android | Confirm `google-services.json` is in `android/app/`, the package name matches Firebase, permission is granted (Android 13+), and your `firebase_messaging` handlers call `handlePushNotification(message.data)`. | +| Build fails after adding the plugin | Android | Ensure Kotlin is **2.0+** in `android/settings.gradle.kts` and `minSdk = 24`. | +| Full-screen call UI not showing | Android | Check `checkCallPermissions()` and request full-screen-intent / overlay / battery / autostart via the helpers in section 6. | +| Lock-screen call is a black screen | Android | Make sure `incomingCallMain` is defined in `main.dart` and annotated with `@pragma('vm:entry-point')`. | +| No VoIP pushes | iOS | Ensure Push Notifications + Background Modes (Voice over IP) are enabled, the bundle ID matches the CometChat APNs VoIP provider, and you registered `PushPlatform.APNS_FLUTTER_VOIP`. | +| APNs token never registers | iOS | Confirm the `AppDelegate` forwards the token via `NotificationVoipPlugin.shared?.setAPNsToken(deviceToken)` and that permission was granted. | +| CallKit UI never dismisses | iOS | Call `endCall(sessionId)` (or `endAllCalls()`) when your call session ends so the plugin reports it to CallKit. | +| Token registration errors | Both | Verify the provider IDs match the dashboard exactly and that `registerToken` runs **after** login. | +| Notification/call taps ignored | Both | Subscribe to `onNotificationTap`, and call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready with the same key passed to `MaterialApp`. | diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx index 5a2728d6e..c348128dc 100644 --- a/notifications/ios-apns-push-notifications.mdx +++ b/notifications/ios-apns-push-notifications.mdx @@ -1,5 +1,5 @@ --- -title: "iOS APNs Push Notifications" +title: "iOS APNs" description: "Implement APNs push notifications with CometChat UIKit for iOS, including CallKit integration for VoIP calls." --- diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx index 3dec0b426..47f4c3c28 100644 --- a/notifications/ios-fcm-push-notifications.mdx +++ b/notifications/ios-fcm-push-notifications.mdx @@ -1,5 +1,5 @@ --- -title: "iOS FCM Push Notifications" +title: "iOS FCM" description: "Guide to integrating Firebase Cloud Messaging (FCM) push notifications in iOS apps using CometChat." --- diff --git a/notifications/push-getting-started.mdx b/notifications/push-getting-started.mdx index bb89db7f1..f1ed69a21 100644 --- a/notifications/push-getting-started.mdx +++ b/notifications/push-getting-started.mdx @@ -87,12 +87,8 @@ UI Kit implementation UI Kit implementation -} href="/notifications/flutter-push-notifications-android"> -UI Kit implementation - - -} href="/notifications/flutter-push-notifications-ios"> -UI Kit implementation +} href="/notifications/flutter-push-notifications"> +Android + iOS } href="/notifications/react-native-push-notifications-android"> diff --git a/notifications/react-native-push-notifications-android.mdx b/notifications/react-native-push-notifications-android.mdx index 236316a07..2b17ddee0 100644 --- a/notifications/react-native-push-notifications-android.mdx +++ b/notifications/react-native-push-notifications-android.mdx @@ -1,5 +1,5 @@ --- -title: "React Native Push Notification (Android)" +title: "React Native (Android)" description: "Bring the SampleAppWithPushNotifications experience—FCM + VoIP calls—into any React Native project using CometChat UI Kit." --- diff --git a/notifications/react-native-push-notifications-ios.mdx b/notifications/react-native-push-notifications-ios.mdx index 34bc18625..7a0f56551 100644 --- a/notifications/react-native-push-notifications-ios.mdx +++ b/notifications/react-native-push-notifications-ios.mdx @@ -1,5 +1,5 @@ --- -title: "React Native Push Notifications (iOS)" +title: "React Native (iOS)" description: "Bring the SampleAppWithPushNotifications experience—APNs + VoIP—into any React Native project using CometChat UI Kit." --- diff --git a/notifications/web-push-notifications.mdx b/notifications/web-push-notifications.mdx index e69a56736..124482e05 100644 --- a/notifications/web-push-notifications.mdx +++ b/notifications/web-push-notifications.mdx @@ -1,5 +1,5 @@ --- -title: "Web Push Notifications" +title: "Web" description: "Set up FCM web push for CometChat React UI Kit—service worker, VAPID keys, token registration, and foreground/background handlers." --- From 0e38f6bea33caa7c5b47889a79e5e2607dbf33f6 Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Tue, 28 Jul 2026 13:21:12 +0530 Subject: [PATCH 06/11] Updated version --- ui-kit/flutter/call-features.mdx | 2 +- ui-kit/flutter/getting-started.mdx | 4 ++-- ui-kit/flutter/upgrading-from-v5.mdx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-kit/flutter/call-features.mdx b/ui-kit/flutter/call-features.mdx index 503c018ad..1e871bdfe 100644 --- a/ui-kit/flutter/call-features.mdx +++ b/ui-kit/flutter/call-features.mdx @@ -15,7 +15,7 @@ Add the dependency to your `pubspec.yaml`: ```yaml pubspec.yaml dependencies: - cometchat_chat_uikit: ^6.0.5 + cometchat_chat_uikit: ^6.1.0 ``` *** diff --git a/ui-kit/flutter/getting-started.mdx b/ui-kit/flutter/getting-started.mdx index 42fd68740..a2e729524 100644 --- a/ui-kit/flutter/getting-started.mdx +++ b/ui-kit/flutter/getting-started.mdx @@ -43,7 +43,7 @@ To get started, create a new flutter application project. ```yaml pubspec.yaml dependencies: - cometchat_chat_uikit: ^6.0.5 + cometchat_chat_uikit: ^6.1.0 ``` Final `pubspec.yaml` @@ -63,7 +63,7 @@ dependencies: flutter: sdk: flutter - cometchat_chat_uikit: ^6.0.5 + cometchat_chat_uikit: ^6.1.0 cupertino_icons: ^1.0.8 diff --git a/ui-kit/flutter/upgrading-from-v5.mdx b/ui-kit/flutter/upgrading-from-v5.mdx index 13d5b79a4..a40de3b26 100644 --- a/ui-kit/flutter/upgrading-from-v5.mdx +++ b/ui-kit/flutter/upgrading-from-v5.mdx @@ -65,7 +65,7 @@ MessageTemplateUtils.getAllMessageTemplates(); ```yaml pubspec.yaml dependencies: - cometchat_chat_uikit: ^6.0.5 + cometchat_chat_uikit: ^6.1.0 ``` ### 5. State Management Migration (Advanced) From 6d67f35e6c8358246410dc6f9a66770aeab66852 Mon Sep 17 00:00:00 2001 From: "Raj Shah(CometChat)" Date: Tue, 28 Jul 2026 13:41:26 +0530 Subject: [PATCH 07/11] Added resources in Flutter --- notifications/flutter-push-notifications.mdx | 36 +++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/notifications/flutter-push-notifications.mdx b/notifications/flutter-push-notifications.mdx index 4287b4fc4..4abc5860a 100644 --- a/notifications/flutter-push-notifications.mdx +++ b/notifications/flutter-push-notifications.mdx @@ -3,23 +3,6 @@ title: "Flutter" description: "Add CometChat push notifications and VoIP calls to a Flutter app (Android + iOS) with the drop-in cometchat_push_notifications SDK." --- - - - The drop-in push & VoIP plugin on pub.dev. - - - Source, changelog, and example app. - - - ## What this guide covers - Adding the `cometchat_push_notifications` plugin and initializing it. @@ -499,3 +482,22 @@ Add a `WidgetsBindingObserver` to your root widget and clear in `initState` and | CallKit UI never dismisses | iOS | Call `endCall(sessionId)` (or `endAllCalls()`) when your call session ends so the plugin reports it to CallKit. | | Token registration errors | Both | Verify the provider IDs match the dashboard exactly and that `registerToken` runs **after** login. | | Notification/call taps ignored | Both | Subscribe to `onNotificationTap`, and call `handleIncomingCallLaunch(navigatorKey)` after the navigator is ready with the same key passed to `MaterialApp`. | + +## Resources + + + + The drop-in push & VoIP plugin on pub.dev. + + + Source, changelog, and example app. + + From bf0121c8ded455623b8ccdba1cdf509b9a11b9d4 Mon Sep 17 00:00:00 2001 From: Ashfaaq Ali Date: Tue, 28 Jul 2026 15:37:13 +0530 Subject: [PATCH 08/11] Rewrite Android push guide for drop-in push-notifications-android SDK Replace the copy-the-UI-Kit-sample approach with the released com.cometchat:push-notifications-android:1.0.0 drop-in SDK, mirroring the new Flutter guide's structure. Covers dependency setup, Application init via PNConfiguration.Builder, forwarding FCM payloads to handlePushNotification, token registration/refresh, notification-tap and call-event listeners, badge count, testing, and troubleshooting. Co-Authored-By: Claude Opus 4.8 --- notifications/android-push-notifications.mdx | 794 +++++-------------- 1 file changed, 180 insertions(+), 614 deletions(-) diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index 33b0d5a59..9523d313d 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -1,711 +1,277 @@ --- title: "Android" -description: "Setup FCM and CometChat for message and call push notifications on Android." +description: "Add CometChat push notifications and VoIP calls to an Android app with the drop-in cometchat push-notifications-android SDK." --- - - - Reference implementation of Kotlin UI Kit, FCM and Push Notification Setup. - ## What this guide covers -- 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. -- App icon badge count and grouped notifications using `unreadMessageCount` from the CometChat push payload. +- Adding the `push-notifications-android` SDK and initializing it. +- Wiring your own Firebase Messaging service to forward payloads to the SDK. +- Requesting permission and registering the FCM token after login. +- Letting the SDK render chat notifications (grouped, inline reply) and full-screen VoIP calls. +- Handling notification taps and call events, and suppressing notifications for the open chat. +- Testing and troubleshooting. -{/* ## What you need first + +The `push-notifications-android` SDK replaces the previous approach of copying the UI Kit sample's `fcm/` and `voip/` packages (`FCMService`, `FCMMessageNotificationUtils`, `FCMMessageBroadcastReceiver`, `CometChatVoIP`, `CometChatVoIPConnectionService`, and related helpers). Token registration, notification channels, stacking, avatars, inline reply, delivery receipts, the VoIP ConnectionService, the incoming-call screen, ring timeout, call-collision handling, and SDK auto-init on a killed-app wake are all handled inside the SDK. Your app keeps only a ~5-line Firebase Messaging service and three facade calls. + -- 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 it works -## How FCM and CometChat fit together +- **FCM's role:** Firebase issues the registration token and delivers the CometChat payload as a **data message**. The SDK does **not** depend on `firebase-messaging` — your app owns Firebase and passes the payload as a `Map`. +- **CometChat's role:** The FCM provider you add in the dashboard binds your registered token to the logged-in user so CometChat can route pushes on your behalf. +- **The SDK's role:** `CometChatPushNotifications` parses the payload and decides chat vs call. Chat → builds/stacks notifications. Call → drives a full-screen incoming-call experience over an Android Telecom `SELF_MANAGED` `ConnectionService` (the same approach WhatsApp/Signal use — no `READ_PHONE_STATE`/`ANSWER_PHONE_CALLS` and no phone-account toggle in Settings). +- **Killed-app wake:** When FCM starts your process, `init()` in `Application.onCreate()` runs first and stores your credentials; `handlePushNotification(...)` then auto-initializes the Chat SDK before routing, so calls connect even from a terminated state. -- **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. +## Prerequisites + +- The FCM provider, Firebase project, and `google-services.json` from **[Getting Started](/notifications/push-getting-started)** (this guide assumes those are done). +- The CometChat **Chat SDK / UI Kit** already integrated (you call `CometChatUIKit.init()` / `login()` yourself). +- **minSdk 24+**, **compileSdk 36**, **Java 11**, and a Gradle/AGP version matching your UI Kit project. +- A physical device — background delivery and full-screen calls are unreliable on emulators. **Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your FCM provider, and configure Firebase (`google-services.json` + service account JSON). This guide covers only the Android app wiring. -## 1. Add dependencies (Gradle) - -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. - - - - -```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" } -``` - -This TOML file defines versions and aliases for the required dependencies. - - - -```gradle lines -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.google.services) -} +## 1. Add the dependency -android { - compileSdk 35 - defaultConfig { - applicationId "your.package.name" - minSdk 26 - targetSdk 35 - } - kotlinOptions { jvmTarget = "11" } -} +Add the CometChat Maven repository (the same one that serves the Chat and Calls SDKs) in `settings.gradle.kts`: -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) +```kotlin settings.gradle.kts +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + maven("https://dl.cloudsmith.io/public/cometchat/cometchat/maven/") + } } ``` - - - -- 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. - -## 2. 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`. +Add the SDK plus your own Firebase Messaging dependency in the app module. Apply the `google-services` plugin so `google-services.json` is picked up: -- 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). - -## 3. Application wiring, sample code, and callbacks - -- 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** - -- `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. - -**Splash/entry deep link handler** (adapt activity targets): - -```kotlin lines -// In your Splash/entry activity (e.g., SplashActivity) -override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - handleDeepLinking() +```kotlin app/build.gradle.kts +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("com.google.gms.google-services") } -private fun handleDeepLinking() { - NotificationManagerCompat.from(this) - .cancel(AppConstants.FCMConstants.NOTIFICATION_GROUP_SUMMARY_ID) - - val notificationType = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_TYPE) - val notificationPayload = intent.getStringExtra(AppConstants.FCMConstants.NOTIFICATION_PAYLOAD) +dependencies { + implementation("com.cometchat:push-notifications-android:1.0.0") - startActivity( - Intent(this, HomeActivity::class.java).apply { - putExtra(AppConstants.FCMConstants.NOTIFICATION_TYPE, notificationType) - putExtra(AppConstants.FCMConstants.NOTIFICATION_PAYLOAD, notificationPayload) - } - ) - finish() + // Your app owns Firebase — the SDK does not depend on it. + implementation(platform("com.google.firebase:firebase-bom:34.15.0")) + implementation("com.google.firebase:firebase-messaging") } ``` -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. - -**SplashViewModel (init UIKit + login check)** - -```kotlin lines -class SplashViewModel : ViewModel() { - private val loginStatus = MutableLiveData() - - 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) + +You do **not** need to add notification, full-screen-intent, foreground-service, `WAKE_LOCK`, or `MANAGE_OWN_CALLS` permissions, or declare the incoming-call activity / VoIP services in your `AndroidManifest.xml`. The SDK's manifest declares everything it needs and Gradle merges it into your app automatically. + - val uiKitSettings = UIKitSettings.UIKitSettingsBuilder() - .setAutoEstablishSocketConnection(false) - .setAppId(appId) - .setRegion(region) - .setAuthKey(authKey) - .subscribePresenceForAllUsers() - .build() - - CometChatUIKit.init(context, uiKitSettings, object : CometChat.CallbackListener() { - override fun onSuccess(s: String) { - CometChat.setDemoMetaInfo(getAppMetadata(context)) - checkUserIsNotLoggedIn() - } - override fun onError(e: CometChatException) { - Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show() - } - }) - } +## 2. Store your credentials - 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 - } +Keep the values from Getting Started where your app can read them: - fun checkUserIsNotLoggedIn() { - loginStatus.value = CometChatUIKit.getLoggedInUser() != null - } - - fun getLoginStatus(): LiveData = loginStatus +```kotlin AppCredentials.kt +object AppCredentials { + const val APP_ID = "YOUR_APP_ID" + const val REGION = "YOUR_REGION" + const val AUTH_KEY = "YOUR_AUTH_KEY" + const val FCM_PROVIDER_ID = "FCM-PROVIDER-ID" } ``` -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)** +## 3. Initialize the SDK -```kotlin lines -object Repository { - fun registerFCMToken(listener: CometChat.CallbackListener) { /* fetch FCM token and call registerPushToken */ } - fun unregisterFCMToken(listener: CometChat.CallbackListener) { /* call unregisterPushToken */ } +Initialize in `Application.onCreate()` so the SDK is ready before any push arrives (including killed-app wakes). Build the config with `PNConfiguration.Builder(appId, region)`: - fun rejectCallWithBusyStatus( - call: Call, - callbackListener: CometChat.CallbackListener? = null - ) { /* reject with CALL_STATUS_BUSY and notify UIKit */ } +```kotlin MyApplication.kt +import android.app.Application +import com.cometchat.pushnotification.CometChatPushNotifications +import com.cometchat.pushnotification.PNConfiguration - fun acceptCall( - call: Call, - callbackListener: CometChat.CallbackListener - ) { /* acceptCall and notify UIKit */ } - - 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)** - -```kotlin lines class MyApplication : Application() { override fun onCreate() { super.onCreate() - if (!CometChatUIKit.isSDKInitialized()) { - SplashViewModel().initUIKit(this) - } - - 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 } - }) - } + // Initialize the CometChat UI Kit / Chat SDK first (your existing setup) … - 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) {} - }) - } + val config = PNConfiguration.Builder(AppCredentials.APP_ID, AppCredentials.REGION) + .setNotificationSmallIcon(R.drawable.ic_notification) + .setVoIPEnabled(true) // full-screen incoming-call UI (default: true) + .setInlineReplyEnabled(true) // reply from the notification (default: true) + .build() - 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() - } - } - - fun isAppInForeground(): Boolean = isAppInForeground - var soundManager: CometChatSoundManager? = null + CometChatPushNotifications.init(this, config) } } ``` -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. - -State to set at runtime: - -- `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. +Point `` at `MyApplication` in your manifest. -## 4. Application wiring and permissions +## 4. Forward FCM payloads to the SDK -- [`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: +Create your own `FirebaseMessagingService`. Forward each data message to `handlePushNotification(...)` and each token to `handleTokenRefresh(...)`: -```kotlin lines -override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - AppUtils.requestNotificationPermission(this) - configureVoIP() - handleDeepLinking() // open chats based on NOTIFICATION_TYPE/NOTIFICATION_PAYLOAD -} - -private fun configureVoIP() { - CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString()) - launchVoIP() -} +```kotlin AppFCMService.kt +import com.cometchat.pushnotification.CometChatPushNotifications +import com.cometchat.pushnotification.models.PushPlatform +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage -private fun launchVoIP() { - if (!CometChatVoIP.hasReadPhoneStatePermission(this)) { - CometChatVoIP.requestReadPhoneStatePermission(this, CometChatVoIPConstant.PermissionCode.READ_PHONE_STATE) - return +class AppFCMService : FirebaseMessagingService() { + override fun onMessageReceived(message: RemoteMessage) { + CometChatPushNotifications.handlePushNotification(this, data = message.data) } - 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 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) - } - } - 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() - } - } -} -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 - } -} -``` - -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. - -## 5. Register the FCM token after login - -Call registration right after `CometChatUIKit.login()` succeeds: - -```kotlin lines -FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> - if (task.isSuccessful) { - val token = task.result - CometChatNotifications.registerPushToken( + override fun onNewToken(token: String) { + // Re-binds a rotated token when a user is logged in; no-op otherwise. + CometChatPushNotifications.handleTokenRefresh( + PushPlatform.FCM_ANDROID, 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 */ } - } + AppCredentials.FCM_PROVIDER_ID, ) } } ``` -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. +Register the service in your manifest: -Re-register on token refresh. Keep the provider ID aligned to the FCM provider you created for this app. - -Handle FCM refresh tokens too: - -```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 */ } - } - ) -} +```xml AndroidManifest.xml + + + + + ``` -Ensures a rotated FCM token is re-bound to the logged-in user; without this, pushes will stop after Firebase refreshes the token. + +`handlePushNotification` also accepts optional `title`, `body`, `icon`, `uid`, and `guid` overrides — pass `uid`/`guid` if you want to control the notification grouping key. For the default experience, just pass `data`. + -## 6. Unregister the token on logout +## 5. Request permission and register the token -```kotlin lines -CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener() { - override fun onSuccess(s: String?) { /* success */ } - override fun onError(e: CometChatException) { /* handle error */ } -}) -// Then call CometChatUIKit.logout() -``` +Request `POST_NOTIFICATIONS` (Android 13+) early using the SDK helper, and register the token **after** login so it binds to the session: -## 7. Badge count - -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. You can use this to set the app icon badge and enrich local notifications. - -### 7.1 Enable unread badge count on the CometChat Dashboard - -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Scroll to the bottom and enable the **Unread Badge Count** toggle. - -This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. - -### 7.2 Add the ShortcutBadger dependency - -Add the ShortcutBadger library to your app-level `build.gradle`: +```kotlin +import com.cometchat.pushnotification.helpers.CometChatPNHelper -```gradle lines -dependencies { - implementation 'me.leolin:ShortcutBadger:1.1.22@aar' +// In your entry activity, before/at login: +if (!CometChatPNHelper.hasNotificationPermission(this)) { + CometChatPNHelper.requestNotificationPermission(this, REQUEST_CODE_NOTIFICATIONS) } ``` -### 7.3 Expected payload format - -CometChat sends FCM data messages with this structure (relevant fields): - -```json -{ - "data": { - "unreadMessageCount": "5", - "title": "New Message", - "alert": "John: Hello!", - "conversationId": "user_abc123", - "conversationType": "user" - } +```kotlin +import com.cometchat.pushnotification.CometChatPushNotifications +import com.cometchat.pushnotification.models.PushPlatform +import com.google.firebase.messaging.FirebaseMessaging + +// After CometChatUIKit.login(...) succeeds: +FirebaseMessaging.getInstance().token.addOnSuccessListener { token -> + CometChatPushNotifications.registerToken( + PushPlatform.FCM_ANDROID, + token, + AppCredentials.FCM_PROVIDER_ID, + onSuccess = { /* token registered */ }, + onError = { e -> /* log e */ }, + ) } ``` -`unreadMessageCount` is a string representing the total unread messages across all conversations for the logged-in user. - -### 7.4 Update the app badge from the push payload - -Inside your notification service (for example `FCMService.onMessageReceived`), parse `unreadMessageCount` and update the badge: +Unregister **before** logout so the device stops receiving pushes for that user: -```kotlin lines -import me.leolin.shortcutbadger.ShortcutBadger - -// Inside onMessageReceived, after receiving the message: -val unreadCountStr: String? = message.data["unreadMessageCount"] -unreadCountStr?.toIntOrNull()?.let { count -> - if (count >= 0) { - ShortcutBadger.applyCount(applicationContext, count) - } else { - Log.w(TAG, "Invalid badge count: $count") - } -} ?: Log.d(TAG, "No unreadMessageCount in payload") +```kotlin +CometChatPushNotifications.unregisterToken( + onSuccess = { /* then CometChatUIKit.logout(...) */ }, + onError = { e -> /* log e */ }, +) ``` -`ShortcutBadger` uses launcher-specific APIs (Samsung, Huawei, Xiaomi, etc.) to display a badge number on the app icon. Passing `0` clears the badge. +## 6. Notification taps and call events -### 7.5 Show unread count in the notification +Suppress notifications for the chat that is currently open, and set the tap listener so you can navigate when a chat notification is tapped: -Update your notification builder to display the unread count in the notification itself: - -```kotlin lines -// Inside your notification building logic -mNotificationBuilder.setNumber(count) -mNotificationBuilder.setSubText("$count unread messages") +```kotlin +import com.cometchat.pushnotification.CometChatPushNotifications +import com.cometchat.pushnotification.listeners.NotificationTapListener + +// When a chat screen opens (clear it on exit): +CometChatPushNotifications.setCurrentOpenChatId(conversationId) + +CometChatPushNotifications.setOnNotificationTapListener(object : NotificationTapListener { + override fun onNotificationTapped( + context: Context, + user: User?, + group: Group?, + message: BaseMessage?, + ) { + // Build your own Intent to open the conversation for `user` / `group`. + } +}) ``` -- `setNumber(count)` displays a count badge on the notification icon. -- `setSubText()` shows the unread count below the notification title. - -### 7.6 Clear badge when the app opens - -Clear the badge count when the app launches and every time it resumes from the background. Override `onResume()` in your main activity: +By default the SDK shows its **built-in full-screen incoming-call screen** and launches the CometChat Calls UI on accept — you don't need to write any call UI. Optionally, observe foreground call events to drive your own in-app UI: -```kotlin lines -override fun onResume() { - super.onResume() - ShortcutBadger.removeCount(this) -} -``` +```kotlin +import com.cometchat.pushnotification.listeners.CallEventListener +import com.cometchat.pushnotification.models.PNCallInfo -This ensures the badge is cleared when the user opens the app, keeping the badge count in sync with the actual unread state. - -{/* ## 9. What arrives in the push payload - -Payload keys delivered to `onMessageReceived` (adapted from the shared push integration): - -```json lines -{ - "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 -} +CometChatPushNotifications.setCallEventListener(object : CallEventListener { + override fun onIncomingCall(callInfo: PNCallInfo) { /* callInfo.sessionId, callerName, callType … */ } + override fun onCallCancelled(sessionId: String) { /* tear down UI */ } +}) ``` -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. - -## 9. Handle call pushes (ConnectionService) + +To fully replace the built-in call screen with your own, set `setOnIncomingCallHandler(...)` (override the ringing screen) and/or `setOnCallAnsweredHandler(...)` (override the post-accept ongoing-call screen). The SDK still manages ringtone, ring timeout, and cleanup. + -- 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. +## 7. Badge count -## 10. Customize notification text or parse payloads +CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string, total unread across all conversations). Enable it once on the dashboard: **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences → Unread Badge Count**. -Parse the push into a `BaseMessage` for deep links: +Parse it from the payload and set the app-icon badge with a launcher badge library such as `ShortcutBadger`: ```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 -} +val count = message.data["unreadMessageCount"]?.toIntOrNull() ?: 0 +ShortcutBadger.applyCount(applicationContext, count) // 0 clears the badge ``` -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. - -Override the push body before sending: - -```kotlin -val meta = JSONObject().put("pushNotification", "Custom notification body") -customMessage.metadata = meta -CometChat.sendCustomMessage(customMessage, object : CallbackListener() {}) -``` +Clear the badge when the app resumes (`onResume` of your main activity). -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 +## 8. 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). +2. Log in and confirm token registration succeeds (check the `registerToken` success callback / 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. + - App open in a different chat: notification appears (grouped, with inline reply). + - App backgrounded/killed: notification appears; tapping opens the right conversation via `onNotificationTapped`. + - No notification for the chat currently open (via `setCurrentOpenChatId`). +4. Trigger an incoming CometChat call and confirm the full-screen call UI shows the caller with Accept/Decline, even on the lock screen; Accept joins the call, Decline rejects it. +5. Toggle Wi-Fi/cellular and reinstall to confirm token registration survives refreshes (`onNewToken` → `handleTokenRefresh`). -## Troubleshooting +## 9. 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. | -| Badge count not showing | Verify **Unread Badge Count** is enabled in CometChat Dashboard, ShortcutBadger dependency is added, and the launcher supports badges (Samsung, Huawei, Xiaomi). | +| No notifications received | Confirm `google-services.json` is in the app module, the package name matches Firebase, `POST_NOTIFICATIONS` is granted (Android 13+), and your `FirebaseMessagingService` calls `handlePushNotification(this, data = message.data)`. | +| Token registration errors | Verify `FCM_PROVIDER_ID` matches the dashboard exactly and that `registerToken` runs **after** `login` succeeds. | +| Nothing happens on a killed-app push | Ensure `CometChatPushNotifications.init(...)` runs in `Application.onCreate()` and `` points at your `Application` subclass. | +| Full-screen call UI not showing | Test on a physical device; on aggressive OEM skins (MIUI/Redmi/POCO), grant autostart/lock-screen/overlay permissions for the app. | +| Pushes stop after some time | Make sure `onNewToken` forwards to `handleTokenRefresh` (or re-`registerToken`) — FCM rotates tokens. | + +## Resources + + + + The drop-in push & VoIP SDK on the CometChat Maven repo. + + + Source, changelog, and sample. + + From bb76667081ea2c8643aacfe3163d8f5028095f7f Mon Sep 17 00:00:00 2001 From: Ashfaaq Ali Date: Tue, 28 Jul 2026 16:25:40 +0530 Subject: [PATCH 09/11] fix --- notifications/android-push-notifications.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx index 9523d313d..6f5580973 100644 --- a/notifications/android-push-notifications.mdx +++ b/notifications/android-push-notifications.mdx @@ -100,8 +100,7 @@ class MyApplication : Application() { val config = PNConfiguration.Builder(AppCredentials.APP_ID, AppCredentials.REGION) .setNotificationSmallIcon(R.drawable.ic_notification) - .setVoIPEnabled(true) // full-screen incoming-call UI (default: true) - .setInlineReplyEnabled(true) // reply from the notification (default: true) + .setVoIPEnabled(true) .build() CometChatPushNotifications.init(this, config) From e55fa4c0af24ab952cb5926f20903e15ccb7ac96 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 28 Jul 2026 19:03:30 +0530 Subject: [PATCH 10/11] Rewrite iOS push guide for drop-in push-notifications-ios SDK Consolidates the two legacy iOS pages (APNs + FCM copy-files patterns) into a single notifications/ios-push-notifications.mdx that documents the CometChatPushNotifications drop-in SDK: SPM/CocoaPods install, AppDelegate + SceneDelegate wiring, CometChatPushNotificationsDelegate for taps and calls, foreground suppression, badge, and troubleshooting. Updates docs.json nav + redirects, the notifications and getting-started landing pages (single "iOS" card, no FCM-iOS Firebase tab), and stray cross-references from Flutter VoIP and legacy iOS extensions pages. --- calls/flutter/voip-calling.mdx | 2 +- docs.json | 15 +- notifications.mdx | 3 +- notifications/ios-apns-push-notifications.mdx | 1300 ----------------- notifications/ios-fcm-push-notifications.mdx | 372 ----- notifications/ios-push-notifications.mdx | 373 +++++ notifications/push-getting-started.mdx | 15 +- sdk/ios/2.0/extensions.mdx | 2 +- 8 files changed, 391 insertions(+), 1691 deletions(-) delete mode 100644 notifications/ios-apns-push-notifications.mdx delete mode 100644 notifications/ios-fcm-push-notifications.mdx create mode 100644 notifications/ios-push-notifications.mdx diff --git a/calls/flutter/voip-calling.mdx b/calls/flutter/voip-calling.mdx index b5be9ca57..9804b611a 100644 --- a/calls/flutter/voip-calling.mdx +++ b/calls/flutter/voip-calling.mdx @@ -38,7 +38,7 @@ Before implementing VoIP calling, ensure you have: - [CometChat Chat SDK](/sdk/flutter/overview) and [Calls SDK](/calls/flutter/setup) integrated - Push notifications configured for both platforms: - [Firebase Cloud Messaging (FCM)](/notifications/android-push-notifications) for Android - - [APNs](/notifications/ios-apns-push-notifications) for iOS + - [APNs](/notifications/ios-push-notifications) for iOS - [Push notifications enabled](/notifications/push-overview) in CometChat Dashboard diff --git a/docs.json b/docs.json index 050eaa71f..b58f37e78 100644 --- a/docs.json +++ b/docs.json @@ -6490,8 +6490,7 @@ "group": "Platform Integrations", "pages": [ "notifications/android-push-notifications", - "notifications/ios-apns-push-notifications", - "notifications/ios-fcm-push-notifications", + "notifications/ios-push-notifications", "notifications/flutter-push-notifications", "notifications/react-native-push-notifications-android", "notifications/react-native-push-notifications-ios", @@ -7101,11 +7100,19 @@ }, { "source": "/extensions/ios-fcm-push-notifications", - "destination": "/notifications/ios-fcm-push-notifications" + "destination": "/notifications/ios-push-notifications" }, { "source": "/extensions/ios-apns-push-notifications", - "destination": "/notifications/ios-apns-push-notifications" + "destination": "/notifications/ios-push-notifications" + }, + { + "source": "/notifications/ios-apns-push-notifications", + "destination": "/notifications/ios-push-notifications" + }, + { + "source": "/notifications/ios-fcm-push-notifications", + "destination": "/notifications/ios-push-notifications" }, { "source": "/extensions/flutter-push-notifications", diff --git a/notifications.mdx b/notifications.mdx index ada60c136..c478083cb 100644 --- a/notifications.mdx +++ b/notifications.mdx @@ -61,8 +61,7 @@ canonical: "https://cometchat.com/docs" } href="/notifications/android-push-notifications" horizontal /> - } href="/notifications/ios-apns-push-notifications" horizontal /> - } href="/notifications/ios-fcm-push-notifications" horizontal /> + } href="/notifications/ios-push-notifications" horizontal /> } href="/notifications/flutter-push-notifications" horizontal /> } href="/notifications/react-native-push-notifications-android" horizontal /> diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx deleted file mode 100644 index c348128dc..000000000 --- a/notifications/ios-apns-push-notifications.mdx +++ /dev/null @@ -1,1300 +0,0 @@ ---- -title: "iOS APNs" -description: "Implement APNs push notifications with CometChat UIKit for iOS, including CallKit integration for VoIP calls." ---- - - - Reference implementation of iOS UIKit, APNs and Push Notification Setup. - - -## What this guide covers - -- Prerequisite: the shared dashboard + Apple/APNs setup in the [Getting Started](/notifications/push-getting-started) guide. -- APNs + PushKit/CallKit wiring (tokens, delegates, CallKit). -- Incoming message/call handling and deep links. -- Badge count and grouped notifications. -- Payload customization and testing. - -{/* ## What you need first - -- 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 - -- **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. - - -**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple/Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. - - -## 1. Wiring APNs + PushKit/CallKit - -- 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 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]) - } - - // 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 - } - - CometChatPNHelper.handleTapActionOnNotification(userInfo: userInfo, completionHandler: completionHandler) - } - -} - -``` - - - ```swift lines -#if canImport(CometChatCallsSDK) - -import Foundation -import PushKit -import CallKit -import AVFoundation -import CometChatSDK -import CometChatCallsSDK - -extension AppDelegate: PKPushRegistryDelegate, CXProviderDelegate { - - // MARK: - VoIP Push Token Updates - - func pushRegistry( - _ registry: PKPushRegistry, - didUpdate pushCredentials: PKPushCredentials, - for type: PKPushType - ) { - print("VoIP token updated for type: \(type.rawValue)") - cometchatAPNsHelper.registerForVoIPCalls(pushCredentials: pushCredentials) - } - - 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 - } - - registry.desiredPushTypes = [] - registry.desiredPushTypes = [.voIP] - print("VoIP token refreshed") - } - - // 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) - } - - func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { - print("User answered call") - - // CRITICAL: Configure audio session BEFORE answering - configureAudioSession() - - cometchatAPNsHelper.onAnswerCallAction(action: action) - } - - 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)") - } - } -} - -#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() - } - } 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() - } - - 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) - } - } - - cometChatOngoingCall.setOnCallEnded { [weak self] call in - DispatchQueue.main.async { - 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) - } - } onError: { error in - print("Error accepting call: \(error?.errorDescription ?? "")") - } - } - - 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 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 }) - } - - 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) - } - } - } - - public func onProviderDidReset(provider: CXProvider) { - if let uuid = self.uuid { - onCall = true - provider.reportCall(with: uuid, endedAt: Date(), reason: .unanswered) - } - } - - public func onEndCallAction(action: CXEndCallAction) { - let endCallAction = CXEndCallAction(call: uuid!) - let transaction = CXTransaction() - transaction.addAction(endCallAction) - - callController.request(transaction) { error in - if let error = error { - print("Error requesting transaction: \(error)") - } else { - 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 ?? "")") - } - } - } - } - -} - -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 { - -let cometchatAPNsHelper = CometChatAPNsHelper() -static var currentActiveUser: CometChatSDK.User? -static var currentActiveGroup: CometChatSDK.Group? - -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 - } - - 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") - } - - 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 - } - - 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() -} - -static func navigateToViewController(_ viewController: UIViewController) { - - guard let window = UIApplication.shared.windows.first else { - print("Window not found") - return - } - - if let navigationController = window.rootViewController as? UINavigationController { - if let currentViewController = navigationController.viewControllers.last, - currentViewController.description == viewController.description { - print("Already in same view") - return - - } - navigationController.popViewController(animated: false) - navigationController.pushViewController(viewController, animated: false) - } else { - print("Root view controller is not a UINavigationController") - } - -} - -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 - } - - let textMessage = TextMessage(receiverUid: sender, text: text, receiverType: .user) - if let parentID = withParentId { - textMessage.parentMessageId = parentID - } - CometChatUIKit.sendTextMessage(message: textMessage) -} - -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 - } - - let textMessage = TextMessage(receiverUid: groupID, text: text, receiverType: .group) - if let parentID = withParentId { - textMessage.parentMessageId = parentID - } - CometChatUIKit.sendTextMessage(message: textMessage) -} - -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 - } - - let senderUser = User(uid: sender, name: senderName) - senderUser.avatar = userInfo["senderAvatar"] as? String - - getUser(forUID: sender) { retrievedUser in - DispatchQueue.main.async { - if let user = retrievedUser { - senderUser.status = user.status - } else { - print("Failed to retrieve user status") - } - - let chatViewController = MessagesVC() - chatViewController.user = retrievedUser - self.navigateToViewController(chatViewController) - } - - } -} - - -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 - } - - let groupUser = Group(guid: groupID, name: groupName, groupType: .private, password: nil) - - 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) - } - } -} - -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 - } - - 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) - } -} - -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 - } - - let groupUser = Group(guid: groupID, name: groupName, groupType: .private, password: nil) - groupUser.icon = userInfo["receiverAvatar"] as? String - DispatchQueue.main.async { - - 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 - } - - 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) - } -} - -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) - } -} - -static func shouldPresentNotification(userInfo: [AnyHashable: Any]) -> Bool { - guard let notificationType = userInfo["type"] as? String, - let receiverType = userInfo["receiverType"] as? String else { - return true - } - - 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 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 - } - -} - -``` - - - -## 2. 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)") - } -} -``` - -## 3. Unregister the token on logout - -Before logging the user out, unregister the push token so the device stops receiving notifications for that user. - -```swift lines -CometChatNotifications.unregisterPushToken( - onSuccess: { success in - print("Push token unregistered: \(success)") - CometChatUIKit.logout(onSuccess: { _ in - print("Logout successful") - }, onError: { error in - print("Logout failed: \(error.errorDescription)") - }) - }, - onError: { error in - print("Token unregister failed: \(error.errorCode) - \(error.errorDescription)") - } -) -``` - -Always call `CometChatNotifications.unregisterPushToken()` **before** `CometChatUIKit.logout()`. If you skip this step, the device may continue to receive pushes for the logged-out user. - -## 4. Badge count - -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. You can use this to set the app icon badge. - -### 4.1 Enable unread badge count on the CometChat Dashboard - -1. Go to **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. -2. Scroll to the bottom and enable the **Unread Badge Count** toggle. - -This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. - -### 4.2 Expected payload format - -CometChat sends APNs payloads with this structure (relevant fields): - -```json -{ - "unreadMessageCount": "5", - "title": "New Message", - "alert": "John: Hello!", - "conversationId": "user_abc123", - "receiverType": "user" -} -``` - -`unreadMessageCount` is a string representing the total unread messages across all conversations for the logged-in user. - -### 4.3 Update the app badge from the push payload - -Inside your `UNUserNotificationCenterDelegate` method (for example `willPresent` or a Notification Service Extension), parse `unreadMessageCount` and update the badge: - -```swift lines -// Inside userNotificationCenter(_:willPresent:) or a Notification Service Extension -let userInfo = notification.request.content.userInfo - -if let unreadCountStr = userInfo["unreadMessageCount"] as? String, - let count = Int(unreadCountStr), count >= 0 { - DispatchQueue.main.async { - UIApplication.shared.applicationIconBadgeNumber = count - } -} else { - print("No valid unreadMessageCount in payload") -} -``` - -Setting `applicationIconBadgeNumber` to `0` clears the badge. - -### 4.4 Clear badge when the app opens - -Clear the badge count when the app launches and every time it returns to the foreground. In your `SceneDelegate` or `AppDelegate`: - -```swift lines -func sceneDidBecomeActive(_ scene: UIScene) { - UIApplication.shared.applicationIconBadgeNumber = 0 -} -``` - -This keeps the badge in sync with the actual unread state. - -## 5. Navigation from notifications - -When the user taps a notification, use `userNotificationCenter(_:didReceive:withCompletionHandler:)` to extract conversation details and navigate to the correct screen. - -```swift lines -func userNotificationCenter( - _ center: UNUserNotificationCenter, - didReceive response: UNNotificationResponse, - withCompletionHandler completionHandler: @escaping () -> Void -) { - let userInfo = response.notification.request.content.userInfo - - guard let receiverType = userInfo["receiverType"] as? String else { - completionHandler() - return - } - - if receiverType == "user" { - guard let senderUid = userInfo["sender"] as? String, - let senderName = userInfo["senderName"] as? String else { - completionHandler() - return - } - let user = User(uid: senderUid, name: senderName) - let messagesVC = MessagesVC() - messagesVC.user = user - navigateToViewController(messagesVC) - - } else if receiverType == "group" { - guard let groupId = userInfo["receiver"] as? String, - let groupName = userInfo["receiverName"] as? String else { - completionHandler() - return - } - let group = Group(guid: groupId, name: groupName, groupType: .public, password: nil) - let messagesVC = MessagesVC() - messagesVC.group = group - navigateToViewController(messagesVC) - } - - completionHandler() -} -``` - -The `navigateToViewController` helper (shown in `CometChatPNHelper.swift` above) pushes the `MessagesVC` onto the navigation stack. Ensure the root view controller is ready before navigation -- if the app was terminated, wait until login completes before routing. - -## 6. 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. - -## 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. | -| Badge count not showing | Verify **Unread Badge Count** is enabled in CometChat Dashboard and that your app reads `unreadMessageCount` from the payload. | -| Notification tap does not navigate | Ensure `UNUserNotificationCenterDelegate` is set, payload contains `receiverType`/`sender`/`receiver`, and root view controller is ready. | diff --git a/notifications/ios-fcm-push-notifications.mdx b/notifications/ios-fcm-push-notifications.mdx deleted file mode 100644 index 47f4c3c28..000000000 --- a/notifications/ios-fcm-push-notifications.mdx +++ /dev/null @@ -1,372 +0,0 @@ ---- -title: "iOS FCM" -description: "Guide to integrating Firebase Cloud Messaging (FCM) push notifications in iOS apps using CometChat." ---- - -FCM doesn't support VoIP pushes; use APNs + PushKit for VoIP calls. - - - Reference implementation of iOS UIKit, FCM and Push Notification Setup. - - -## What this guide covers - -- Prerequisite: the shared dashboard + Firebase/APNs setup in the [Getting Started](/notifications/push-getting-started) guide. -- Podfile + AppDelegate wiring for Firebase Messaging on iOS. -- Token registration/rotation with CometChat Push Notifications (FCM iOS provider). -- Incoming message/call handling and deep links. -- Badge count and grouped notifications. -- Payload customization and testing. - -{/* ## What you need first - -- 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. */} - -## How FCM + CometChat work together - -- **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. - - -**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **FCM (iOS)** provider, upload your APNs key to Firebase (Cloud Messaging), download `GoogleService-Info.plist` into your target, and enable the Xcode capabilities. This guide covers only the iOS app wiring. - - -## 1. Add dependencies (Podfile) - -```ruby lines -target 'YourApp' do - use_frameworks! - - pod 'CometChatCallsSDK' - pod 'CometChatUIKitSwift', '5.1.7' - pod 'Firebase/Messaging' # add for FCM -end -``` - -Run `pod install`. - -## 2. Wire AppDelegate (Firebase + delegates) - -```swift lines highlight={15, 17, 19-37} -import UIKit -import FirebaseCore -import FirebaseMessaging -import CometChatUIKitSwift -import CometChatSDK - -@main -class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - - FirebaseApp.configure() - - Messaging.messaging().delegate = self - - 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") - } - } - - return true - } - - 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) - } - - func messaging( - _ messaging: Messaging, - didReceiveRegistrationToken fcmToken: String? - ) { - guard let fcmToken = fcmToken else { - print("FCM token is nil") - return - } - - 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") - } - } - - - 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. - -## 3. Create FCM helper - -Create `CometChatFCMHelper.swift` and add: - -```swift -import UIKit -import UserNotifications - -final class CometChatFCMHelper { - - static let shared = CometChatFCMHelper() - private init() {} - - /// Configure push notifications (iOS-safe) - func configure(application: UIApplication, delegate: UNUserNotificationCenterDelegate) { - - // 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) - } - } - - // 2. Set delegate - UNUserNotificationCenter.current().delegate = delegate - - // 3. Register for APNs - DispatchQueue.main.async { - application.registerForRemoteNotifications() - } - } -} -``` - -{/* Alternative: pass the FCM token into `UIKitSettings` via `.set(fcmKey:)` before `CometChatUIKit.init` (e.g., `SceneDelegate`), alongside APNs device/VoIP tokens. */} - -**What this helper is doing** - -- 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. - -## 4. Badge count and grouped notifications - -CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (a string) representing the total unread messages across all conversations for the logged-in user. Use this to set the app icon badge and enrich local notifications. - -### 4.1 Enable unread badge count on the CometChat Dashboard - - - - Go to **CometChat Dashboard → Notifications → Settings → Preferences → Push - Notification Preferences**. - - - Scroll to the bottom and enable the **Unread Badge Count** toggle. - - - -This ensures CometChat includes the `unreadMessageCount` field in every push payload sent to your app. - -### 4.2 Clear badge count when app becomes active - -Add the following to your `SceneDelegate.swift` to reset the badge when the user opens the app: - -```swift -func sceneDidBecomeActive(_ scene: UIScene) { - // Clear badge count when user opens the app - UIApplication.shared.applicationIconBadgeNumber = 0 - print("Badge count cleared") -} -``` - -### 4.3 Update badge count from push notifications - -Update your `AppDelegate.swift` to handle badge count updates in both foreground and background states: - - - -```swift -func userNotificationCenter( - _ center: UNUserNotificationCenter, - willPresent notification: UNNotification, - withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void -) { - let userInfo = notification.request.content.userInfo - print("Will present notification: \(userInfo)") - - // Update badge count from payload - if let unreadCountString = userInfo["unreadMessageCount"] as? String, - let unreadCount = Int(unreadCountString) { - if unreadCount >= 0 { - UIApplication.shared.applicationIconBadgeNumber = unreadCount - print("Badge count updated (foreground): \(unreadCount)") - } - } else { - print("No unreadMessageCount in payload") - } - - completionHandler([.banner, .badge, .sound]) -} -``` - - -```swift -func application( - _ application: UIApplication, - didReceiveRemoteNotification userInfo: [AnyHashable: Any], - fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void -) { - print("Received remote notification: \(userInfo)") - - // Update badge count from payload - if let unreadCountString = userInfo["unreadMessageCount"] as? String, - let unreadCount = Int(unreadCountString) { - if unreadCount >= 0 { - UIApplication.shared.applicationIconBadgeNumber = unreadCount - print("Badge count updated (background): \(unreadCount)") - } - } else { - print("No unreadMessageCount in payload") - } - - completionHandler(.newData) -} -``` - - - - - The `unreadMessageCount` field is sent as a string in the payload. Always - parse it to an integer and validate it's non-negative before updating the - badge. - - -## 5. 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. - -## 6. Troubleshooting - -| 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/ios-push-notifications.mdx b/notifications/ios-push-notifications.mdx new file mode 100644 index 000000000..71c963708 --- /dev/null +++ b/notifications/ios-push-notifications.mdx @@ -0,0 +1,373 @@ +--- +title: "iOS" +description: "Add CometChat push notifications and VoIP calls to an iOS app with the drop-in CometChatPushNotifications SDK." +--- + +## What this guide covers + +- Adding the `CometChatPushNotifications` SDK and initializing it. +- Wiring `AppDelegate` to forward APNs callbacks to the SDK and register for VoIP pushes. +- Letting the SDK handle APNs alerts (foreground presentation, taps, quick reply, badge) and the full incoming-call experience via PushKit + CallKit. +- Handling notification taps and presenting the ongoing call screen via the delegate. +- Suppressing notifications for the chat that is currently open, clearing the badge on resume, and telling the SDK when the Calls stack is ready. +- Testing and troubleshooting. + + +The `CometChatPushNotifications` SDK replaces the previous approach of copying `CometChatAPNsHelper.swift`, `CometChatPNHelper.swift`, and the `AppDelegate+PN` / `AppDelegate+VoIP` extensions from the UI Kit sample. APNs token registration, foreground presentation, tap-to-open, quick reply, badge management, delivery / campaign engagement receipts, and the full PushKit + CallKit incoming-call flow are all handled inside the SDK. + + +## How it works + +- **APNs is the transport:** Apple issues the APNs device token (chat alerts) and the PushKit VoIP token (calls) — no FCM bridge is involved. +- **CometChat's role:** The **APNs Device** and **APNs VoIP** providers you add in the dashboard hold your `.p8` key. The SDK registers both tokens with CometChat automatically after login, so CometChat can route pushes on your behalf. +- **The SDK's role:** `CometChatPushNotifications` retrieves the tokens, registers them with CometChat, parses payloads, decides chat vs. call, presents CallKit for VoIP pushes, and calls back into your `CometChatPushNotificationsDelegate` for navigation and to present the ongoing call screen. +- **Cold-start VoIP:** when the app is killed and a VoIP push arrives, PushKit wakes the app and the SDK presents the CallKit screen natively before any UI is built. On accept, `presentCallScreen(for:sessionId:)` fires once your `notifyCallsSDKReady()` has run. + +## Prerequisites + +- The APNs Device provider, APNs VoIP provider, and `.p8` setup from **[Getting Started](/notifications/push-getting-started)** (this guide assumes those are done). +- The CometChat **Chat SDK / UI Kit** already integrated (you call `CometChatUIKit.init()` / `login()` yourself). +- **Xcode 15+**, **iOS 15.1+** deployment target, **Swift 5.9+**. +- **CometChatSDK ≥ 4.1.5** and **CometChatCallsSDK ≥ 5.0.0** (SPM users must add these packages explicitly — see below). +- A physical device — APNs, PushKit, and CallKit do not work on the simulator. + + +**Complete the [Getting Started](/notifications/push-getting-started) guide first** — enable Push Notifications, add your **APNs Device** and **APNs VoIP** providers, and finish the Apple / Xcode setup (`.p8` key + capabilities). This guide covers only the iOS app wiring. + + +## 1. Add the dependency + + + + In Xcode, go to **File → Add Package Dependencies…** and add: + + ``` + https://github.com/cometchat/push-notifications-sdk-ios.git + ``` + + Add the product **`CometChatPushNotificationsSwift`** to your app target. + + + SPM does not resolve transitive dependencies for prebuilt xcframeworks. You **must** also add these two packages to your project or the build fails with `Unable to resolve module dependency`: + + | Package | Minimum version | Product | + | --- | --- | --- | + | `https://github.com/cometchat/chat-sdk-ios.git` | **4.1.5** | `CometChatSDK` | + | `https://github.com/cometchat/calls-sdk-ios.git` | **5.0.0** | `CometChatCallsSDK` | + + + + ```ruby Podfile + platform :ios, '15.1' + + target 'YourApp' do + use_frameworks! + pod 'CometChatPushNotifications', '1.0.0' + end + ``` + + CocoaPods resolves `CometChatSDK` and `CometChatCallsSDK` for you. + + ```bash + pod install + ``` + + Open the generated `.xcworkspace` from here on. + + + +## 2. Configure the app target + +1. **Signing & Capabilities → + Capability** — add **Push Notifications** and **Background Modes**. Under Background Modes tick **Remote notifications**, **Voice over IP**, and **Audio, AirPlay, and Picture in Picture** (needed while a call is active). + + + Enable Push Notifications and Background Modes + + +2. **Entitlements** — Push Notifications adds `aps-environment` for you (`development` for debug builds, `production` for release). + +3. **Info.plist** — add microphone and camera usage strings so CallKit can request them, and confirm the background modes are present: + + ```xml + NSMicrophoneUsageDescription + Required for voice and video calls. + NSCameraUsageDescription + Required for video calls. + + UIBackgroundModes + + remote-notification + voip + audio + + ``` + +4. In the CometChat dashboard, keep the **Bundle ID** on your APNs providers in sync with your Xcode target's bundle ID. + +## 3. Store your credentials + +Keep the values from Getting Started where your app can read them. The provider ID here is the **APNs Device** provider — the SDK also registers the VoIP token against the same provider automatically: + +```swift AppConstants.swift +enum AppConstants { + static let APP_ID = "YOUR_APP_ID" + static let REGION = "YOUR_REGION" + static let AUTH_KEY = "YOUR_AUTH_KEY" + static let PROVIDER_ID = "APNS-PROVIDER-ID" +} +``` + +## 4. Initialize the SDK in `AppDelegate` + +Initialize the SDK in `application(_:didFinishLaunchingWithOptions:)`, set yourself as the delegate, register for VoIP pushes, and forward the three APNs `UIApplicationDelegate` callbacks. The SDK cannot intercept these callbacks itself — you must forward them: + +```swift AppDelegate.swift +import UIKit +import CometChatSDK +import CometChatUIKitSwift +import CometChatPushNotificationsSwift +#if canImport(CometChatCallsSDK) +import CometChatCallsSDK +#endif + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Your existing CometChat UI Kit / Chat SDK initialization runs elsewhere + // (typically in SceneDelegate) — the push SDK does not need to run before it. + + let config = CometChatPushNotificationsConfig( + providerId: AppConstants.PROVIDER_ID, + enableBadgeCount: true, + showInAppNotifications: true, + foregroundCallPresentation: .callKit // native CallKit UI, foreground included + ) + CometChatPushNotifications.shared.initialize(config: config) + CometChatPushNotifications.shared.delegate = self + CometChatPushNotifications.shared.registerForVoIPPushes() + return true + } + + // MARK: - APNs callbacks (forward all three to the SDK) + + func application(_ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + CometChatPushNotifications.shared.registerDeviceToken(deviceToken) + } + + func application(_ application: UIApplication, + didFailToRegisterForRemoteNotificationsWithError error: Error) { + CometChatPushNotifications.shared.handleRegistrationFailure(error) + } + + func application(_ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + CometChatPushNotifications.shared.handleBackgroundNotification(userInfo: userInfo) + completionHandler(.newData) + } +} +``` + +`CometChatPushNotificationsConfig` accepts: + +| Field | Default | Purpose | +| --- | --- | --- | +| `providerId` | required | The **APNs Device** provider ID from your dashboard. | +| `enableBadgeCount` | `false` | Set the app icon badge from the payload's `unreadMessageCount`. Pair with `clearBadgeCount()` on `sceneDidBecomeActive`. | +| `showInAppNotifications` | `false` | Show a banner for chat pushes while the app is in the foreground. Suppressed automatically for the currently open chat (see step 6). | +| `foregroundCallPresentation` | `.callKit` | How to render an incoming VoIP call that arrives in the foreground. `.callKit` uses the native iOS call screen; use your custom UI only if you disable the UI Kit's in-app incoming call. | + + +Token registration and rotation are handled automatically. After a successful `CometChat.login(...)`, the SDK binds the stored APNs and VoIP tokens to the user, and it re-registers them if either token changes. You do **not** need to call any `registerToken` API yourself. + + +## 5. Wire `SceneDelegate` — notify Calls SDK and clear badge + +Call `notifyCallsSDKReady()` **after** `CometChatUIKit.init(...)` succeeds so the SDK can present any incoming call that arrived during a cold start. Clear the badge every time the scene becomes active. Also disable the UI Kit's in-app incoming call so CallKit is not doubled up: + +```swift SceneDelegate.swift +import UIKit +import CometChatUIKitSwift +import CometChatSDK +import CometChatPushNotificationsSwift + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, + options: UIScene.ConnectionOptions) { + let settings = UIKitSettings() + .set(appID: AppConstants.APP_ID) + .set(authKey: AppConstants.AUTH_KEY) + .set(region: AppConstants.REGION) + .enable(inAppIncomingCall: false) // let CallKit own the incoming call UI + .build() + + CometChatUIKit.init(uiKitSettings: settings) { result in + if case .success = result { + // Tell the push SDK the Calls stack is ready so a call that arrived + // during cold start (VoIP push wakes the app in the killed state) can be presented. + CometChatPushNotifications.shared.notifyCallsSDKReady() + } + } + } + + func sceneDidBecomeActive(_ scene: UIScene) { + CometChatPushNotifications.shared.clearBadgeCount() + } +} +``` + +## 6. Suppress notifications for the open chat + +On every chat screen, tell the SDK which conversation is active so it doesn't show a banner for messages you can already see. Clear it when the screen goes away: + +```swift MessagesVC.swift +import CometChatUIKitSwift +import CometChatPushNotificationsSwift + +override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if let uid = user?.uid { + CometChatPushNotifications.shared.setActiveConversation(userId: uid) + } else if let guid = group?.guid { + CometChatPushNotifications.shared.setActiveConversation(groupId: guid) + } +} + +override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + CometChatPushNotifications.shared.clearActiveConversation() +} +``` + +## 7. Handle taps and calls via the delegate + +Adopt `CometChatPushNotificationsDelegate` to route notification taps and present the ongoing call screen once the SDK has accepted a call via CallKit: + +```swift AppDelegate+PN.swift +import CometChatSDK +import CometChatUIKitSwift +import CometChatPushNotificationsSwift +#if canImport(CometChatCallsSDK) +import CometChatCallsSDK +#endif + +extension AppDelegate: CometChatPushNotificationsDelegate { + + // MARK: - Chat notification taps + func navigateToChat(for user: CometChatSDK.User) { + pushMessages(user: user, group: nil) + } + + func navigateToChat(for group: CometChatSDK.Group) { + pushMessages(user: nil, group: group) + } + + func navigateToDefaultScreen() { + DispatchQueue.main.async { + self.topMostNavigationController()?.popToRootViewController(animated: true) + } + } + + // MARK: - Calls (CallKit already accepted — just present your ongoing-call screen) + #if canImport(CometChatCallsSDK) + func presentCallScreen(for call: CometChatSDK.Call, sessionId: String) { + DispatchQueue.main.async { + let ongoing = CometChatOngoingCall() + let isAudioOnly = (call.callType == .audio) + let builder = CometChatCallsSDK.CallSettingsBuilder() + .setIsAudioOnly(isAudioOnly) + ongoing.set(callSettingsBuilder: builder) + ongoing.set(callWorkFlow: .defaultCalling) + ongoing.set(sessionId: sessionId) + ongoing.modalPresentationStyle = .fullScreen + ongoing.setOnCallEnded { _ in + // ALWAYS report call end back to CallKit so the system UI dismisses. + CometChatPushNotifications.shared.endCallKitSession() + ongoing.dismiss(animated: true) + } + self.topMostViewController()?.present(ongoing, animated: true) + } + } + + func onCallCleanupComplete() { + // Called when the SDK fully ended the call (e.g. the user hit End on the + // native CallKit screen). Dismiss the ongoing-call screen if still on top. + DispatchQueue.main.async { + self.dismissIfPresenting(CometChatOngoingCall.self) + } + } + + func onCallMissed(call: CometChatSDK.Call, reason: CometChatMissedCallReason) { + // The SDK already posted the missed-call notification — hook for call logs / analytics. + } + + func onCallMuteStateChanged(isMuted: Bool) { + // Fires when the CallKit screen (or the Calls SDK) toggles mute. + } + #endif + + func onPushTokenRegistrationFailed(platform: CometChatPushTokenPlatform, + error: CometChatSDK.CometChatException) { + // Fires for automatic registrations too (VoIP capture, login re-registration). + print("Push token registration failed [\(platform.rawValue)]: \(error.errorDescription)") + } +} +``` + + +`pushMessages(user:group:)`, `topMostViewController()`, `topMostNavigationController()`, and `dismissIfPresenting(_:)` above are helpers you define in your `AppDelegate` — they know your app's navigation structure. Delegate callbacks can also fire from background threads (PushKit and notification-response callbacks in particular), so wrap all UIKit work in `DispatchQueue.main.async`. + + +## 8. Badge count + +CometChat's Enhanced Push Notification payload includes an `unreadMessageCount` field (total unread across all conversations). Enable it once on the dashboard: + +1. **CometChat Dashboard → Notification Engine → Settings → Preferences → Push Notification Preferences**. +2. Enable the **Unread Badge Count** toggle. + +With `enableBadgeCount: true` on the config, iOS updates the app-icon badge from the APNs payload automatically — no extra client code is needed to display it. Clear the badge and dismiss stale notifications every time the app resumes by calling `clearBadgeCount()` from `sceneDidBecomeActive` (already wired in step 5). + +## 9. Testing checklist + +1. Install on a **physical device**. Grant notification, microphone, and camera permissions when prompted. +2. Log in and confirm the delegate does **not** report `onPushTokenRegistrationFailed` — that means both the APNs device token and the VoIP token were registered against your providers. +3. Send a message from another user: + - App foregrounded on a different chat: banner appears (if `showInAppNotifications: true`). + - App foregrounded on the **same** chat: no banner (foreground suppression from step 6). + - App backgrounded / killed: notification appears; tapping opens the right conversation via `navigateToChat(for:)`. + - Long-press the notification → **Reply** — the quick-reply text is sent as a message in that conversation. +4. Trigger an incoming CometChat call and confirm: + - The **CallKit** incoming screen shows the caller with Accept / Decline, even on the lock screen. + - Accepting presents your ongoing call screen from `presentCallScreen(for:sessionId:)`. + - Ending the call from either the CallKit screen or your ongoing screen tears down cleanly (via `endCallKitSession()` and `onCallCleanupComplete()`). + - A missed call fires `onCallMissed(call:reason:)` and posts a missed-call notification. +5. Force-quit the app, trigger a call, and confirm the CallKit UI still appears — this exercises the PushKit cold-start path. + +## 10. Troubleshooting + +| Symptom | Quick checks | +| --- | --- | +| No notifications received | Confirm **Push Notifications** capability is enabled, the target's bundle ID matches the APNs Device provider in the dashboard, and your `AppDelegate` forwards `didReceiveRemoteNotification` to `handleBackgroundNotification(userInfo:)`. Verify APNs environment: `.p8` covers both dev and prod but the `aps-environment` entitlement (`development` vs `production`) must match your build. | +| `onPushTokenRegistrationFailed` fires | Verify `PROVIDER_ID` matches the dashboard exactly and that CometChat login has succeeded before you expect a token to bind. | +| Incoming call screen never shows | Confirm **Background Modes → Voice over IP** is enabled, `registerForVoIPPushes()` runs in `didFinishLaunchingWithOptions`, and the APNs VoIP provider is configured in the dashboard. | +| Two incoming-call screens (CallKit + in-app) | Call `.enable(inAppIncomingCall: false)` in your `UIKitSettings` so only CallKit shows the incoming UI. | +| CallKit screen never dismisses after call ends | Always call `CometChatPushNotifications.shared.endCallKitSession()` from your ongoing call screen's on-ended callback. | +| Nothing happens on a killed-app call | Ensure `notifyCallsSDKReady()` runs after `CometChatUIKit.init` succeeds — otherwise the SDK cannot present the ongoing screen once the user accepts from CallKit. | +| Build fails with `Unable to resolve module dependency` (SPM) | Add `cometchat/chat-sdk-ios` (≥ 4.1.5) and `cometchat/calls-sdk-ios` (≥ 5.0.0) as separate SPM packages — SPM does not resolve transitive deps for prebuilt xcframeworks. | +| Testing on the simulator | APNs, PushKit, and CallKit require a real device — the simulator only confirms the app compiles. | + +## Resources + + + Source, releases, and installation instructions (SPM & CocoaPods) for the drop-in push & VoIP SDK. + diff --git a/notifications/push-getting-started.mdx b/notifications/push-getting-started.mdx index f1ed69a21..b89d1b284 100644 --- a/notifications/push-getting-started.mdx +++ b/notifications/push-getting-started.mdx @@ -6,7 +6,7 @@ description: "Getting started with CometChat Push Notifications — enable push, The steps below are common to every platform. Complete them once, then follow your platform guide for app-specific wiring (Gradle, Podfile, manifest, service worker, token registration). Each platform guide links back here. -Which providers and credentials you need depends on your platform: **Android / Web / React Native (Android)** use **FCM**; **iOS / React Native (iOS) / Flutter (iOS)** use **APNs** (plus **APNs VoIP** for calls). iOS can optionally use FCM as a bridge. +Which providers and credentials you need depends on your platform: **Android / Web / React Native (Android)** use **FCM**; **iOS / React Native (iOS) / Flutter (iOS)** use **APNs** (plus **APNs VoIP** for calls). ## CometChat dashboard setup @@ -19,7 +19,7 @@ Which providers and credentials you need depends on your platform: **Android / W 2. Click **Add Credentials** and add the provider(s) your platform needs: - - **FCM** (Android, Web, React Native Android, Flutter Android, and optional iOS): upload the Firebase **service account JSON** (Firebase → Project settings → Service accounts → Generate new private key). + - **FCM** (Android, Web, React Native Android, Flutter Android): upload the Firebase **service account JSON** (Firebase → Project settings → Service accounts → Generate new private key). - **APNs Device** (iOS alerts): upload your `.p8` Auth Key with its **Key ID**, **Team ID**, and **Bundle ID**. - **APNs VoIP** (iOS calls): add a second provider with the same `.p8` (recommended for CallKit reliability). @@ -31,7 +31,7 @@ Which providers and credentials you need depends on your platform: **Android / W ## Firebase configuration -Required for FCM (Android, Web, React Native Android, Flutter Android) and for iOS when you use FCM or upload your APNs key to Firebase. +Required for FCM (Android, Web, React Native Android, Flutter Android). 1. In the [Firebase Console](https://console.firebase.google.com/), create or select a project and enable **Cloud Messaging**. 2. Register the app for your platform and download its config file: @@ -40,9 +40,6 @@ Required for FCM (Android, Web, React Native Android, Flutter Android) and for i Register your Android package name (the same as `applicationId` in `android/app/build.gradle`) and download **`google-services.json`** into `android/app/`. - - Register your iOS bundle ID, download **`GoogleService-Info.plist`** into your app target, and upload your APNs Auth Key under **Project settings → Cloud Messaging → Apple app configuration**. - Add a **Web** app (``), copy the **Firebase config** object, and under **Project settings → Cloud Messaging → Web Push certificates** generate/copy the **VAPID key**. @@ -79,11 +76,7 @@ After completing the setup above, follow the guide for your platform to wire the UI Kit implementation -} href="/notifications/ios-apns-push-notifications"> -UI Kit implementation - - -} href="/notifications/ios-fcm-push-notifications"> +} href="/notifications/ios-push-notifications"> UI Kit implementation diff --git a/sdk/ios/2.0/extensions.mdx b/sdk/ios/2.0/extensions.mdx index 2abc9ed62..abc932796 100644 --- a/sdk/ios/2.0/extensions.mdx +++ b/sdk/ios/2.0/extensions.mdx @@ -13,7 +13,7 @@ Extensions pickup where our core leaves. They help extend the functionality of C Extensions that help alert users of new messages. *Recommended for all apps.* -[Push Notification](/notifications/ios-fcm-push-notifications)\ +[Push Notification](/notifications/ios-push-notifications)\ [Email Notification](https://assets.cometchat.io/legacy-docs/notifications/email-notifications-extension-legacy.html)\ [SMS Notification](https://assets.cometchat.io/legacy-docs/notifications/sms-notifications-extension-legacy.html) From 72e281dd002d223ea6b76c1017fa7934c62b089c Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 28 Jul 2026 20:07:42 +0530 Subject: [PATCH 11/11] Revert unrelated link change in sdk/ios/2.0/extensions.mdx The legacy 2.0 extensions page is out of scope for the push SDK rewrite; the redirect on the old slug handles the old link. --- sdk/ios/2.0/extensions.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ios/2.0/extensions.mdx b/sdk/ios/2.0/extensions.mdx index abc932796..2abc9ed62 100644 --- a/sdk/ios/2.0/extensions.mdx +++ b/sdk/ios/2.0/extensions.mdx @@ -13,7 +13,7 @@ Extensions pickup where our core leaves. They help extend the functionality of C Extensions that help alert users of new messages. *Recommended for all apps.* -[Push Notification](/notifications/ios-push-notifications)\ +[Push Notification](/notifications/ios-fcm-push-notifications)\ [Email Notification](https://assets.cometchat.io/legacy-docs/notifications/email-notifications-extension-legacy.html)\ [SMS Notification](https://assets.cometchat.io/legacy-docs/notifications/sms-notifications-extension-legacy.html)