Skip to main content

React Native UI Kit Sample App

Reference implementation of React Native UI Kit, APNs and Push Notification Setup.

What this guide covers

  • CometChat dashboard setup (enable push, add an APNs provider) with screenshots.
  • Apple setup (APNs key, capabilities, Info.plist).
  • Wiring the package’s notification and call handlers into your app.
  • Native iOS setup — a few AppDelegate lines; no PushKit or CallKit code to write.
  • Token registration (APNs + VoIP), notification/call handling, navigation, testing, and troubleshooting.
  • App icon badge count using unreadMessageCount from the CometChat push payload.

How APNs + CometChat work together

  • APNs’s role: Issues the device token for chat notifications and, through PushKit, the VoIP token for calls, and delivers both kinds of push. No Firebase is needed on iOS.
  • CometChat’s role: The APNs provider you add in the CometChat dashboard holds your .p8 key. When init() runs after login, the package registers both tokens with that one provider, and CometChat sends chat pushes and VoIP call pushes through APNs.
  • The package’s role: It creates and owns the PushKit registry and reports every VoIP push to CallKit — before React Native starts in a killed app. Every CometChat action runs in JavaScript through the Chat SDK your app already uses.
  • Flow: Permission prompt → APNs issues the device token and PushKit the VoIP token → after login, init() registers both with AppCredentials.apnsProviderId → CometChat sends to APNs → iOS shows the notification, or the package reports the call to CallKit → your onNotificationTap, onCallAccepted and onCallEnded handlers navigate.

1. Enable push and add providers (CometChat Dashboard)

  1. Go to Notifications → Settings and enable Push Notifications.
Enable Push Notifications
  1. Click Add Credentials, choose APNs, upload your .p8 key with its Key ID and Team ID, and copy the Provider ID. One APNs provider covers both chat notifications and VoIP call pushes.
Upload APNs credentials
Keep the provider ID—you’ll use it in AppCredentials.apnsProviderId.

2. Prepare Apple credentials

2.1 Apple Developer portal

  1. Generate an APNs Auth Key (.p8) and note the Key ID and Team ID.
  2. Enable Push Notifications on your app’s bundle ID.
.p12 certificates are deprecated. Apple recommends .p8 Auth Keys for push notifications: they never expire and work across all your apps.

3. Local configuration file

Create src/AppCredentials.ts with your app credentials and provider IDs. The same file serves the Android guide:
src/AppCredentials.ts

4. Bring the push package into React Native

4.1 Install the package

Keep the Podfile’s platform :ios, min_ios_version_supported from the React Native template. Don’t lower it: current React Native requires iOS 15.1, and a lower platform fails the build — for example with 'hermes/hermes.h' file not found.
Remove other push and call libraries first@react-native-firebase/messaging, @notifee/react-native, react-native-callkeep, react-native-voip-push-notification — along with their code and native setup. Each registers its own push handler or PushKit registry, and every notification or call then arrives twice.

4.2 Wire the entry points

The JavaScript below is the same for Android and iOS — one set of files serves both guides. Lines for one platform do nothing on the other: registerBackgroundCallTask() and notificationSmallIcon only apply on Android, and waiting for each permission answer before the next request matters only on Android.
index.js — the same file as on Android. registerBackgroundCallTask() is a no-op on iOS, where CallKit handles a decline in a killed app:
index.js
src/navigation/navigationRef.ts — a notification tap or answered call that launched the app arrives before your navigator exists, so every navigation waits for it:
src/navigation/navigationRef.ts
src/push/pushNotifications.ts — everything push does for the logged-in user: the tap, call-accepted and call-ended handlers, the permission requests, and init():
src/push/pushNotifications.ts
Not using the UI Kit? Delete the @cometchat/chat-uikit-react-native import and the emitMessageEvent block in markConversationRead, point SCREENS and the route params at your own screens, and in logout.ts call CometChat.logout() instead of CometChatUIKit.logout().
App.tsx — call usePushOnLogin() once, in a component that renders after CometChat is initialized, and pass navigationRef to your NavigationContainer:
App.tsx
usePushOnLogin() starts push after a fresh login and when a session is restored on launch, and removes the handlers when the user logs out — so a later login never registers them twice.

4.3 Align dependencies and configuration

  • Peer dependencies: @cometchat/chat-sdk-react-native (or the UI Kit) for chat, @cometchat/calls-sdk-react-native for calls, and React Navigation for the handlers above.
  • init() options:
    • fcmProviderId (Android) and apnsProviderId (iOS) — from step 1.
    • notificationSmallIcon — the Android status-bar icon.
    • showInForeground (default false) — show chat notifications while the app is open.
    • ringInForeground (default true) — ring with the system call UI while the app is open. Set false when your app shows its own incoming-call screen, as the UI Kit does.
    • voip (default true), androidChannelId, androidChannelName.

