Skip to main content
The CometChatConversations component displays a list of all conversations (one-on-one and group chats) for the currently logged-in user. It shows the last message, unread count, typing indicators, and user presence in real-time.

Prerequisites

Before using this component, ensure you have:
  1. Initialized the CometChat UI Kit
  2. Logged in a user

Basic Usage

import CometChatUIKitSwift

class ConversationsViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let conversations = CometChatConversations()
        
        // Push to navigation stack
        navigationController?.pushViewController(conversations, animated: true)
    }
}

Production-Ready Implementation

This example shows a complete conversations screen with navigation to messages, custom filtering, and error handling:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class ChatListViewController: UIViewController {
    
    private var conversationsController: CometChatConversations!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupConversations()
    }
    
    private func setupConversations() {
        // Create custom request builder for filtering
        let requestBuilder = ConversationRequest.ConversationRequestBuilder(limit: 30)
            .set(conversationType: .both)  // Show both user and group conversations
        
        // Initialize conversations component
        conversationsController = CometChatConversations(conversationRequestBuilder: requestBuilder)
        
        // Handle conversation selection - navigate to messages
        conversationsController.set(onItemClick: { [weak self] conversation, indexPath in
            self?.openMessages(for: conversation)
        })
        
        // Handle long press for additional options
        conversationsController.set(onItemLongClick: { [weak self] conversation, indexPath in
            self?.showConversationOptions(for: conversation)
        })
        
        // Handle errors
        conversationsController.set(onError: { error in
            print("Conversations error: \(error.errorDescription)")
        })
        
        // Handle empty state
        conversationsController.set(onEmpty: {
            print("No conversations found")
        })
        
        // Handle when conversations are loaded
        conversationsController.set(onLoad: { conversations in
            print("Loaded \(conversations.count) conversations")
        })
        
        // Present the conversations
        navigationController?.pushViewController(conversationsController, animated: true)
    }
    
    private func openMessages(for conversation: Conversation) {
        if let user = conversation.conversationWith as? User {
            let messagesVC = CometChatMessages()
            messagesVC.set(user: user)
            navigationController?.pushViewController(messagesVC, animated: true)
        } else if let group = conversation.conversationWith as? Group {
            let messagesVC = CometChatMessages()
            messagesVC.set(group: group)
            navigationController?.pushViewController(messagesVC, animated: true)
        }
    }
    
    private func showConversationOptions(for conversation: Conversation) {
        let alert = UIAlertController(title: "Options", message: nil, preferredStyle: .actionSheet)
        
        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ in
            CometChat.deleteConversation(
                conversationWith: conversation.conversationWith?.uid ?? "",
                conversationType: conversation.conversationType
            ) { success in
                print("Conversation deleted")
            } onError: { error in
                print("Delete failed: \(error?.errorDescription ?? "")")
            }
        })
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(alert, animated: true)
    }
}

Filter Conversations

Use ConversationRequestBuilder to filter which conversations appear:
// Show only one-on-one conversations
let userOnlyBuilder = ConversationRequest.ConversationRequestBuilder(limit: 20)
    .set(conversationType: .user)

let conversations = CometChatConversations(conversationRequestBuilder: userOnlyBuilder)
// Show only group conversations
let groupOnlyBuilder = ConversationRequest.ConversationRequestBuilder(limit: 20)
    .set(conversationType: .group)

let conversations = CometChatConversations(conversationRequestBuilder: groupOnlyBuilder)
// Filter by tags
let taggedBuilder = ConversationRequest.ConversationRequestBuilder(limit: 20)
    .set(conversationType: .both)
    .withTags(true)
    .set(tags: ["support", "sales"])

let conversations = CometChatConversations(conversationRequestBuilder: taggedBuilder)

Actions Reference

MethodDescriptionExample
set(onItemClick:)Triggered when a conversation is tappedNavigate to messages
set(onItemLongClick:)Triggered on long pressShow options menu
set(onBack:)Triggered when back button is pressedCustom navigation
set(onSelection:)Triggered in selection modeMulti-select conversations
set(onError:)Triggered when an error occursShow error alert
set(onEmpty:)Triggered when list is emptyShow empty state
set(onLoad:)Triggered when conversations loadAnalytics tracking

Styling

Quick Styling

let conversations = CometChatConversations()

