@cometchat/skills 3.1.0 → 4.1.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/README.md +75 -17
  2. package/bin/install.js +523 -43
  3. package/package.json +17 -8
  4. package/skills/cometchat/SKILL.md +850 -109
  5. package/skills/cometchat-android-v5/SKILL.md +120 -0
  6. package/skills/cometchat-android-v5-components/SKILL.md +430 -0
  7. package/skills/cometchat-android-v5-core/SKILL.md +468 -0
  8. package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
  9. package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
  10. package/skills/cometchat-android-v5-features/SKILL.md +130 -0
  11. package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
  12. package/skills/cometchat-android-v5-production/SKILL.md +94 -0
  13. package/skills/cometchat-android-v5-push/SKILL.md +620 -0
  14. package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
  15. package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
  16. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
  17. package/skills/cometchat-android-v6/SKILL.md +107 -0
  18. package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
  19. package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
  20. package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
  21. package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
  22. package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
  23. package/skills/cometchat-android-v6-core/SKILL.md +354 -0
  24. package/skills/cometchat-android-v6-events/SKILL.md +212 -0
  25. package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
  26. package/skills/cometchat-android-v6-features/SKILL.md +130 -0
  27. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
  28. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
  29. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
  30. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
  31. package/skills/cometchat-android-v6-production/SKILL.md +187 -0
  32. package/skills/cometchat-android-v6-push/SKILL.md +274 -0
  33. package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
  34. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
  35. package/skills/cometchat-angular-components/SKILL.md +706 -0
  36. package/skills/cometchat-angular-core/SKILL.md +444 -0
  37. package/skills/cometchat-angular-customization/SKILL.md +424 -0
  38. package/skills/cometchat-angular-features/SKILL.md +447 -0
  39. package/skills/cometchat-angular-patterns/SKILL.md +447 -0
  40. package/skills/cometchat-angular-placement/SKILL.md +685 -0
  41. package/skills/cometchat-angular-production/SKILL.md +415 -0
  42. package/skills/cometchat-angular-theming/SKILL.md +365 -0
  43. package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
  44. package/skills/cometchat-astro-patterns/SKILL.md +3 -3
  45. package/skills/cometchat-components/SKILL.md +2 -0
  46. package/skills/cometchat-core/SKILL.md +11 -6
  47. package/skills/cometchat-customization/references/component-catalog.md +1 -1
  48. package/skills/cometchat-features/SKILL.md +176 -108
  49. package/skills/cometchat-flutter-v5/SKILL.md +167 -0
  50. package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
  51. package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
  52. package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
  53. package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
  54. package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
  55. package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
  56. package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
  57. package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
  58. package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
  59. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
  60. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
  61. package/skills/cometchat-flutter-v6/SKILL.md +445 -0
  62. package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
  63. package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
  64. package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
  65. package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
  66. package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
  67. package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
  68. package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
  69. package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
  70. package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
  71. package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
  72. package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
  73. package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
  74. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
  75. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
  76. package/skills/cometchat-ios/SKILL.md +323 -0
  77. package/skills/cometchat-ios-components/SKILL.md +651 -0
  78. package/skills/cometchat-ios-core/SKILL.md +822 -0
  79. package/skills/cometchat-ios-customization/SKILL.md +825 -0
  80. package/skills/cometchat-ios-features/SKILL.md +725 -0
  81. package/skills/cometchat-ios-placement/SKILL.md +750 -0
  82. package/skills/cometchat-ios-production/SKILL.md +730 -0
  83. package/skills/cometchat-ios-push/SKILL.md +582 -0
  84. package/skills/cometchat-ios-theming/SKILL.md +631 -0
  85. package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
  86. package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
  87. package/skills/cometchat-native-components/SKILL.md +594 -0
  88. package/skills/cometchat-native-core/SKILL.md +407 -0
  89. package/skills/cometchat-native-customization/SKILL.md +594 -0
  90. package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
  91. package/skills/cometchat-native-features/SKILL.md +608 -0
  92. package/skills/cometchat-native-placement/SKILL.md +500 -0
  93. package/skills/cometchat-native-production/SKILL.md +694 -0
  94. package/skills/cometchat-native-push/SKILL.md +593 -0
  95. package/skills/cometchat-native-testing/SKILL.md +583 -0
  96. package/skills/cometchat-native-theming/SKILL.md +656 -0
  97. package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
  98. package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
  99. package/skills/cometchat-placement/SKILL.md +28 -0
  100. package/skills/cometchat-react-patterns/SKILL.md +55 -7
  101. package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
