Documentation Index
Fetch the complete documentation index at: https://cometchat-22654f5b-eng-35741-widget-builder.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a Card Schema JSON — a structured layout rendered natively by the CometChat Cards library. The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the Dashboard Setup Guide for step-by-step instructions.
CometChatCardsSwift
CometChatCardsSwift is the rendering library for Campaigns. The NotificationFeedItem.content field contains a Card Schema JSON — a structured layout definition. CometChatCardsSwift takes this JSON and produces a native UIKit view. Without it, content is just a raw dictionary.
The library:
- Parses the JSON schema into typed Swift models.
- Renders native UIKit views (no web views) for each element (text, images, buttons, rows, etc.).
- Handles light/dark mode color resolution from themed color pairs.
- Manages interactive actions — button taps emit structured
CometChatCardActionEvent objects with the action type and element ID.
- Supports dynamic content — accordions expand/collapse, tabs switch panels, all with proper auto-layout height changes.
If you use CometChatNotificationFeed (UI Kit), the Cards library is used internally and you don’t interact with it directly. If you’re building a custom feed UI, you import CometChatCardsSwift and use CometChatCardView to render each item’s content.
Setting Up CometChatCardsSwift
Installation
Swift Package Manager
In Xcode: File → Add Package Dependencies → enter:
https://github.com/cometchat/cometchat-cards-sdk-ios
Add CometChatCardsSwift to your target.
CocoaPods
pod 'CometChatCardsSwift'
No initialization is required. The library is stateless — import it and start rendering.
Rendering Feed Items as Cards
import CometChatSDK
import CometChatCardsSwift
// After fetching items via NotificationFeedRequest
request.fetchNext(onSuccess: { items in
for item in items {
guard let jsonData = try? JSONSerialization.data(withJSONObject: item.content),
let jsonString = String(data: jsonData, encoding: .utf8) else { continue }
let cardView = CometChatCardView(frame: .zero)
cardView.translatesAutoresizingMaskIntoConstraints = false
cardView.themeMode = .auto
cardView.cardJson = jsonString
cardView.actionCallback = { actionEvent in
// Report engagement on every action
CometChat.reportFeedEngagement(item, interactionString: actionEvent.elementId, onSuccess: {}, onError: { _ in })
switch actionEvent.action {
case .openUrl(let url, _):
if let link = URL(string: url) {
UIApplication.shared.open(link)
}
case .chatWithUser(let uid):
// Navigate to 1-on-1 chat
break
case .chatWithGroup(let guid):
// Navigate to group chat
break
default:
break
}
}
container.addSubview(cardView)
}
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
CometChatCardView Properties
| Property | Type | Description |
|---|
cardJson | String? | Set this to render a card. Triggers a full re-render. |
themeMode | CometChatCardThemeMode | .auto (follows system), .light, or .dark. |
actionCallback | ((CometChatCardActionEvent) -> Void)? | Called when user taps a button, link, or icon button inside the card. |
themeOverride | CometChatCardThemeOverride? | Override default theme colors without modifying card JSON. |
Theme Override
Override default card theme colors to match your app’s brand:
var override = CometChatCardThemeOverride()
override.textColor = CometChatCardColorValue(light: "#1A1A1A", dark: "#F0F0F0")
override.buttonFilledBg = CometChatCardColorValue(light: "#6852D6", dark: "#8B7AE8")
override.buttonFilledText = CometChatCardColorValue(light: "#FFFFFF", dark: "#FFFFFF")
override.fontFamily = "Avenir"
cardView.themeOverride = override
Content Size Changes
Cards with expandable content (accordions, tabs) change height dynamically. Observe size changes to update your layout:
NotificationCenter.default.addObserver(
forName: CometChatCardView.contentSizeDidChangeNotification,
object: cardView,
queue: .main
) { _ in
tableView.beginUpdates()
tableView.endUpdates()
}
Key Concepts
| Concept | Description |
|---|
| NotificationFeedItem | A single notification in the feed. Contains Card Schema JSON in its content field, a category for filtering, timestamps, and metadata. |
| NotificationCategory | A category label used for filter chips (e.g., “Promotions”, “Updates”). |
| Card Schema JSON | The fully rendered card layout (images, text, buttons) inside NotificationFeedItem.content. Passed directly to the CometChat Cards renderer. |
| PushNotification | Represents a campaign push notification payload received via APNs. |
How Cards Render in the Notification Feed
Each NotificationFeedItem has a content field containing a [String: Any] dictionary — this is the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer library.
The rendering flow:
- Fetch feed items via
NotificationFeedRequest
- For each item, extract
item.content — this is the Card Schema JSON
- Pass to the Cards renderer (
CometChatCardView)
- The renderer produces a native UIKit view from the JSON
Card Schema JSON Structure
{
"version": "1.0",
"body": [
{ "type": "image", "id": "img_1", "url": "https://...", "height": 200 },
{ "type": "text", "id": "txt_1", "content": "Flash Sale!", "variant": "heading2" },
{ "type": "button", "id": "btn_1", "label": "Shop Now", "action": { "type": "openUrl", "url": "https://..." } }
],
"style": { "background": {"light": "#FFFFFF", "dark": "#1E1E1E"}, "borderRadius": 12, "padding": 16 },
"fallbackText": "Flash Sale! Shop Now: https://..."
}
The body array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
Retrieve Notification Feed Items
Use NotificationFeedRequest to fetch a paginated list of feed items. Uses cursor-based pagination internally.
Build the Request
let request = NotificationFeedRequest.NotificationFeedRequestBuilder()
.set(limit: 20)
.set(readState: .unread)
.set(category: "Updates")
.build()
Builder Parameters
| Method | Type | Default | Description |
|---|
set(limit:) | Int | 20 | Items per page. |
set(readState:) | FeedReadState | All | Filter by .read or .unread. Omit for all. |
set(category:) | String | nil | Filter by category name. Sent as templateCategory query param. If categoryId is also set, categoryId takes priority. |
set(categoryId:) | String | nil | Filter by category ID. Sent as templateCategory query param. Takes priority over category if both are set. |
set(channelId:) | String | nil | Filter by channel. |
set(tags:) | [String] | nil | Filter by tags (comma-joined). |
set(dateFrom:) | String | nil | ISO 8601 date — items sent on or after. |
set(dateTo:) | String | nil | ISO 8601 date — items sent on or before. |
set(category:) and set(categoryId:) both map to the same server-side filter (templateCategory). Use one or the other — not both. If both are provided, categoryId silently overwrites category.
Fetch Items
request.fetchNext(onSuccess: { items in
for item in items {
let cardJson = item.content
// Pass cardJson to CometChatCardView
}
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
Call fetchNext() repeatedly for pagination. When the server has no more items, subsequent calls return an empty array.
NotificationFeedItem Fields
| Field | Type | Description |
|---|
id | String | Unique item identifier. |
category | String | Notification category (e.g., “promotions”). |
categoryId | String | Category ID. |
content | [String: Any] | Card Schema JSON — pass to CometChat Cards renderer. |
readAt | Double | Unix timestamp when read, or 0 if unread. |
deliveredAt | Double | Unix timestamp when delivered, or 0. |
sentAt | Double | Unix timestamp when sent. |
metadata | [String: Any] | Custom key-value metadata. |
tags | [String] | Tags for filtering. |
sender | String | Sender identifier. |
receiver | String | Receiver identifier. |
receiverType | String | Receiver type. |
isRead | Bool | Computed — true if readAt != 0. |
Retrieve Notification Categories
Use NotificationCategoriesRequest to fetch available categories for filter chips.
let categoriesRequest = NotificationCategoriesRequest.NotificationCategoriesRequestBuilder()
.set(limit: 50)
.build()
categoriesRequest.fetchNext(onSuccess: { categories in
for category in categories {
print("Category: \(category.label)")
}
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
NotificationCategory Fields
| Field | Type | Description |
|---|
id | String | Category identifier. |
label | String | Display label for filter UI. |
Real-Time Notification Feed Listener
Listen for new feed items arriving via WebSocket. This listener is independent from CometChatMessageDelegate, CometChatGroupDelegate, and CometChatCallDelegate.
CometChat.addNotificationFeedListener("feedListener", self)
// Implement CometChatNotificationFeedDelegate
extension MyViewController: CometChatNotificationFeedDelegate {
func onFeedItemReceived(feedItem: NotificationFeedItem) {
print("New item: \(feedItem.id)")
let cardJson = feedItem.content
// Insert at top of feed and render
}
}
Remove the listener when no longer needed:
CometChat.removeNotificationFeedListener("feedListener")
Mark Feed Item as Read
Mark a single item as read. Idempotent — safe to call multiple times.
CometChat.markFeedItemAsRead(feedItem, onSuccess: {
print("Marked as read")
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
Mark Feed Item as Delivered
Mark a single item as delivered. Idempotent.
CometChat.markFeedItemAsDelivered(feedItem, onSuccess: {
// Success
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
Report Engagement
Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent per topic.
CometChat.reportFeedEngagement(feedItem, interactionString: "clicked", onSuccess: {
// Success
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
The interactionString parameter is a free-form string describing the engagement (e.g., “viewed”, “clicked”, “interacted”).
Get Unread Count
Fetch the total number of unread notification feed items. Optionally filter by category.
CometChat.getNotificationFeedUnreadCount(category: nil, onSuccess: { count in
print("Unread: \(count)")
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
Fetch Single Feed Item
Fetch a specific item by ID — useful for deep linking from push notifications.
CometChat.getNotificationFeedItem(id: "item-id-123", onSuccess: { item in
let cardJson = item.content
// Render the card
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
})
Push Notification Tracking
When a campaign push notification arrives via APNs, use these methods to report delivery and click engagement.
Setting Up Notification Service Extension
- In Xcode: File → New → Target → select Notification Service Extension.
- Enable App Groups on both your main app target and the extension target with the same group ID (e.g.,
group.com.yourapp.cometchat).
- Add
CometChatSDK as a dependency to the extension target.
Identifying Campaign Push Notifications
Campaign pushes contain a cometchat dictionary with "type": "business_messaging":
func isCampaignNotification(userInfo: [AnyHashable: Any]) -> Bool {
if let cometchat = userInfo["cometchat"] as? [String: Any],
let type = cometchat["type"] as? String,
type == "business_messaging" {
return true
}
return false
}
Push Payload Structure
{
"aps": { "alert": { "title": "...", "body": "..." }, "badge": 1, "mutable-content": 1 },
"cometchat": {
"type": "business_messaging",
"appId": "your-app-id",
"campaignId": "campaign-cuid",
"notificationId": "notification-cuid",
"pushNotificationId": "uuid-for-push-tracking",
"uid": "target-user-id",
"sentAt": "2026-05-21T12:43:55.206Z"
}
}
Mark Push Notification as Delivered
Call this in your Notification Service Extension:
import UserNotifications
import CometChatSDK
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)!
// Required for extensions to share auth state via App Group container
CometChat.setExtensionGroupID(id: "group.your.app.group")
let userInfo = bestAttemptContent.userInfo
if let cometchat = userInfo["cometchat"] as? [String: Any],
let type = cometchat["type"] as? String,
type == "business_messaging" {
let push = PushNotification.fromPayload(cometchat)
CometChat.markPushNotificationDelivered(push, onSuccess: {}, onError: { _ in })
}
contentHandler(bestAttemptContent)
}
}
Mark Push Notification as Clicked
Call this when the user taps the push notification:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let cometchat = userInfo["cometchat"] as? [String: Any],
let type = cometchat["type"] as? String,
type == "business_messaging" {
let push = PushNotification.fromPayload(cometchat)
CometChat.markPushNotificationClicked(push, onSuccess: {}, onError: { _ in })
navigateToNotificationsTab()
completionHandler()
return
}
completionHandler()
}
PushNotification Fields
| Field | Type | Description |
|---|
id | String | Push notification ID from the push payload. |
announcementId | String | Notification/announcement ID. |
campaignId | String? | Campaign ID if from a campaign. |
source | String | Always “campaign” for notification feed pushes. |
FeedReadState Enum
| Case | Value | Description |
|---|
.read | "read" | Only read items. |
.unread | "unread" | Only unread items. |
If not set, returns all items (default).
Supported Card Actions
When a user taps a button or link inside a card, the action callback receives one of these action types:
| Action Type | Parameters | Description |
|---|
openUrl | url, openIn | Open a URL in browser or webview. |
chatWithUser | uid | Navigate to 1:1 chat. |
chatWithGroup | guid | Navigate to group chat. |
sendMessage | text, receiverUid, receiverGuid | Send a text message. |
copyToClipboard | value | Copy text to clipboard. |
downloadFile | url, filename | Download a file. |
initiateCall | callType (audio/video), uid, guid | Start a call. |
apiCall | url, method, headers, body | Make an HTTP request. |
customCallback | callbackId, payload | App-specific logic. |