Skip to main content

Documentation Index

Fetch the complete documentation index at: https://cometchat-22654f5b-docs-android-v6-beta2.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

iOS UI Kit Sample App

Reference implementation of iOS UIKit, APNs and Push Notification Setup.

What this guide covers

  • CometChat dashboard setup (enable push, add APNs Device + APNs VoIP providers) with screenshots.
  • APNs + PushKit/CallKit wiring (tokens, delegates, CallKit).
  • Incoming message/call handling and deep links.
  • Badge count and grouped notifications.
  • Payload customization and testing.

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.

1. Enable push and add providers (CometChat Dashboard)

  1. Go to Notifications → Settings and enable Push Notifications.
Enable Push Notifications
  1. 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.
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

  • 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.
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)
    }

}

4. Register APNs device + VoIP tokens with CometChat

  • In your AppDelegate.swift, implement the following methods to handle APNs registration success and failure, and to register the device token with CometChat.
  • Make sure to import the necessary modules at the top of the file.
  • Complete your AppDelegate.swift as shown below:
import UIKit
import PushKit
import CometChatSDK
import CometChatUIKitSwift

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var pushRegistry: PKPushRegistry?
    let cometchatAPNsHelper = CometChatAPNsHelper()
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {

        UNUserNotificationCenter.current().delegate = self

        cometchatAPNsHelper.configurePushNotification(application: application, delegate: self)

        // Initialize PushKit
        initializePushKit()

        return true
    }

    // MARK: - APNs Registration Success
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("APNs Device token received!")

        if CometChat.getLoggedInUser() != nil {
            print("User is logged in, registering APNs token...")
            cometchatAPNsHelper.registerTokenForPushNotification(deviceToken: deviceToken)
        } else {
            print("User NOT logged in yet, will register token after login")
            // Store token for later registration
            let hexString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
            UserDefaults.standard.set(hexString, forKey: "pendingAPNsToken")
        }
    }

    // MARK: - APNs Registration Failure
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register for APNs: \(error.localizedDescription)")
    }
}

5. Unregister the token on logout

Before logging the user out, unregister the push token so the device stops receiving notifications for that user.
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.

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

  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

CometChat sends APNs payloads with this structure (relevant fields):
{
  "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.

6.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:
// 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.

6.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:
func sceneDidBecomeActive(_ scene: UIScene) {
    UIApplication.shared.applicationIconBadgeNumber = 0
}
This keeps the badge in sync with the actual unread state.

7. Navigation from notifications

When the user taps a notification, use userNotificationCenter(_:didReceive:withCompletionHandler:) to extract conversation details and navigate to the correct screen.
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.

8. Testing checklist

  1. Install on a device; grant notification permission. Verify APNs device token logs.
  2. Log in, then confirm both device + VoIP tokens register with CometChat (success callbacks).
  3. Send a message from another user:
    • Foreground: ensure willPresent shows your chosen presentation.
    • Background/terminated: tapping opens the correct conversation.
  4. Trigger an incoming call; CallKit UI should show caller info. Accept should join the call; Decline should reject via CometChat and end CallKit.
  5. Rotate tokens (reinstall or toggle VoIP) to ensure re-registration works.

6. Troubleshooting

SymptomQuick checks
No pushesEntitlements set, APNs provider creds correct, bundle ID matches dashboard, permission granted.
Token registration failsRun after login; provider IDs correct for device vs VoIP.
Taps do nothingVerify notification center delegate and navigation readiness before routing.
Call UI missingEnsure PushKit delegate fires, CallKit capabilities enabled, VoIP provider ID set.
Audio errorsConfigure AVAudioSession for playAndRecord when reporting/accepting calls.
Badge count not showingVerify Unread Badge Count is enabled in CometChat Dashboard and that your app reads unreadMessageCount from the payload.
Notification tap does not navigateEnsure UNUserNotificationCenterDelegate is set, payload contains receiverType/sender/receiver, and root view controller is ready.