@@ -0,0 +1,750 @@
1
+ ---
2
+ name: cometchat-ios-placement
3
+ description: "WHERE to put CometChat in your iOS app — navigation patterns, tab bars, modals, and embedded views."
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 placement navigation tabs modal patterns"
11
+ ---
12
+
13
+ ## Purpose
14
+
15
+ This skill teaches WHERE to place CometChat components in your iOS app. It covers navigation patterns, tab bar integration, modal presentations, and embedded views for different use cases.
16
+
17
+ ---
18
+
19
+ ## 1. Navigation Stack Pattern
20
+
21
+ The most common pattern for messaging apps. Push conversations onto a navigation stack.
22
+
23
+ ### Basic Navigation Flow
24
+
25
+ ```
26
+ ┌─────────────────────────────────────────────────────────────┐
27
+ │ UINavigationController │
28
+ │ ┌─────────────────────────────────────────────────────────┐│
29
+ │ │ CometChatConversations ││
30
+ │ │ ┌─────────────────────────────────────────────────────┐││
31
+ │ │ │ Conversation 1 │││
32
+ │ │ │ Conversation 2 ──────────────────────────────────►│││
33
+ │ │ │ Conversation 3 │││
34
+ │ │ └─────────────────────────────────────────────────────┘││
35
+ │ └─────────────────────────────────────────────────────────┘│
36
+ └─────────────────────────────────────────────────────────────┘
37
+ │
38
+ ▼
39
+ ┌─────────────────────────────────────────────────────────────┐
40
+ │ MessagesViewController (pushed) │
41
+ │ ┌─────────────────────────────────────────────────────────┐│
42
+ │ │ CometChatMessageHeader ││
43
+ │ ├─────────────────────────────────────────────────────────┤│
44
+ │ │ CometChatMessageList ││
45
+ │ │ ││
46
+ │ │ ││
47
+ │ ├─────────────────────────────────────────────────────────┤│
48
+ │ │ CometChatMessageComposer ││
49
+ │ └─────────────────────────────────────────────────────────┘│
50
+ └─────────────────────────────────────────────────────────────┘
51
+ ```
52
+
53
+ ### Implementation (UIKit)
54
+
55
+ ```swift
56
+ // SceneDelegate.swift
57
+ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
58
+ guard let windowScene = (scene as? UIWindowScene) else { return }
59
+
60
+ let window = UIWindow(windowScene: windowScene)
61
+
62
+ // Create conversations list
63
+ let conversations = CometChatConversations()
64
+ conversations.set(onItemClick: { [weak conversations] conversation, _ in
65
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
66
+ if let user = conversation.conversationWith as? User {
67
+ messagesVC.set(user: user)
68
+ } else if let group = conversation.conversationWith as? Group {
69
+ messagesVC.set(group: group)
70
+ }
71
+ conversations?.navigationController?.pushViewController(messagesVC, animated: true)
72
+ })
73
+
74
+ // Wrap in navigation controller
75
+ let navController = UINavigationController(rootViewController: conversations)
76
+
77
+ window.rootViewController = navController
78
+ self.window = window
79
+ window.makeKeyAndVisible()
80
+ }
81
+ ```
82
+
83
+ ### Implementation (SwiftUI)
84
+
85
+ ```swift
86
+ import SwiftUI
87
+ import CometChatUIKitSwift
88
+ import CometChatSDK
89
+
90
+ struct ChatNavigationView: View {
91
+ @State private var selectedConversation: Conversation?
92
+ @State private var showMessages = false
93
+
94
+ var body: some View {
95
+ NavigationStack {
96
+ ConversationsListView(selectedConversation: $selectedConversation)
97
+ .navigationDestination(isPresented: $showMessages) {
98
+ if let conversation = selectedConversation {
99
+ MessagesView(conversation: conversation)
100
+ }
101
+ }
102
+ .onChange(of: selectedConversation) { newValue in
103
+ showMessages = newValue != nil
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ struct ConversationsListView: UIViewControllerRepresentable {
110
+ @Binding var selectedConversation: Conversation?
111
+
112
+ func makeUIViewController(context: Context) -> CometChatConversations {
113
+ let conversations = CometChatConversations()
114
+ conversations.onItemClick = { conversation, _ in
115
+ selectedConversation = conversation
116
+ }
117
+ return conversations
118
+ }
119
+
120
+ func updateUIViewController(_ uiViewController: CometChatConversations, context: Context) {}
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 2. Tab Bar Pattern
127
+
128
+ For apps where chat is one of several main features.
129
+
130
+ ### Tab Layout
131
+
132
+ ```
133
+ ┌─────────────────────────────────────────────────────────────┐
134
+ │ │
135
+ │ Content Area │
136
+ │ │
137
+ │ │
138
+ │ │
139
+ ├─────────────────────────────────────────────────────────────┤
140
+ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
141
+ │ │Home │ │Chats│ │Users│ │Groups│ │Calls│ │
142
+ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
143
+ └─────────────────────────────────────────────────────────────┘
144
+ ```
145
+
146
+ ### Implementation (UIKit)
147
+
148
+ ```swift
149
+ import UIKit
150
+ import CometChatUIKitSwift
151
+ import CometChatSDK
152
+
153
+ class MainTabBarController: UITabBarController {
154
+
155
+ override func viewDidLoad() {
156
+ super.viewDidLoad()
157
+ setupTabs()
158
+ }
159
+
160
+ private func setupTabs() {
161
+ // Home tab (your existing content)
162
+ let homeVC = HomeViewController()
163
+ homeVC.tabBarItem = UITabBarItem(
164
+ title: "Home",
165
+ image: UIImage(systemName: "house"),
166
+ selectedImage: UIImage(systemName: "house.fill")
167
+ )
168
+
169
+ // Chats tab
170
+ let chatsVC = createChatsTab()
171
+ let chatsNav = UINavigationController(rootViewController: chatsVC)
172
+ chatsNav.tabBarItem = UITabBarItem(
173
+ title: "Chats",
174
+ image: UIImage(systemName: "message"),
175
+ selectedImage: UIImage(systemName: "message.fill")
176
+ )
177
+
178
+ // Users tab
179
+ let usersVC = CometChatUsers()
180
+ usersVC.set(onItemClick: { [weak self] user, _ in
181
+ self?.openMessages(with: user)
182
+ })
183
+ let usersNav = UINavigationController(rootViewController: usersVC)
184
+ usersNav.tabBarItem = UITabBarItem(
185
+ title: "Users",
186
+ image: UIImage(systemName: "person.2"),
187
+ selectedImage: UIImage(systemName: "person.2.fill")
188
+ )
189
+
190
+ // Groups tab
191
+ let groupsVC = CometChatGroups()
192
+ groupsVC.set(onItemClick: { [weak self] group, _ in
193
+ self?.openMessages(with: group)
194
+ })
195
+ let groupsNav = UINavigationController(rootViewController: groupsVC)
196
+ groupsNav.tabBarItem = UITabBarItem(
197
+ title: "Groups",
198
+ image: UIImage(systemName: "person.3"),
199
+ selectedImage: UIImage(systemName: "person.3.fill")
200
+ )
201
+
202
+ var tabs: [UIViewController] = [
203
+ UINavigationController(rootViewController: homeVC),
204
+ chatsNav,
205
+ usersNav,
206
+ groupsNav
207
+ ]
208
+
209
+ // Calls tab - only available if CometChatCallsSDK is installed
210
+ #if canImport(CometChatCallsSDK)
211
+ let callsVC = CometChatCallLogs()
212
+ let callsNav = UINavigationController(rootViewController: callsVC)
213
+ callsNav.tabBarItem = UITabBarItem(
214
+ title: "Calls",
215
+ image: UIImage(systemName: "phone"),
216
+ selectedImage: UIImage(systemName: "phone.fill")
217
+ )
218
+ tabs.append(callsNav)
219
+ #endif
220
+
221
+ viewControllers = tabs
222
+ }
223
+
224
+ private func createChatsTab() -> CometChatConversations {
225
+ let conversations = CometChatConversations()
226
+ conversations.set(onItemClick: { [weak self] conversation, _ in
227
+ if let user = conversation.conversationWith as? User {
228
+ self?.openMessages(with: user)
229
+ } else if let group = conversation.conversationWith as? Group {
230
+ self?.openMessages(with: group)
231
+ }
232
+ })
233
+ return conversations
234
+ }
235
+
236
+ private func openMessages(with user: User) {
237
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
238
+ messagesVC.set(user: user)
239
+ messagesVC.hidesBottomBarWhenPushed = true
240
+
241
+ if let navController = selectedViewController as? UINavigationController {
242
+ navController.pushViewController(messagesVC, animated: true)
243
+ }
244
+ }
245
+
246
+ private func openMessages(with group: Group) {
247
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
248
+ messagesVC.set(group: group)
249
+ messagesVC.hidesBottomBarWhenPushed = true
250
+
251
+ if let navController = selectedViewController as? UINavigationController {
252
+ navController.pushViewController(messagesVC, animated: true)
253
+ }
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Implementation (SwiftUI)
259
+
260
+ ```swift
261
+ struct MainTabView: View {
262
+ @State private var selectedTab = 0
263
+
264
+ var body: some View {
265
+ TabView(selection: $selectedTab) {
266
+ HomeView()
267
+ .tabItem {
268
+ Label("Home", systemImage: "house")
269
+ }
270
+ .tag(0)
271
+
272
+ ChatNavigationView()
273
+ .tabItem {
274
+ Label("Chats", systemImage: "message")
275
+ }
276
+ .tag(1)
277
+
278
+ UsersNavigationView()
279
+ .tabItem {
280
+ Label("Users", systemImage: "person.2")
281
+ }
282
+ .tag(2)
283
+
284
+ GroupsNavigationView()
285
+ .tabItem {
286
+ Label("Groups", systemImage: "person.3")
287
+ }
288
+ .tag(3)
289
+
290
+ CallsNavigationView()
291
+ .tabItem {
292
+ Label("Calls", systemImage: "phone")
293
+ }
294
+ .tag(4)
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ ---
301
+
302
+ ## 3. Modal Presentation Pattern
303
+
304
+ For apps where chat is a secondary feature, presented modally.
305
+
306
+ ### Modal Layout
307
+
308
+ ```
309
+ ┌─────────────────────────────────────────────────────────────┐
310
+ │ Your App Content │
311
+ │ │
312
+ │ ┌─────────────────────────────────────────────────────────┐│
313
+ │ │ Product Details ││
314
+ │ │ ││
315
+ │ │ ┌─────────────────────────────────────────────────────┐││
316
+ │ │ │ [Chat with Seller] ◄─────────────────────────────│││
317
+ │ │ └─────────────────────────────────────────────────────┘││
318
+ │ └─────────────────────────────────────────────────────────┘│
319
+ └─────────────────────────────────────────────────────────────┘
320
+ │
321
+ ▼ (modal presentation)
322
+ ┌─────────────────────────────────────────────────────────────┐
323
+ │ ┌─────────────────────────────────────────────────────────┐│
324
+ │ │ [X] Chat with John ││
325
+ │ ├─────────────────────────────────────────────────────────┤│
326
+ │ │ ││
327
+ │ │ Messages ││
328
+ │ │ ││
329
+ │ ├─────────────────────────────────────────────────────────┤│
330
+ │ │ Composer ││
331
+ │ └─────────────────────────────────────────────────────────┘│
332
+ └─────────────────────────────────────────────────────────────┘
333
+ ```
334
+
335
+ ### Implementation (UIKit)
336
+
337
+ ```swift
338
+ class ProductDetailViewController: UIViewController {
339
+
340
+ var sellerUID: String?
341
+
342
+ @IBAction func chatWithSellerTapped(_ sender: UIButton) {
343
+ guard let sellerUID = sellerUID else { return }
344
+
345
+ // Fetch the seller user
346
+ CometChat.getUser(UID: sellerUID) { [weak self] user in
347
+ guard let user = user else { return }
348
+
349
+ DispatchQueue.main.async {
350
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
351
+ messagesVC.set(user: user)
352
+
353
+ let navController = UINavigationController(rootViewController: messagesVC)
354
+ navController.modalPresentationStyle = .pageSheet
355
+
356
+ // Add close button
357
+ messagesVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
358
+ barButtonSystemItem: .close,
359
+ target: self,
360
+ action: #selector(self?.dismissChat)
361
+ )
362
+
363
+ self?.present(navController, animated: true)
364
+ }
365
+ } onError: { error in
366
+ print("Error fetching user: \(error?.errorDescription ?? "")")
367
+ }
368
+ }
369
+
370
+ @objc private func dismissChat() {
371
+ dismiss(animated: true)
372
+ }
373
+ }
374
+ ```
375
+
376
+ ### Implementation (SwiftUI)
377
+
378
+ ```swift
379
+ struct ProductDetailView: View {
380
+ let sellerUID: String
381
+ @State private var showChat = false
382
+ @State private var seller: User?
383
+
384
+ var body: some View {
385
+ VStack {
386
+ // Product details...
387
+
388
+ Button("Chat with Seller") {
389
+ fetchSellerAndShowChat()
390
+ }
391
+ .buttonStyle(.borderedProminent)
392
+ }
393
+ .sheet(isPresented: $showChat) {
394
+ if let seller = seller {
395
+ NavigationStack {
396
+ MessagesView(user: seller)
397
+ .toolbar {
398
+ ToolbarItem(placement: .navigationBarLeading) {
399
+ Button("Close") {
400
+ showChat = false
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ }
408
+
409
+ private func fetchSellerAndShowChat() {
410
+ CometChat.getUser(UID: sellerUID) { user in
411
+ DispatchQueue.main.async {
412
+ self.seller = user
413
+ self.showChat = true
414
+ }
415
+ } onError: { error in
416
+ print("Error: \(error?.errorDescription ?? "")")
417
+ }
418
+ }
419
+ }
420
+ ```
421
+
422
+ ---
423
+
424
+ ## 4. Floating Button Pattern
425
+
426
+ For support chat or quick access to messages.
427
+
428
+ ### Floating Button Layout
429
+
430
+ ```
431
+ ┌─────────────────────────────────────────────────────────────┐
432
+ │ Your App Content │
433
+ │ │
434
+ │ │
435
+ │ │
436
+ │ │
437
+ │ │
438
+ │ │
439
+ │ ┌─────┐ │
440
+ │ │ 💬 │ │
441
+ │ └─────┘ │
442
+ └─────────────────────────────────────────────────────────────┘
443
+ ```
444
+
445
+ ### Implementation (UIKit)
446
+
447
+ ```swift
448
+ class FloatingChatButton: UIButton {
449
+
450
+ private var unreadCount: Int = 0 {
451
+ didSet {
452
+ updateBadge()
453
+ }
454
+ }
455
+
456
+ private lazy var badgeLabel: UILabel = {
457
+ let label = UILabel()
458
+ label.backgroundColor = .systemRed
459
+ label.textColor = .white
460
+ label.font = .systemFont(ofSize: 12, weight: .bold)
461
+ label.textAlignment = .center
462
+ label.layer.cornerRadius = 10
463
+ label.clipsToBounds = true
464
+ label.isHidden = true
465
+ return label
466
+ }()
467
+
468
+ override init(frame: CGRect) {
469
+ super.init(frame: frame)
470
+ setupButton()
471
+ }
472
+
473
+ required init?(coder: NSCoder) {
474
+ super.init(coder: coder)
475
+ setupButton()
476
+ }
477
+
478
+ private func setupButton() {
479
+ backgroundColor = .systemBlue
480
+ layer.cornerRadius = 28
481
+ layer.shadowColor = UIColor.black.cgColor
482
+ layer.shadowOffset = CGSize(width: 0, height: 4)
483
+ layer.shadowRadius = 8
484
+ layer.shadowOpacity = 0.3
485
+
486
+ setImage(UIImage(systemName: "message.fill"), for: .normal)
487
+ tintColor = .white
488
+
489
+ addSubview(badgeLabel)
490
+ badgeLabel.translatesAutoresizingMaskIntoConstraints = false
491
+ NSLayoutConstraint.activate([
492
+ badgeLabel.topAnchor.constraint(equalTo: topAnchor, constant: -5),
493
+ badgeLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 5),
494
+ badgeLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
495
+ badgeLabel.heightAnchor.constraint(equalToConstant: 20)
496
+ ])
497
+ }
498
+
499
+ private func updateBadge() {
500
+ badgeLabel.isHidden = unreadCount == 0
501
+ badgeLabel.text = unreadCount > 99 ? "99+" : "\(unreadCount)"
502
+ }
503
+
504
+ func setUnreadCount(_ count: Int) {
505
+ unreadCount = count
506
+ }
507
+ }
508
+
509
+ // Usage in a view controller
510
+ class MainViewController: UIViewController {
511
+
512
+ private lazy var chatButton: FloatingChatButton = {
513
+ let button = FloatingChatButton()
514
+ button.addTarget(self, action: #selector(openChat), for: .touchUpInside)
515
+ return button
516
+ }()
517
+
518
+ override func viewDidLoad() {
519
+ super.viewDidLoad()
520
+ setupFloatingButton()
521
+ }
522
+
523
+ private func setupFloatingButton() {
524
+ view.addSubview(chatButton)
525
+ chatButton.translatesAutoresizingMaskIntoConstraints = false
526
+ NSLayoutConstraint.activate([
527
+ chatButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
528
+ chatButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
529
+ chatButton.widthAnchor.constraint(equalToConstant: 56),
530
+ chatButton.heightAnchor.constraint(equalToConstant: 56)
531
+ ])
532
+ }
533
+
534
+ @objc private func openChat() {
535
+ let conversations = CometChatConversations()
536
+ conversations.set(onItemClick: { [weak self] conversation, _ in
537
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
538
+ if let user = conversation.conversationWith as? User {
539
+ messagesVC.set(user: user)
540
+ } else if let group = conversation.conversationWith as? Group {
541
+ messagesVC.set(group: group)
542
+ }
543
+ conversations.navigationController?.pushViewController(messagesVC, animated: true)
544
+ })
545
+
546
+ let navController = UINavigationController(rootViewController: conversations)
547
+ navController.modalPresentationStyle = .pageSheet
548
+
549
+ conversations.navigationItem.leftBarButtonItem = UIBarButtonItem(
550
+ barButtonSystemItem: .close,
551
+ target: self,
552
+ action: #selector(dismissChat)
553
+ )
554
+
555
+ present(navController, animated: true)
556
+ }
557
+
558
+ @objc private func dismissChat() {
559
+ dismiss(animated: true)
560
+ }
561
+ }
562
+ ```
563
+
564
+ ---
565
+
566
+ ## 5. Split View Pattern (iPad)
567
+
568
+ For iPad apps with master-detail layout.
569
+
570
+ ### Split View Layout
571
+
572
+ ```
573
+ ┌─────────────────────────────────────────────────────────────────────────────┐
574
+ │ ┌─────────────────────────┐ ┌─────────────────────────────────────────────┐│
575
+ │ │ Conversations │ │ Messages ││
576
+ │ │ ┌─────────────────────┐│ │ ┌─────────────────────────────────────────┐││
577
+ │ │ │ John Doe ││ │ │ Header │││
578
+ │ │ │ Jane Smith ◄──────┼┼──┼──┤─────────────────────────────────────────│││
579
+ │ │ │ Team Chat ││ │ │ Message List │││
580
+ │ │ │ ││ │ │ │││
581
+ │ │ │ ││ │ │ │││
582
+ │ │ │ ││ │ ├─────────────────────────────────────────┤││
583
+ │ │ │ ││ │ │ Composer │││
584
+ │ │ └─────────────────────┘│ │ └─────────────────────────────────────────┘││
585
+ │ └─────────────────────────┘ └─────────────────────────────────────────────┘│
586
+ └─────────────────────────────────────────────────────────────────────────────┘
587
+ ```
588
+
589
+ ### Implementation (UIKit)
590
+
591
+ ```swift
592
+ class ChatSplitViewController: UISplitViewController {
593
+
594
+ override func viewDidLoad() {
595
+ super.viewDidLoad()
596
+
597
+ preferredDisplayMode = .oneBesideSecondary
598
+ preferredSplitBehavior = .tile
599
+
600
+ // Primary: Conversations
601
+ let conversations = CometChatConversations()
602
+ conversations.set(onItemClick: { [weak self] conversation, _ in
603
+ self?.showMessages(for: conversation)
604
+ })
605
+ let primaryNav = UINavigationController(rootViewController: conversations)
606
+
607
+ // Secondary: Empty state or messages
608
+ let emptyVC = EmptyStateViewController()
609
+ let secondaryNav = UINavigationController(rootViewController: emptyVC)
610
+
611
+ viewControllers = [primaryNav, secondaryNav]
612
+ }
613
+
614
+ private func showMessages(for conversation: Conversation) {
615
+ let messagesVC = MessagesVC() // your own VC composing CometChatMessageHeader + List + Composer; see cometchat-ios-components § 12
616
+
617
+ if let user = conversation.conversationWith as? User {
618
+ messagesVC.set(user: user)
619
+ } else if let group = conversation.conversationWith as? Group {
620
+ messagesVC.set(group: group)
621
+ }
622
+
623
+ let secondaryNav = UINavigationController(rootViewController: messagesVC)
624
+ showDetailViewController(secondaryNav, sender: self)
625
+ }
626
+ }
627
+
628
+ class EmptyStateViewController: UIViewController {
629
+ override func viewDidLoad() {
630
+ super.viewDidLoad()
631
+ view.backgroundColor = .systemBackground
632
+
633
+ let label = UILabel()
634
+ label.text = "Select a conversation"
635
+ label.textColor = .secondaryLabel
636
+ label.textAlignment = .center
637
+
638
+ view.addSubview(label)
639
+ label.translatesAutoresizingMaskIntoConstraints = false
640
+ NSLayoutConstraint.activate([
641
+ label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
642
+ label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
643
+ ])
644
+ }
645
+ }
646
+ ```
647
+
648
+ ---
649
+
650
+ ## 6. Embedded View Pattern
651
+
652
+ For embedding chat in a portion of the screen.
653
+
654
+ ### Embedded Layout
655
+
656
+ ```
657
+ ┌─────────────────────────────────────────────────────────────┐
658
+ │ Your App Header │
659
+ ├─────────────────────────────────────────────────────────────┤
660
+ │ ┌─────────────────────────────────────────────────────────┐│
661
+ │ │ Your Content ││
662
+ │ │ ││
663
+ │ └─────────────────────────────────────────────────────────┘│
664
+ ├─────────────────────────────────────────────────────────────┤
665
+ │ ┌─────────────────────────────────────────────────────────┐│
666
+ │ │ Embedded Chat (CometChatMessageList + Composer) ││
667
+ │ │ ││
668
+ │ └─────────────────────────────────────────────────────────┘│
669
+ └─────────────────────────────────────────────────────────────┘
670
+ ```
671
+
672
+ ### Implementation (UIKit)
673
+
674
+ ```swift
675
+ class EmbeddedChatViewController: UIViewController {
676
+
677
+ private var supportUser: User?
678
+
679
+ private lazy var chatContainer: UIView = {
680
+ let view = UIView()
681
+ view.backgroundColor = .systemBackground
682
+ view.layer.cornerRadius = 12
683
+ view.clipsToBounds = true
684
+ return view
685
+ }()
686
+
687
+ private lazy var messageList = CometChatMessageList()
688
+ private lazy var messageComposer = CometChatMessageComposer()
689
+
690
+ override func viewDidLoad() {
691
+ super.viewDidLoad()
692
+ setupUI()
693
+ fetchSupportUser()
694
+ }
695
+
696
+ private func setupUI() {
697
+ view.addSubview(chatContainer)
698
+ chatContainer.translatesAutoresizingMaskIntoConstraints = false
699
+
700
+ chatContainer.addSubview(messageList)
701
+ chatContainer.addSubview(messageComposer)
702
+
703
+ messageList.translatesAutoresizingMaskIntoConstraints = false
704
+ messageComposer.translatesAutoresizingMaskIntoConstraints = false
705
+
706
+ NSLayoutConstraint.activate([
707
+ // Chat container takes bottom half of screen
708
+ chatContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
709
+ chatContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
710
+ chatContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16),
711
+ chatContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4),
712
+
713
+ // Message list
714
+ messageList.topAnchor.constraint(equalTo: chatContainer.topAnchor),
715
+ messageList.leadingAnchor.constraint(equalTo: chatContainer.leadingAnchor),
716
+ messageList.trailingAnchor.constraint(equalTo: chatContainer.trailingAnchor),
717
+ messageList.bottomAnchor.constraint(equalTo: messageComposer.topAnchor),
718
+
719
+ // Composer
720
+ messageComposer.leadingAnchor.constraint(equalTo: chatContainer.leadingAnchor),
721
+ messageComposer.trailingAnchor.constraint(equalTo: chatContainer.trailingAnchor),
722
+ messageComposer.bottomAnchor.constraint(equalTo: chatContainer.bottomAnchor)
723
+ ])
724
+ }
725
+
726
+ private func fetchSupportUser() {
727
+ CometChat.getUser(UID: "support-agent") { [weak self] user in
728
+ guard let user = user else { return }
729
+ DispatchQueue.main.async {
730
+ self?.supportUser = user
731
+ self?.messageList.set(user: user)
732
+ self?.messageComposer.set(user: user)
733
+ }
734
+ } onError: { error in
735
+ print("Error: \(error?.errorDescription ?? "")")
736
+ }
737
+ }
738
+ }
739
+ ```
740
+
741
+ ---
742
+
743
+ ## Best Practices
744
+
745
+ 1. **Always wrap in UINavigationController** when presenting CometChat view controllers
746
+ 2. **Hide tab bar when pushing messages** using `hidesBottomBarWhenPushed = true`
747
+ 3. **Handle keyboard properly** — CometChat components handle this automatically
748
+ 4. **Support both orientations** — components adapt to orientation changes
749
+ 5. **Test on iPad** — use split view for better iPad experience
750
+ 6. **Handle deep links** — navigate to specific conversations from push notifications