Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5033,7 +5033,9 @@
"pages": [
"notifications/push-overview",
"notifications/push-integration",
"notifications/push-customization"
"notifications/push-customization",
"notifications/android-push-notifications-flutter",
"notifications/ios-push-notifications-flutter"
]
},
{
Expand Down
Binary file added images/firebase-push-notifications.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/push-notifications-guide-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/push-notifications-guide-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/push-notifications-guide-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/push-notifications-guide-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
222 changes: 222 additions & 0 deletions notifications/android-push-notifications-flutter.mdx

Large diffs are not rendered by default.

223 changes: 223 additions & 0 deletions notifications/ios-push-notifications-flutter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
title: "iOS Push Notifications (Flutter UI Kit)"
description: "End-to-end walkthrough for recreating the CometChat push + CallKit experience from the Flutter UI Kit sample."
---

The Flutter UI Kit push-notification demo already solves permission prompts, Firebase/APNs registration, PushKit, and CallKit synchronization. This guide distills that reference implementation so you can bring the exact experience into any Flutter app that uses CometChat UI Kit and Calls UI Kit.

<Card
title="Reference implementation"
icon="github"
href="https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications"
>
Browse the full push-notification sample (Flutter + native iOS glue) to diff or copy files.
</Card>

<Note>
The steps below refer to the folder names inside the sample repo (for example
<a href="https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications/lib/notifications" target="_blank" rel="noopener noreferrer">lib/notifications</a>
or
<a href="https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/ios/Runner/AppDelegate.swift" target="_blank" rel="noopener noreferrer">ios/Runner/AppDelegate.swift</a>).
Copy the same structure into your project and only change identifiers (bundle ID, provider IDs, package name) to match your app.
</Note>

## Architecture map

| Sample path | Role inside the app | What to copy/replicate |
| --- | --- | --- |
| [`lib/notifications`](https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications/lib/notifications) | Models, platform services, push registry helpers, MethodChannel bridge handlers | Copy the directory as-is when starting a new app; keep the folder boundaries so imports resolve. |
| [`lib/main.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/main.dart) | Initializes Firebase, SharedPreferences, local notifications, and wires `APNSService.setupNativeCallListener` | Reuse the launch sequence so notification taps from a terminated state reach Flutter before `runApp`. |
| [`lib/dashboard.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/dashboard.dart) / [`lib/guard_screen.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/guard_screen.dart) | Boots CometChat UI Kit, requests permissions, registers Call/Notification listeners | Mirror the lifecycle hooks to initialize `APNSService` only once and capture pending notification taps. |
| [`ios/Runner/AppDelegate.swift`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/ios/Runner/AppDelegate.swift) | PushKit + CallKit bridge plus MethodChannel glue for VoIP token + call events | Start from the sample file and update the bundle identifiers or localized strings. |
| [`lib/notifications/services/pn_registry.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/notifications/services/pn_registry.dart) | Wraps `CometChatNotifications.registerPushToken` for APNs, VoIP, and FCM | Supply your CometChat provider IDs so registration works in every environment. |

## 1. Prerequisites

- 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 APNs + VoIP providers created.
- 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.

## 2. Prepare credentials before coding

### 2.1 Apple Developer portal

1. Generate an APNs Auth Key (`.p8`) and note the **Key ID** and **Team ID**.
2. Enable Push Notifications plus Background Modes → *Remote notifications* and *Voice over IP* on the bundle ID.
3. Create a VoIP Services certificate/key if you want separate credentials.

### 2.2 Firebase Console

1. Register the same bundle ID and download `GoogleService-Info.plist`.
2. Enable Cloud Messaging and, if needed, APNs authentication key under *Project Settings → Cloud Messaging*.

### 2.3 CometChat dashboard

1. Turn on the **Push Notifications** extension (V2).

<Frame>
<img src="/images/push-notifications-guide-1.png" alt="CometChat Dashboard - Push Notifications" />
</Frame>

2. Create one provider for APNs device pushes and (optionally) another for VoIP pushes.

<Frame>
<img src="/images/push-notifications-guide-3.png" alt="CometChat Dashboard - Push Notifications" />
</Frame>

3. Copy the generated provider IDs—they are required inside `CometChatConfig`.



### 2.4 Local configuration files

Update [`lib/cometchat_config.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/cometchat_config.dart) (or your own config file) so it exposes:

```dart
class CometChatConfig {
static const appId = "YOUR_APP_ID";
static const region = "YOUR_APP_REGION";
static const authKey = "YOUR_AUTH_KEY";
static const fcmProviderId = "FCM-PROVIDER-ID";
static const apnProviderId = "APNS-PROVIDER-ID";
static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; // optional but recommended
}
```

## 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`](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.

### 3.2 Wire the entry points

**[`lib/main.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/main.dart)**

- Initialize `SharedPreferencesClass`, Firebase, and `FlutterLocalNotificationsPlugin` before calling `runApp`.
- Store `NotificationLaunchHandler.pendingNotificationResponse` when the app is opened by tapping a notification while terminated.
- On iOS, call `APNSService.setupNativeCallListener(context)` from `initState` so Flutter reacts when the native CallKit UI changes state.

**[`lib/guard_screen.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/guard_screen.dart) / [`lib/dashboard.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/dashboard.dart) (or your first screen after login)**

- Ensure `CometChatUIKit.init()` and `CometChatUIKit.login()` finish before rendering the dashboard.
- Instantiate `APNSService` (iOS only) and call `apnsService.init(context)` inside `initState`.
- Register CometChat UI + Calls listeners (`CometChatUIEventListener`, `CometChatCallEventListener`, and `CallListener`) exactly once per session; the sample stores the listener IDs inside `APNSService`.
- Replay `NotificationLaunchHandler.pendingNotificationResponse` after the widget tree builds so taps from a killed app still navigate to `MessagesScreen`.
- Forward lifecycle changes to `IncomingCallOverlay` / `BoolSingleton` to hide stale overlays when the app resumes.

### 3.3 Align dependencies and configuration

Mirror the sample `pubspec.yaml` versions (update as needed when newer releases ship):

```yaml
dependencies:
firebase_core: ^3.0.0
firebase_messaging: ^15.0.0
flutter_apns_x: ^2.1.1
flutter_callkit_incoming: ^2.0.3+3
flutter_local_notifications: ^16.0.0
cometchat_chat_uikit: ^5.0.0
cometchat_calls_uikit: ^5.0.0
permission_handler: ^11.3.0
```

Run `flutter pub get`, then `flutterfire configure` if you still need to generate `firebase_options.dart`.

## 4. Configure the native iOS layer

### 4.1 Capabilities and Info.plist

1. Open `ios/Runner.xcworkspace` in Xcode.
2. Under *Signing & Capabilities*, enable **Push Notifications** and **Background Modes** (Remote notifications + Voice over IP).
3. Add microphone, camera, Bluetooth, and notification permission strings to `Info.plist`.
4. Set the development team that has access to the APNs/VoIP keys you generated earlier.

### 4.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
let appInfoChannel = FlutterMethodChannel(
name: "com.flutter_application_demo/ios",
binaryMessenger: controller.binaryMessenger
)
```

Handle at least `getAppInfo`, `endCall`, `onCallAcceptedFromNative`, and `onCallEndedFromNative`, mirroring the Dart side (`APNSService.setupNativeCallListener`).

- **Firebase + plugin registration** – call `FirebaseApp.configure()` before `GeneratedPluginRegistrant.register(with: self)`.
- **PushKit** – instantiate `PKPushRegistry`, set `desiredPushTypes = [.voIP]`, and forward the token inside `pushRegistry(_:didUpdate:for:)` via `SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(tokenHex)`.
- **CallKit** – configure `CXProviderConfiguration`, keep a `CXCallController`, and implement `provider(_:perform: CXAnswerCallAction)` / `provider(_:perform: CXEndCallAction)` so native actions propagate to Flutter.
- **Incoming push handler** – inside `pushRegistry(_:didReceiveIncomingPushWith:)`, convert the CometChat payload into `flutter_callkit_incoming.Data`, set `extra` with the raw payload, and call `showCallkitIncoming(..., fromPushKit: true)`.
- **UUID helper** – reuse `createUUID(sessionid:)` to produce valid `UUID`s from long CometChat session IDs; this lets CallKit correlate calls even if the payload string exceeds 32 characters.

If you change the MethodChannel name in Swift, remember to update `APNSService.platform` in Dart to match.

## 5. Token registration and runtime events

### 5.1 Standard APNs tokens

`APNSService` hooks into `FirebaseMessaging.instance.getAPNSToken()` (and `onTokenRefresh`) before calling:

```dart
await CometChatPushRegistry.register(
token: token,
isFcm: false,
isVoip: false,
);
```

This registers the device token against the APNs provider selected in `CometChatConfig.apnProviderId`.

### 5.2 VoIP tokens

- Capture the PushKit token in `AppDelegate.pushRegistry(_:didUpdate:for:)`.
- Forward it to Flutter via the MethodChannel or register it directly from Swift by invoking `CometChatPushRegistry` through `SwiftFlutterCallkitIncomingPlugin`.
- If you keep the Dart implementation, emit a MethodChannel call named `onVoipToken` and handle it in `APNSService` by calling `CometChatPushRegistry.register(token: token, isFcm: false, isVoip: true);`.

### 5.3 Local notifications and navigation

- `APNSService._showNotification` displays a local notification when the incoming CometChat message does not belong to the currently open conversation.
- `LocalNotificationService.handleNotificationTap` parses the payload, fetches the relevant user/group, and pushes `MessagesScreen`.
- `NotificationLaunchHandler.pendingNotificationResponse` caches taps triggered while the app is terminated; replay it on the dashboard once the UI is ready.

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

## 6. Testing checklist

1. Run the app on a physical device in debug first. Grant notification, microphone, camera, and Bluetooth permissions when prompted.
2. From the CometChat Dashboard, send a standard message notification. Verify:
- 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.

## 7. Troubleshooting tips

| Symptom | Quick checks |
| --- | --- |
| No VoIP pushes | Entitlements missing? Ensure Push Notifications + Background Modes (VoIP) are enabled and the bundle ID matches the CometChat provider. |
| CallKit UI never dismisses | Make sure `endCall(callUUID:)` reports to `CXProvider`, runs a `CXEndCallAction`, **and** calls `SwiftFlutterCallkitIncomingPlugin.sharedInstance?.endCall`. |
| Flutter never receives native call events | Confirm the MethodChannel names match between Swift and Dart, and `APNSService.setupNativeCallListener` runs inside `initState`. |
| Token registration errors | Double-check `CometChatConfig` provider IDs, and verify you call `registerPushToken` after `CometChatUIKit.login` succeeds. |
| Notification taps ignored | Ensure you replay `NotificationLaunchHandler.pendingNotificationResponse` **after** the navigator key is ready (typically `WidgetsBinding.instance.addPostFrameCallback`). |

## Additional resources

- [Flutter push-notification sample app (GitHub)](https://github.com/cometchat/cometchat-uikit-flutter/tree/v5/sample_app_push_notifications)
- [CometChat Push Notification extension docs](/notifications/push-integration.mdx)
- [Flutter UI Kit quickstart](https://www.cometchat.com/docs/ui-kit/flutter/getting-started)
- [APNs configuration reference](/notifications/ios-apns-push-notifications.mdx)