5. Configure the native iOS layer

5.1 Capabilities and Info.plist

  1. Open ios/<App>.xcworkspace in Xcode.
  2. Under Signing & Capabilities, enable Push Notifications and Background Modes with Voice over IP, Remote notifications, and Audio, AirPlay, and Picture in Picture.
  3. Add the microphone and camera usage strings to Info.plist — a call can’t use either without them:
ios/<App>/Info.plist
Enable Push Notifications and Background Modes for APNs

5.2 AppDelegate.swift

Replace ios/<App>/AppDelegate.swift with this — React Native’s current template plus the push lines. Set withModuleName to your app’s name:
ios/<App>/AppDelegate.swift
You don’t write PushKit or CallKit code, and you don’t set a UNUserNotificationCenter delegate — the package installs its own at launch to handle foreground notifications and taps.
Don’t create a PKPushRegistry of your own. The package owns it, and a second registry — yours or a library’s — makes iOS deliver every VoIP push twice. If another library must own PushKit, skip registerForVoIPPushes() and forward that registry’s didUpdate and didReceiveIncomingPushWith callbacks to CometChatPushNotificationsAppDelegate.didUpdateVoIPToken(_:) and .didReceiveIncomingVoIPPush(_:), calling completion() after it.
Older Swift template (an RCTAppDelegate subclass): call registerForVoIPPushes() before return super.application(...), which starts React Native, and add the same token method. Objective-C AppDelegate.mm: the package’s iOS entry points are Swift-only, so move the AppDelegate to Swift first — the React Native Upgrade Helper shows the change.

6. Token registration and runtime events

6.1 Standard APNs tokens

didRegisterAPNsToken(_:) hands the device token to the package, and init() registers it with your APNs provider for the logged-in user — re-registering it whenever iOS issues a new one. setupPushOnLogin() asks for notification permission before init(). On iOS requestCallPermissions() does nothing: iOS asks for the microphone and camera the first time a call uses them.

6.2 VoIP tokens

registerForVoIPPushes() creates the PushKit registry at launch, and PushKit hands over the VoIP token right away — before React Native runs. The package holds it, and init() registers it with the same APNs provider. If init() runs a moment before login finishes, registration retries 5 times, 3 seconds apart.

6.3 Local notifications and navigation

  • App in the background or killed: iOS shows the APNs notification.
  • App open: the package’s notification delegate shows the banner when showInForeground is true; otherwise the payload goes to onMessageReceived.
  • Tap: onNotificationTap fires, and openFromNotification marks the conversation read, then opens the thread for a thread reply, otherwise the conversation. A tap that launched the app is held until your handler subscribes, and navigation waits for the navigator.

6.4 Call events

Killed-app VoIP: when a VoIP push wakes a killed app, the package reports the call to CallKit before React Native is ready. When the user answers, the app starts, init() delivers the answered call, and onCallAccepted opens your call screen — the call is already accepted. This is why registerForVoIPPushes() runs before React Native starts (step 5.2).

6.5 Unregister on logout

Add src/push/logout.ts and call it from your logout button instead of logging out directly:
src/push/logout.ts
unregister() must run before logout. It needs the session’s auth token — after logout it fails, and the device keeps receiving notifications for the user who just logged out.

7. Badge count using unreadMessageCount

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 is handled by the server: CometChat sets aps.badge in the push payload, and iOS updates the app icon badge when the notification is delivered — no dependency or client code required.

7.1 Enable unread badge count on the CometChat Dashboard

  1. Go to CometChat Dashboard → Notifications → 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.

7.2 Expected payload format

CometChat sends APNs payloads with this structure (relevant fields):
The aps.badge field is set by CometChat server-side, so iOS updates the badge when the push is delivered. In JavaScript (onMessageReceived), the package hands unreadMessageCount over as a string, as on Android.

8. Testing checklist

Use a physical iPhone — the Simulator can’t receive APNs or VoIP pushes — and a release build for killed-app calls.
  1. First launch: log in and allow notifications. Then send a message from another user — it must arrive.
  2. Chat notifications:
    • App open: exactly one banner (showInForeground: true).
    • App in the background: a notification appears; tapping it opens the conversation.
    • App killed: tapping the notification starts the app in the conversation.
    • A thread reply opens the thread; a group message opens the group.
  3. Calls, app killed (locked and unlocked):
    • CallKit shows the call with Accept and Decline.
    • Accept connects the call with audio both ways.
    • Decline shows the call as rejected on the caller’s side.
    • The caller cancelling stops the ring.
  4. Calls, app in the background: CallKit rings, and ending the call from the iOS call screen closes your call screen.
  5. Calls, app open: only your in-app incoming-call screen rings (ringInForeground: false).
  6. Logout: log out, send a message from another user — nothing arrives. Log in as another user — only that user’s notifications arrive.

9. Troubleshooting tips