// Apply custom colors
CometChatConversation.style.backgroundColor = .systemBackground
CometChatConversation.style.lastMessageTextColor = .secondaryLabel
CometChatConversation.style.typingIndicatorColor = .systemGreen

// Custom avatar style
let avatarStyle = AvatarStyle()
avatarStyle.backgroundColor = UIColor(hex: "#6851D6")
avatarStyle.cornerRadius = 8
CometChatConversation.style.avatarStyle = avatarStyle

// Custom badge style for unread count
let badgeStyle = BadgeStyle()
badgeStyle.backgroundColor = UIColor(hex: "#F44649")
badgeStyle.cornerRadius = CometChatCornerStyle(cornerRadius: 10)
CometChatConversation.style.badgeStyle = badgeStyle

Style Properties

PropertyDescription
backgroundColorBackground color of the list
lastMessageTextColorColor of the last message preview
typingIndicatorColorColor of “typing…” indicator
separatorColorColor of row separators
avatarStyleStyle for user/group avatars
badgeStyleStyle for unread message count
receiptStyleStyle for read/delivered receipts
dateStyleStyle for timestamp labels

Hide UI Elements

let conversations = CometChatConversations()

// Hide elements as needed
conversations.hideSearch = true              // Hide search bar
conversations.hideReceipts = true            // Hide read/delivered receipts
conversations.hideUserStatus = true          // Hide online/offline status
conversations.hideGroupType = true           // Hide public/private group icons
conversations.hideDeleteConversationOption = true  // Hide delete option
conversations.hideNavigationBar = true       // Hide navigation bar
conversations.hideBackButton = true          // Hide back button

Custom Views

Custom List Item

Replace the entire conversation row with your own design:
conversations.set(listItemView: { conversation in
    let customView = CustomConversationCell()
    customView.configure(with: conversation)
    return customView
})
// CustomConversationCell.swift
class CustomConversationCell: UIView {
    
    private let avatarView = CometChatAvatar(image: UIImage())
    private let nameLabel = UILabel()
    private let messageLabel = UILabel()
    private let timeLabel = UILabel()
    private let unreadBadge = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        timeLabel.translatesAutoresizingMaskIntoConstraints = false
        unreadBadge.translatesAutoresizingMaskIntoConstraints = false
        
        nameLabel.font = .systemFont(ofSize: 16, weight: .semibold)
        messageLabel.font = .systemFont(ofSize: 14)
        messageLabel.textColor = .secondaryLabel
        timeLabel.font = .systemFont(ofSize: 12)
        timeLabel.textColor = .tertiaryLabel
        
        unreadBadge.font = .systemFont(ofSize: 12, weight: .bold)
        unreadBadge.textColor = .white
        unreadBadge.backgroundColor = .systemBlue
        unreadBadge.textAlignment = .center
        unreadBadge.layer.cornerRadius = 10
        unreadBadge.clipsToBounds = true
        
        addSubview(avatarView)
        addSubview(nameLabel)
        addSubview(messageLabel)
        addSubview(timeLabel)
        addSubview(unreadBadge)
        
        NSLayoutConstraint.activate([
            avatarView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            avatarView.centerYAnchor.constraint(equalTo: centerYAnchor),
            avatarView.widthAnchor.constraint(equalToConstant: 50),
            avatarView.heightAnchor.constraint(equalToConstant: 50),
            
            nameLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 12),
            nameLabel.topAnchor.constraint(equalTo: avatarView.topAnchor, constant: 4),
            nameLabel.trailingAnchor.constraint(lessThanOrEqualTo: timeLabel.leadingAnchor, constant: -8),
            
            messageLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
            messageLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 4),
            messageLabel.trailingAnchor.constraint(lessThanOrEqualTo: unreadBadge.leadingAnchor, constant: -8),
            
            timeLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            timeLabel.topAnchor.constraint(equalTo: nameLabel.topAnchor),
            
