@cometchat/skills 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -17
  3. package/bin/install.js +389 -43
  4. package/package.json +19 -9
  5. package/skills/cometchat/SKILL.md +1066 -265
  6. package/skills/cometchat-android-v5/SKILL.md +120 -0
  7. package/skills/cometchat-android-v5-components/SKILL.md +430 -0
  8. package/skills/cometchat-android-v5-core/SKILL.md +468 -0
  9. package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
  10. package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
  11. package/skills/cometchat-android-v5-features/SKILL.md +130 -0
  12. package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
  13. package/skills/cometchat-android-v5-production/SKILL.md +94 -0
  14. package/skills/cometchat-android-v5-push/SKILL.md +620 -0
  15. package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
  16. package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
  17. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
  18. package/skills/cometchat-android-v6/SKILL.md +107 -0
  19. package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
  20. package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
  21. package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
  22. package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
  23. package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
  24. package/skills/cometchat-android-v6-core/SKILL.md +354 -0
  25. package/skills/cometchat-android-v6-events/SKILL.md +212 -0
  26. package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
  27. package/skills/cometchat-android-v6-features/SKILL.md +130 -0
  28. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
  29. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
  30. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
  31. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
  32. package/skills/cometchat-android-v6-production/SKILL.md +187 -0
  33. package/skills/cometchat-android-v6-push/SKILL.md +274 -0
  34. package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
  35. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
  36. package/skills/cometchat-angular-components/SKILL.md +706 -0
  37. package/skills/cometchat-angular-core/SKILL.md +444 -0
  38. package/skills/cometchat-angular-customization/SKILL.md +424 -0
  39. package/skills/cometchat-angular-features/SKILL.md +447 -0
  40. package/skills/cometchat-angular-patterns/SKILL.md +447 -0
  41. package/skills/cometchat-angular-placement/SKILL.md +685 -0
  42. package/skills/cometchat-angular-production/SKILL.md +415 -0
  43. package/skills/cometchat-angular-theming/SKILL.md +365 -0
  44. package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
  45. package/skills/cometchat-astro-patterns/SKILL.md +3 -3
  46. package/skills/cometchat-components/SKILL.md +11 -0
  47. package/skills/cometchat-core/SKILL.md +11 -6
  48. package/skills/cometchat-customization/references/component-catalog.md +1 -1
  49. package/skills/cometchat-features/SKILL.md +176 -108
  50. package/skills/cometchat-flutter-v5/SKILL.md +167 -0
  51. package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
  52. package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
  53. package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
  54. package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
  55. package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
  56. package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
  57. package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
  58. package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
  59. package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
  60. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
  61. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
  62. package/skills/cometchat-flutter-v6/SKILL.md +445 -0
  63. package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
  64. package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
  65. package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
  66. package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
  67. package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
  68. package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
  69. package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
  70. package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
  71. package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
  72. package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
  73. package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
  74. package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
  75. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
  76. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
  77. package/skills/cometchat-ios/SKILL.md +323 -0
  78. package/skills/cometchat-ios-components/SKILL.md +651 -0
  79. package/skills/cometchat-ios-core/SKILL.md +822 -0
  80. package/skills/cometchat-ios-customization/SKILL.md +825 -0
  81. package/skills/cometchat-ios-features/SKILL.md +725 -0
  82. package/skills/cometchat-ios-placement/SKILL.md +750 -0
  83. package/skills/cometchat-ios-production/SKILL.md +730 -0
  84. package/skills/cometchat-ios-push/SKILL.md +582 -0
  85. package/skills/cometchat-ios-theming/SKILL.md +631 -0
  86. package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
  87. package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
  88. package/skills/cometchat-native-components/SKILL.md +594 -0
  89. package/skills/cometchat-native-core/SKILL.md +407 -0
  90. package/skills/cometchat-native-customization/SKILL.md +594 -0
  91. package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
  92. package/skills/cometchat-native-features/SKILL.md +608 -0
  93. package/skills/cometchat-native-placement/SKILL.md +500 -0
  94. package/skills/cometchat-native-production/SKILL.md +694 -0
  95. package/skills/cometchat-native-push/SKILL.md +593 -0
  96. package/skills/cometchat-native-testing/SKILL.md +583 -0
  97. package/skills/cometchat-native-theming/SKILL.md +656 -0
  98. package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
  99. package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
  100. package/skills/cometchat-production/SKILL.md +60 -0
  101. package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