            unreadBadge.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            unreadBadge.centerYAnchor.constraint(equalTo: messageLabel.centerYAnchor),
            unreadBadge.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
            unreadBadge.heightAnchor.constraint(equalToConstant: 20)
        ])
    }
    
    func configure(with conversation: Conversation) {
        var name = ""
        var avatarURL: String?
        
        if let user = conversation.conversationWith as? User {
            name = user.name ?? ""
            avatarURL = user.avatar
        } else if let group = conversation.conversationWith as? Group {
            name = group.name ?? ""
            avatarURL = group.icon
        }
        
        nameLabel.text = name
        avatarView.setAvatar(avatarUrl: avatarURL, with: name)
        
        // Set last message
        if let textMessage = conversation.lastMessage as? TextMessage {
            messageLabel.text = textMessage.text
        } else if conversation.lastMessage is MediaMessage {
            messageLabel.text = "📎 Attachment"
        } else {
            messageLabel.text = "No messages yet"
        }
        
        // Set time
        if let sentAt = conversation.lastMessage?.sentAt {
            let date = Date(timeIntervalSince1970: TimeInterval(sentAt))
            let formatter = DateFormatter()
            formatter.dateFormat = "h:mm a"
            timeLabel.text = formatter.string(from: date)
        }
        
        // Set unread count
        let unreadCount = conversation.unreadMessageCount
        unreadBadge.isHidden = unreadCount == 0
        unreadBadge.text = unreadCount > 99 ? "99+" : "\(unreadCount)"
    }
}

Custom Subtitle View

Customize just the subtitle area (below the name):
conversations.set(subtitleView: { conversation in
    let label = UILabel()
    label.font = .systemFont(ofSize: 13)
    label.textColor = .secondaryLabel
    
    if let textMessage = conversation.lastMessage as? TextMessage {
        label.text = textMessage.text
    } else if conversation.lastMessage is MediaMessage {
        label.text = "📷 Photo"
    }
    
    return label
})

Custom Date Format

conversations.set(datePattern: { conversation in
    guard let sentAt = conversation.lastMessage?.sentAt else { return "" }
    
    let date = Date(timeIntervalSince1970: TimeInterval(sentAt))
    let formatter = DateFormatter()
    
    if Calendar.current.isDateInToday(date) {
        formatter.dateFormat = "h:mm a"
    } else if Calendar.current.isDateInYesterday(date) {
        return "Yesterday"
    } else {
        formatter.dateFormat = "MMM d"
    }
    
    return formatter.string(from: date)
})

Listen for Events

React to conversation changes in your app:
class MyViewController: UIViewController, CometChatConversationEventListener {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        CometChatConversationEvents.addListener("my-listener", self)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        CometChatConversationEvents.removeListener("my-listener")
    }
    
    // Called when a conversation is deleted
    func ccConversationDelete(conversation: Conversation) {
        print("Conversation deleted: \(conversation.conversationId ?? "")")
    }
}

Complete App Example

Here’s a full implementation with tab bar integration:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class MainTabBarController: UITabBarController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupTabs()
    }
    
    private func setupTabs() {
        // Conversations Tab
        let conversationsVC = ConversationsContainerViewController()
        conversationsVC.tabBarItem = UITabBarItem(
            title: "Chats",
            image: UIImage(systemName: "message"),
            selectedImage: UIImage(systemName: "message.fill")
        )
        
        // Users Tab
        let usersVC = UINavigationController(rootViewController: CometChatUsers())
        usersVC.tabBarItem = UITabBarItem(
            title: "Users",
            image: UIImage(systemName: "person.2"),
            selectedImage: UIImage(systemName: "person.2.fill")
        )
        
        // Groups Tab
        let groupsVC = UINavigationController(rootViewController: CometChatGroups())
        groupsVC.tabBarItem = UITabBarItem(
            title: "Groups",
            image: UIImage(systemName: "person.3"),
            selectedImage: UIImage(systemName: "person.3.fill")
        )
        
        viewControllers = [conversationsVC, usersVC, groupsVC]
    }
}

class ConversationsContainerViewController: UINavigationController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let conversations = CometChatConversations()
        
        // Navigate to messages on tap
        conversations.set(onItemClick: { [weak self] conversation, _ in
            let messagesVC = CometChatMessages()
            
            if let user = conversation.conversationWith as? User {
                messagesVC.set(user: user)
            } else if let group = conversation.conversationWith as? Group {
                messagesVC.set(group: group)
            }
            
            self?.pushViewController(messagesVC, animated: true)
        })
        
        setViewControllers([conversations], animated: false)
    }
}

Troubleshooting

IssueSolution
Empty conversation listEnsure user is logged in and has existing conversations
Conversations not updatingCheck that real-time listeners are active
Navigation not workingVerify navigationController is not nil
Custom views not appearingEnsure custom view has proper constraints