@@ -0,0 +1,582 @@
1
+ ---
2
+ name: cometchat-ios-push
3
+ description: "Set up push notifications for CometChat iOS apps — APNs configuration, token registration, and notification handling."
4
+ license: "MIT"
5
+ compatibility: "CometChatUIKitSwift ^5; iOS 13+"
6
+ allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
7
+ metadata:
8
+ author: "CometChat"
9
+ version: "3.0.0"
10
+ tags: "chat cometchat ios push notifications apns"
11
+ ---
12
+
13
+ ## Purpose
14
+
15
+ This skill teaches how to set up push notifications for CometChat iOS apps, including APNs configuration, token registration, and handling incoming notifications.
16
+
17
+ ---
18
+
19
+ ## 1. Prerequisites
20
+
21
+ Before setting up push notifications:
22
+
23
+ 1. **Apple Developer Account** — Required for APNs certificates
24
+ 2. **CometChat Dashboard Access** — To configure push notification settings
25
+ 3. **Physical iOS Device** — Push notifications don't work on simulators
26
+
27
+ ---
28
+
29
+ ## 2. APNs Certificate Setup
30
+
31
+ ### Step 1: Create APNs Key (Recommended)
32
+
33
+ 1. Go to [Apple Developer Portal](https://developer.apple.com/account)
34
+ 2. Navigate to **Certificates, Identifiers & Profiles** → **Keys**
35
+ 3. Click **+** to create a new key
36
+ 4. Enter a name (e.g., "CometChat Push Key")
37
+ 5. Enable **Apple Push Notifications service (APNs)**
38
+ 6. Click **Continue** → **Register**
39
+ 7. Download the `.p8` file (you can only download it once!)
40
+ 8. Note the **Key ID** and your **Team ID**
41
+
42
+ ### Step 2: Configure CometChat Dashboard
43
+
44
+ 1. Go to [CometChat Dashboard](https://app.cometchat.com)
45
+ 2. Select your app
46
+ 3. Navigate to **Notifications** → **Push Notifications**
47
+ 4. Select **iOS** tab
48
+ 5. Upload your `.p8` file
49
+ 6. Enter your **Key ID** and **Team ID**
50
+ 7. Select the environment (Development/Production)
51
+ 8. Save the configuration
52
+
53
+ ---
54
+
55
+ ## 3. Xcode Project Configuration
56
+
57
+ ### Enable Push Notifications Capability
58
+
59
+ 1. Open your project in Xcode
60
+ 2. Select your target
61
+ 3. Go to **Signing & Capabilities**
62
+ 4. Click **+ Capability**
63
+ 5. Add **Push Notifications**
64
+ 6. Add **Background Modes** and enable:
65
+ - Remote notifications
66
+ - Voice over IP (if using calls)
67
+
68
+ ### Update Info.plist
69
+
70
+ Add the following to your `Info.plist`:
71
+
72
+ ```xml
73
+ <key>UIBackgroundModes</key>
74
+ <array>
75
+ <string>remote-notification</string>
76
+ <string>voip</string>
77
+ </array>
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 4. Code Implementation
83
+
84
+ ### AppDelegate Setup
85
+
86
+ ```swift
87
+ import UIKit
88
+ import UserNotifications
89
+ import CometChatUIKitSwift
90
+ import CometChatSDK
91
+
92
+ @main
93
+ class AppDelegate: UIResponder, UIApplicationDelegate {
94
+
95
+ func application(
96
+ _ application: UIApplication,
97
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
98
+ ) -> Bool {
99
+
100
+ // Initialize CometChat
101
+ initializeCometChat()
102
+
103
+ // Request notification permissions
104
+ requestNotificationPermission()
105
+
106
+ // Register for remote notifications
107
+ application.registerForRemoteNotifications()
108
+
109
+ return true
110
+ }
111
+
112
+ private func initializeCometChat() {
113
+ let uiKitSettings = UIKitSettings()
114
+ .set(appID: "YOUR_APP_ID")
115
+ .set(authKey: "YOUR_AUTH_KEY")
116
+ .set(region: "us")
117
+ .subscribePresenceForAllUsers()
118
+ .build()
119
+
120
+ CometChatUIKit(uiKitSettings: uiKitSettings) { result in
121
+ switch result {
122
+ case .success:
123
+ print("CometChat initialized")
124
+ case .failure(let error):
125
+ print("CometChat init failed: \(error)")
126
+ }
127
+ }
128
+ }
129
+
130
+ private func requestNotificationPermission() {
131
+ let center = UNUserNotificationCenter.current()
132
+ center.delegate = self
133
+
134
+ center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
135
+ if granted {
136
+ print("Notification permission granted")
137
+ } else if let error = error {
138
+ print("Notification permission error: \(error)")
139
+ }
140
+ }
141
+ }
142
+
143
+ // MARK: - Remote Notification Registration
144
+
145
+ func application(
146
+ _ application: UIApplication,
147
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
148
+ ) {
149
+ let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
150
+ print("APNs Device Token: \(token)")
151
+
152
+ // Register token with CometChat
153
+ registerPushToken(token)
154
+ }
155
+
156
+ func application(
157
+ _ application: UIApplication,
158
+ didFailToRegisterForRemoteNotificationsWithError error: Error
159
+ ) {
160
+ print("Failed to register for remote notifications: \(error)")
161
+ }
162
+
163
+ private func registerPushToken(_ token: String) {
164
+ CometChat.registerTokenForPushNotification(
165
+ token: token,
166
+ settings: ["voip": false]
167
+ ) { success in
168
+ print("Push token registered: \(success)")
169
+ } onError: { error in
170
+ print("Push token registration failed: \(error?.errorDescription ?? "")")
171
+ }
172
+ }
173
+
174
+ // MARK: - Handle Incoming Notifications
175
+
176
+ func application(
177
+ _ application: UIApplication,
178
+ didReceiveRemoteNotification userInfo: [AnyHashable: Any],
179
+ fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
180
+ ) {
181
+ print("Received remote notification: \(userInfo)")
182
+
183
+ // Handle CometChat notification
184
+ if let messageData = userInfo["message"] as? [String: Any] {
185
+ handleCometChatNotification(messageData)
186
+ }
187
+
188
+ completionHandler(.newData)
189
+ }
190
+
191
+ private func handleCometChatNotification(_ data: [String: Any]) {
192
+ // Parse notification data
193
+ guard let type = data["type"] as? String else { return }
194
+
195
+ switch type {
196
+ case "chat":
197
+ handleChatNotification(data)
198
+ case "call":
199
+ handleCallNotification(data)
200
+ default:
201
+ print("Unknown notification type: \(type)")
202
+ }
203
+ }
204
+
205
+ private func handleChatNotification(_ data: [String: Any]) {
206
+ // Extract sender info
207
+ guard let senderUID = data["sender"] as? String else { return }
208
+
209
+ // Navigate to conversation
210
+ DispatchQueue.main.async {
211
+ NotificationCenter.default.post(
212
+ name: .openConversation,
213
+ object: nil,
214
+ userInfo: ["uid": senderUID]
215
+ )
216
+ }
217
+ }
218
+
219
+ private func handleCallNotification(_ data: [String: Any]) {
220
+ // Handle incoming call notification
221
+ print("Incoming call notification")
222
+ }
223
+ }
224
+
225
+ // MARK: - UNUserNotificationCenterDelegate
226
+
227
+ extension AppDelegate: UNUserNotificationCenterDelegate {
228
+
229
+ // Handle notification when app is in foreground
230
+ func userNotificationCenter(
231
+ _ center: UNUserNotificationCenter,
232
+ willPresent notification: UNNotification,
233
+ withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
234
+ ) {
235
+ // Show notification even when app is in foreground
236
+ completionHandler([.banner, .sound, .badge])
237
+ }
238
+
239
+ // Handle notification tap
240
+ func userNotificationCenter(
241
+ _ center: UNUserNotificationCenter,
242
+ didReceive response: UNNotificationResponse,
243
+ withCompletionHandler completionHandler: @escaping () -> Void
244
+ ) {
245
+ let userInfo = response.notification.request.content.userInfo
246
+
247
+ if let messageData = userInfo["message"] as? [String: Any] {
248
+ handleCometChatNotification(messageData)
249
+ }
250
+
251
+ completionHandler()
252
+ }
253
+ }
254
+
255
+ // MARK: - Notification Names
256
+
257
+ extension Notification.Name {
258
+ static let openConversation = Notification.Name("openConversation")
259
+ }
260
+ ```
261
+
262
+ ### Handle Notification Navigation
263
+
264
+ ```swift
265
+ class MainViewController: UIViewController {
266
+
267
+ override func viewDidLoad() {
268
+ super.viewDidLoad()
269
+
270
+ // Listen for notification navigation
271
+ NotificationCenter.default.addObserver(
272
+ self,
273
+ selector: #selector(handleOpenConversation(_:)),
274
+ name: .openConversation,
275
+ object: nil
276
+ )
277
+ }
278
+
279
+ @objc private func handleOpenConversation(_ notification: Notification) {
280
+ guard let uid = notification.userInfo?["uid"] as? String else { return }
281
+
282
+ // Fetch user and open conversation
283
+ CometChat.getUser(UID: uid) { [weak self] user in
284
+ guard let user = user else { return }
285
+
286
+ DispatchQueue.main.async {
287
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer
288
+ messagesVC.set(user: user)
289
+ self?.navigationController?.pushViewController(messagesVC, animated: true)
290
+ }
291
+ } onError: { error in
292
+ print("Error fetching user: \(error?.errorDescription ?? "")")
293
+ }
294
+ }
295
+
296
+ deinit {
297
+ NotificationCenter.default.removeObserver(self)
298
+ }
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 5. VoIP Push Notifications (for Calls)
305
+
306
+ ### Import PushKit
307
+
308
+ ```swift
309
+ import PushKit
310
+ ```
311
+
312
+ ### Register for VoIP
313
+
314
+ ```swift
315
+ class AppDelegate: UIResponder, UIApplicationDelegate {
316
+
317
+ var voipRegistry: PKPushRegistry?
318
+
319
+ func application(
320
+ _ application: UIApplication,
321
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
322
+ ) -> Bool {
323
+
324
+ // ... other setup ...
325
+
326
+ // Register for VoIP push
327
+ registerForVoIPPush()
328
+
329
+ return true
330
+ }
331
+
332
+ private func registerForVoIPPush() {
333
+ voipRegistry = PKPushRegistry(queue: .main)
334
+ voipRegistry?.delegate = self
335
+ voipRegistry?.desiredPushTypes = [.voIP]
336
+ }
337
+ }
338
+
339
+ // MARK: - PKPushRegistryDelegate
340
+
341
+ extension AppDelegate: PKPushRegistryDelegate {
342
+
343
+ func pushRegistry(
344
+ _ registry: PKPushRegistry,
345
+ didUpdate pushCredentials: PKPushCredentials,
346
+ for type: PKPushType
347
+ ) {
348
+ let token = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
349
+ print("VoIP Token: \(token)")
350
+
351
+ // Register VoIP token with CometChat
352
+ CometChat.registerTokenForPushNotification(
353
+ token: token,
354
+ settings: ["voip": true]
355
+ ) { success in
356
+ print("VoIP token registered: \(success)")
357
+ } onError: { error in
358
+ print("VoIP token registration failed: \(error?.errorDescription ?? "")")
359
+ }
360
+ }
361
+
362
+ func pushRegistry(
363
+ _ registry: PKPushRegistry,
364
+ didReceiveIncomingPushWith payload: PKPushPayload,
365
+ for type: PKPushType,
366
+ completion: @escaping () -> Void
367
+ ) {
368
+ print("Received VoIP push: \(payload.dictionaryPayload)")
369
+
370
+ // Handle incoming call
371
+ if let callData = payload.dictionaryPayload["call"] as? [String: Any] {
372
+ handleIncomingCall(callData)
373
+ }
374
+
375
+ completion()
376
+ }
377
+
378
+ private func handleIncomingCall(_ data: [String: Any]) {
379
+ // Show incoming call UI
380
+ // This should use CallKit for proper iOS call handling
381
+ }
382
+ }
383
+ ```
384
+
385
+ ---
386
+
387
+ ## 6. UIKitSettings with Push Tokens
388
+
389
+ You can also pass tokens during initialization:
390
+
391
+ ```swift
392
+ let uiKitSettings = UIKitSettings()
393
+ .set(appID: "YOUR_APP_ID")
394
+ .set(authKey: "YOUR_AUTH_KEY")
395
+ .set(region: "us")
396
+ .set(deviceToken: apnsToken) // APNs token
397
+ .set(voipToken: voipToken) // VoIP token
398
+ .subscribePresenceForAllUsers()
399
+ .build()
400
+
401
+ CometChatUIKit(uiKitSettings: uiKitSettings) { result in
402
+ // ...
403
+ }
404
+ ```
405
+
406
+ ---
407
+
408
+ ## 7. Notification Service Extension
409
+
410
+ For rich notifications with images and custom content:
411
+
412
+ ### Create Extension
413
+
414
+ 1. In Xcode, go to **File** → **New** → **Target**
415
+ 2. Select **Notification Service Extension**
416
+ 3. Name it (e.g., "NotificationService")
417
+
418
+ ### Implement Extension
419
+
420
+ ```swift
421
+ // NotificationService.swift
422
+ import UserNotifications
423
+
424
+ class NotificationService: UNNotificationServiceExtension {
425
+
426
+ var contentHandler: ((UNNotificationContent) -> Void)?
427
+ var bestAttemptContent: UNMutableNotificationContent?
428
+
429
+ override func didReceive(
430
+ _ request: UNNotificationRequest,
431
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
432
+ ) {
433
+ self.contentHandler = contentHandler
434
+ bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
435
+
436
+ guard let bestAttemptContent = bestAttemptContent else {
437
+ contentHandler(request.content)
438
+ return
439
+ }
440
+
441
+ // Customize notification content
442
+ if let messageData = request.content.userInfo["message"] as? [String: Any] {
443
+ customizeNotification(bestAttemptContent, with: messageData)
444
+ }
445
+
446
+ contentHandler(bestAttemptContent)
447
+ }
448
+
449
+ private func customizeNotification(
450
+ _ content: UNMutableNotificationContent,
451
+ with data: [String: Any]
452
+ ) {
453
+ // Set title from sender name
454
+ if let senderName = data["senderName"] as? String {
455
+ content.title = senderName
456
+ }
457
+
458
+ // Set body from message
459
+ if let messageText = data["text"] as? String {
460
+ content.body = messageText
461
+ }
462
+
463
+ // Add image attachment if available
464
+ if let imageURL = data["imageURL"] as? String,
465
+ let url = URL(string: imageURL) {
466
+ downloadAndAttachImage(url, to: content)
467
+ }
468
+ }
469
+
470
+ private func downloadAndAttachImage(_ url: URL, to content: UNMutableNotificationContent) {
471
+ let task = URLSession.shared.downloadTask(with: url) { localURL, _, error in
472
+ guard let localURL = localURL, error == nil else { return }
473
+
474
+ let tempDir = FileManager.default.temporaryDirectory
475
+ let tempFile = tempDir.appendingPathComponent(UUID().uuidString + ".jpg")
476
+
477
+ try? FileManager.default.moveItem(at: localURL, to: tempFile)
478
+
479
+ if let attachment = try? UNNotificationAttachment(identifier: "image", url: tempFile) {
480
+ content.attachments = [attachment]
481
+ }
482
+
483
+ self.contentHandler?(content)
484
+ }
485
+ task.resume()
486
+ }
487
+
488
+ override func serviceExtensionTimeWillExpire() {
489
+ if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
490
+ contentHandler(bestAttemptContent)
491
+ }
492
+ }
493
+ }
494
+ ```
495
+
496
+ ---
497
+
498
+ ## 8. Badge Count Management
499
+
500
+ ### Update Badge Count
501
+
502
+ ```swift
503
+ // Set badge count
504
+ UIApplication.shared.applicationIconBadgeNumber = unreadCount
505
+
506
+ // Clear badge count
507
+ UIApplication.shared.applicationIconBadgeNumber = 0
508
+ ```
509
+
510
+ ### Sync with CometChat Unread Count
511
+
512
+ ```swift
513
+ func updateBadgeCount() {
514
+ CometChat.getUnreadMessageCount { userUnread, groupUnread in
515
+ var totalUnread = 0
516
+
517
+ for (_, count) in userUnread {
518
+ totalUnread += count as? Int ?? 0
519
+ }
520
+
521
+ for (_, count) in groupUnread {
522
+ totalUnread += count as? Int ?? 0
523
+ }
524
+
525
+ DispatchQueue.main.async {
526
+ UIApplication.shared.applicationIconBadgeNumber = totalUnread
527
+ }
528
+ } onError: { error in
529
+ print("Error getting unread count: \(error?.errorDescription ?? "")")
530
+ }
531
+ }
532
+ ```
533
+
534
+ ---
535
+
536
+ ## 9. Testing Push Notifications
537
+
538
+ ### Using Terminal
539
+
540
+ ```bash
541
+ # Test APNs push (requires authentication)
542
+ curl -v \
543
+ --header "apns-topic: com.yourapp.bundleid" \
544
+ --header "apns-push-type: alert" \
545
+ --header "authorization: bearer $JWT_TOKEN" \
546
+ --data '{"aps":{"alert":"Test message"}}' \
547
+ --http2 \
548
+ https://api.push.apple.com/3/device/$DEVICE_TOKEN
549
+ ```
550
+
551
+ ### Using CometChat Dashboard
552
+
553
+ 1. Go to CometChat Dashboard
554
+ 2. Navigate to **Users**
555
+ 3. Select a user
556
+ 4. Click **Send Push Notification**
557
+ 5. Enter a test message
558
+ 6. Send
559
+
560
+ ---
561
+
562
+ ## Troubleshooting
563
+
564
+ | Issue | Solution |
565
+ |---|---|
566
+ | Token not registering | Ensure device is physical, not simulator |
567
+ | Notifications not received | Check APNs certificate in dashboard |
568
+ | Badge not updating | Check notification permissions |
569
+ | VoIP not working | Ensure VoIP capability is enabled |
570
+ | Notifications delayed | Check APNs environment (dev vs prod) |
571
+
572
+ ---
573
+
574
+ ## Best Practices
575
+
576
+ 1. **Always request permission** before registering for notifications
577
+ 2. **Handle token refresh** — tokens can change
578
+ 3. **Test on real devices** — simulators don't support push
579
+ 4. **Use Notification Service Extension** for rich notifications
580
+ 5. **Implement proper deep linking** for notification taps
581
+ 6. **Clear badge count** when user opens the app
582
+ 7. **Handle both foreground and background** notifications