Skip to main content
The CometChatMessageList component displays a scrollable list of messages in a conversation. It supports text messages, media messages, reactions, threads, and real-time updates for new messages, edits, and deletions.
{
  "component": "CometChatMessageList",
  "package": "CometChatUIKitSwift",
  "import": "import CometChatUIKitSwift\nimport CometChatSDK",
  "description": "Displays a scrollable list of messages in a conversation with real-time updates for new messages, edits, deletions, reactions, and typing indicators.",
  "inherits": "UIViewController",
  "primaryOutput": {
    "callback": "onMessageClick",
    "type": "(BaseMessage) -> Void"
  },
  "props": {
    "data": {
      "messagesRequestBuilder": {
        "type": "MessagesRequest.MessageRequestBuilder?",
        "default": "nil",
        "note": "Custom request builder for filtering messages"
      },
      "reactionsRequestBuilder": {
        "type": "ReactionsRequest.ReactionsRequestBuilder?",
        "default": "nil",
        "note": "Custom request builder for fetching reactions"
      }
    },
    "callbacks": {
      "onThreadRepliesClick": "(BaseMessage, MessageTemplate) -> Void",
      "onReactionClick": "(ReactionCount, BaseMessage) -> Void",
      "onReactionListItemClick": "(MessageReaction, BaseMessage) -> Void",
      "onError": "(CometChatException) -> Void",
      "onEmpty": "() -> Void",
      "onLoad": "([BaseMessage]) -> Void"
    },
    "visibility": {
      "hideAvatar": { "type": "Bool", "default": false },
      "hideReceipts": { "type": "Bool", "default": false },
      "hideDateSeparator": { "type": "Bool", "default": false },
      "hideHeaderView": { "type": "Bool", "default": false },
      "hideFooterView": { "type": "Bool", "default": false },
      "hideReactionOption": { "type": "Bool", "default": false },
      "hideReplyInThreadOption": { "type": "Bool", "default": false },
      "hideEditMessageOption": { "type": "Bool", "default": false },
      "hideDeleteMessageOption": { "type": "Bool", "default": false },
      "hideCopyMessageOption": { "type": "Bool", "default": false },
      "hideMessageInfoOption": { "type": "Bool", "default": false },
      "hideTranslateMessageOption": { "type": "Bool", "default": false },
      "hideMessagePrivatelyOption": { "type": "Bool", "default": false },
      "hideGroupActionMessages": { "type": "Bool", "default": false },
      "hideNewMessageIndicator": { "type": "Bool", "default": false },
      "hideEmptyView": { "type": "Bool", "default": false },
      "hideErrorView": { "type": "Bool", "default": false },
      "hideLoadingView": { "type": "Bool", "default": false }
    },
    "sound": {
      "disableSoundForMessages": { "type": "Bool", "default": false }
    },
    "behavior": {
      "scrollToBottomOnNewMessages": { "type": "Bool", "default": true },
      "startFromUnreadMessages": { "type": "Bool", "default": false },
      "showMarkAsUnreadOption": { "type": "Bool", "default": false },
      "messageAlignment": { "type": "MessageAlignment", "default": ".standard" }
    },
    "viewSlots": {
      "headerView": "() -> UIView",
      "footerView": "() -> UIView",
      "emptyStateView": "() -> UIView",
      "errorStateView": "() -> UIView",
      "loadingStateView": "() -> UIView",
      "newMessageIndicatorView": "() -> UIView"
    },
    "formatting": {
      "datePattern": "(Int?) -> String",
      "timePattern": "(Int) -> String",
      "dateSeparatorPattern": "(Int?) -> String",
      "textFormatters": "[CometChatTextFormatter]"
    }
  },
  "events": [],
  "sdkListeners": [
    "onMessageReceived",
    "onMessageEdited",
    "onMessageDeleted",
    "onTypingStarted",
    "onTypingEnded",
    "onMessageReactionAdded",
    "onMessageReactionRemoved"
  ],
  "compositionExample": {
    "description": "MessageList is typically used within CometChatMessages alongside MessageHeader and MessageComposer",
    "components": ["CometChatMessageHeader", "CometChatMessageList", "CometChatMessageComposer"],
    "flow": "User views messages → types in composer → sends message → MessageList updates"
  },
  "types": {
    "BaseMessage": {
      "id": "Int",
      "sender": "User?",
      "receiver": "AppEntity?",
      "sentAt": "Int",
      "type": "String",
      "category": "String"
    },
    "MessageAlignment": {
      "standard": "Outgoing messages on right, incoming on left",
      "left": "All messages aligned to left"
    }
  }
}

Where It Fits

CometChatMessageList is the core component for displaying messages in a chat. It’s typically used within CometChatMessages alongside CometChatMessageHeader and CometChatMessageComposer.
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class ChatViewController: UIViewController {
    
    private var messageList: CometChatMessageList!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMessageList()
    }
    
    private func setupMessageList(for user: User) {
        messageList = CometChatMessageList()
        messageList.set(user: user)
        
        // Handle thread replies
        messageList.set(onThreadRepliesClick: { [weak self] message, template in
            self?.openThreadView(for: message)
        })
        
        // Handle reactions
        messageList.set(onReactionClick: { [weak self] reactionCount, message in
            self?.showReactionDetails(reactionCount, for: message)
        })
        
        addChild(messageList)
        view.addSubview(messageList.view)
        messageList.didMove(toParent: self)
    }
    
    private func openThreadView(for message: BaseMessage) {
        let threadMessages = CometChatMessages()
        threadMessages.set(user: message.sender as? User)
        threadMessages.set(parentMessage: message)
        navigationController?.pushViewController(threadMessages, animated: true)
    }
    
    private func showReactionDetails(_ reactionCount: ReactionCount, for message: BaseMessage) {
        // Show reaction details
    }
}

Minimal Render

import CometChatUIKitSwift
import CometChatSDK

let messageList = CometChatMessageList()
messageList.set(user: user)

Filtering

Use MessagesRequest.MessageRequestBuilder to filter which messages appear in the list. The builder pattern allows chaining multiple filter conditions.
import CometChatUIKitSwift
import CometChatSDK

// Create a custom request builder
let messageRequestBuilder = MessagesRequest.MessageRequestBuilder()
    .set(uid: "user-id")
    .set(limit: 30)
    .set(types: ["text", "image", "video"])

let messageList = CometChatMessageList()
messageList.set(user: user)
messageList.set(messagesRequestBuilder: messageRequestBuilder)

Filter Recipes

RecipeCode
Text messages only.set(types: ["text"])
Media messages only.set(types: ["image", "video", "audio", "file"])
Search by keyword.set(searchKeyword: "hello")
Limit results.set(limit: 50)
Messages after timestamp.set(timestamp: 1234567890)
Hide deleted messages.hideDeletedMessages(true)
Specific categories.set(categories: ["message", "custom"])

Actions and Events

Callback Props

onThreadRepliesClick

Fires when a user taps on the thread indicator of a message bubble.
import CometChatUIKitSwift
import CometChatSDK

let messageList = CometChatMessageList()

messageList.set(onThreadRepliesClick: { [weak self] message, template in
    guard let self = self else { return }
    
    let threadMessages = CometChatMessages()
    if let user = message.sender as? User {
        threadMessages.set(user: user)
    }
    threadMessages.set(parentMessage: message)
    self.navigationController?.pushViewController(threadMessages, animated: true)
})

onReactionClick

Fires when a user taps on a reaction on a message bubble.
import CometChatUIKitSwift
import CometChatSDK

let messageList = CometChatMessageList()

messageList.set(onReactionClick: { [weak self] reactionCount, message in
    guard let self = self else { return }
    print("Reaction \(reactionCount.reaction) tapped on message \(message.id)")
})

onReactionListItemClick

Fires when a user taps on a specific reaction in the reaction list.
import CometChatUIKitSwift
import CometChatSDK

let messageList = CometChatMessageList()

messageList.set(onReactionListItemClick: { [weak self] messageReaction, message in
    guard let self = self else { return }
    print("User \(messageReaction.reactedBy?.name ?? "") reacted with \(messageReaction.reaction)")
})

onError

Fires when an error occurs while loading messages.
import CometChatUIKitSwift

let messageList = CometChatMessageList()

messageList.set(onError: { error in
    print("Error loading messages: \(error.errorDescription)")
})

onEmpty

Fires when the message list is empty.
import CometChatUIKitSwift

let messageList = CometChatMessageList()

messageList.set(onEmpty: {
    print("No messages found")
})

onLoad

Fires when messages are successfully loaded.
import CometChatUIKitSwift
import CometChatSDK

let messageList = CometChatMessageList()

messageList.set(onLoad: { messages in
    print("Loaded \(messages.count) messages")
})

Actions Reference

MethodDescriptionExample
set(onThreadRepliesClick:)Triggered when thread indicator is tappedOpen thread view
set(onReactionClick:)Triggered when a reaction is tappedShow reaction details
set(onReactionListItemClick:)Triggered when reaction list item is tappedShow who reacted
set(onError:)Triggered when an error occursShow error alert
set(onEmpty:)Triggered when list is emptyShow empty state
set(onLoad:)Triggered when messages loadAnalytics tracking
scrollToBottom(isAnimated:)Scrolls to the bottom of the listNavigate to latest
goToMessage(withId:)Scrolls to a specific messageJump to message

SDK Events (Real-Time, Automatic)

SDK ListenerInternal behavior
onMessageReceivedAdds new message to the list
onMessageEditedUpdates the edited message in place
onMessageDeletedRemoves or marks message as deleted
onTypingStartedShows typing indicator
onTypingEndedHides typing indicator
onMessageReactionAddedUpdates reaction count on message
onMessageReactionRemovedUpdates reaction count on message

Custom View Slots

SlotSignatureReplaces
headerView() -> UIViewHeader above message list
footerView() -> UIViewFooter below message list
emptyStateView() -> UIViewEmpty state display
errorStateView() -> UIViewError state display
loadingStateView() -> UIViewLoading state display
newMessageIndicatorView() -> UIViewNew message indicator

headerView

Add a custom header above the message list.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let headerView = UIView()
headerView.backgroundColor = UIColor.systemGray6

let label = UILabel()
label.text = "Pinned Messages"
label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
label.textColor = UIColor.secondaryLabel
headerView.addSubview(label)

messageList.set(headerView: headerView)

footerView

Add a custom footer below the message list.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let footerView = UIView()
footerView.backgroundColor = UIColor.systemGray6

let quickRepliesStack = UIStackView()
quickRepliesStack.axis = .horizontal
quickRepliesStack.spacing = 8

let replies = ["👍", "Thanks!", "Got it"]
for reply in replies {
    let button = UIButton(type: .system)
    button.setTitle(reply, for: .normal)
    button.backgroundColor = UIColor.systemBackground
    button.layer.cornerRadius = 16
    quickRepliesStack.addArrangedSubview(button)
}

footerView.addSubview(quickRepliesStack)
messageList.set(footerView: footerView)

emptyStateView

Customize the view shown when there are no messages.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let emptyView = UIView()
let imageView = UIImageView(image: UIImage(systemName: "bubble.left.and.bubble.right"))
imageView.tintColor = UIColor.tertiaryLabel

let label = UILabel()
label.text = "No messages yet. Start the conversation!"
label.textColor = UIColor.secondaryLabel
label.textAlignment = .center

emptyView.addSubview(imageView)
emptyView.addSubview(label)

messageList.set(emptyView: emptyView)

loadingStateView

Customize the loading indicator.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let loadingView = UIActivityIndicatorView(style: .large)
loadingView.color = UIColor.systemBlue
loadingView.startAnimating()

messageList.set(loadingView: loadingView)

errorStateView

Customize the error state view.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let errorLabel = UILabel()
errorLabel.text = "Something went wrong!"
errorLabel.textColor = .red
messageList.set(errorView: errorLabel)

newMessageIndicatorView

Set a custom view for the unread message indicator.
import UIKit
import CometChatUIKitSwift

let messageList = CometChatMessageList()

let newMessageIndicatorView = NewUnreadMessageIndicator()
messageList.set(newMessageIndicatorView: newMessageIndicatorView)

class NewUnreadMessageIndicator: UIView {
    
    private let label: UILabel = {
        let label = UILabel()
        label.text = "New Messages ↓"
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
        label.textAlignment = .center
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = UIColor.systemBlue
        layer.cornerRadius = 16
        
        addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: centerXAnchor),
            label.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Text Formatters

Customize how text is displayed in messages using text formatters.
import CometChatUIKitSwift
import CometChatSDK

let myCustomTextFormatter = MyCustomTextFormatter(trackingCharacter: "#")

let cometChatMessages = CometChatMessages()
    .set(user: user)
    .set(textFormatter: [myCustomTextFormatter])
You can create a custom text formatter:
import Foundation
import CometChatSDK
import CometChatUIKitSwift

class MyCustomTextFormatter: CometChatTextFormatter {
    
    override func getRegex() -> String {
        return "(\\bsure\\b)"
    }

    override func getTrackingCharacter() -> Character {
        return "#"
    }

    override func search(string: String, suggestedItems: ((_: [SuggestionItem]) -> ())? = nil) {
        // This function would call an API or perform a local search
    }

    override func onScrollToBottom(suggestionItemList: [SuggestionItem], listItem: ((_: [SuggestionItem]) -> ())?) {
        // This function would call the next page of an API
    }

    override func onItemClick(suggestedItem: SuggestionItem, user: User?, group: Group?) {
        // Handle clicked item
    }

    override func handlePreMessageSend(baseMessage: BaseMessage, suggestionItemList: [SuggestionItem]) {
        // Modify the message before it's sent
    }

    override func prepareMessageString(
        baseMessage: BaseMessage,
        regexString: String,
        alignment: MessageBubbleAlignment = .left,
        formattingType: FormattingType
    ) -> NSAttributedString {
        let attrString = NSMutableAttributedString(string: "SURE")
        if alignment == .left {
            // Received message styling
            attrString.addAttribute(.foregroundColor, value: UIColor.blue, range: NSRange(location: 0, length: attrString.length))
        } else {
            // Sent message styling
            attrString.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: 0, length: attrString.length))
        }
        attrString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 18), range: NSRange(location: 0, length: attrString.length))
        return attrString
    }

    override func onTextTapped(baseMessage: BaseMessage, tappedText: String, controller: UIViewController?) {
        // Handle text tap action
    }
}

Styling

Style Hierarchy

  1. Global styles (CometChatMessageList.style) apply to all instances
  2. Instance styles override global for specific instances

Global Level Styling

import UIKit
import CometChatUIKitSwift

// Apply global styles that affect all CometChatMessageList instances
CometChatMessageList.style.backgroundColor = UIColor.systemBackground
CometChatMessageList.style.emptyStateTitleColor = UIColor.label
CometChatMessageList.style.emptyStateTitleFont = UIFont.systemFont(ofSize: 20, weight: .bold)
CometChatMessageList.style.emptyStateSubtitleColor = UIColor.secondaryLabel

// Customize message bubble styles
CometChatMessageList.messageBubbleStyle.outgoing.backgroundColor = UIColor.systemBlue
CometChatMessageList.messageBubbleStyle.incoming.backgroundColor = UIColor.systemGray5

Instance Level Styling

import UIKit
import CometChatUIKitSwift

// Create a custom style for a specific instance
let messageListStyle = MessageListStyle()
messageListStyle.backgroundColor = UIColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1.0)
messageListStyle.borderWidth = 0
messageListStyle.borderColor = .clear

let messageList = CometChatMessageList()
messageList.style = messageListStyle

Key Style Properties

PropertyTypeDefaultDescription
backgroundColorUIColorCometChatTheme.backgroundColor01Background color of the list
borderWidthCGFloat0Border width for the component
borderColorUIColor.clearBorder color for the component
cornerRadiusCometChatCornerStyle?nilCorner radius for the component
shimmerGradientColor1UIColorCometChatTheme.backgroundColor04First shimmer gradient color
shimmerGradientColor2UIColorCometChatTheme.backgroundColor03Second shimmer gradient color
emptyStateTitleColorUIColorCometChatTheme.textColorPrimaryEmpty state title color
emptyStateTitleFontUIFontCometChatTypography.Heading3.boldEmpty state title font
emptyStateSubtitleColorUIColorCometChatTheme.textColorSecondaryEmpty state subtitle color
emptyStateSubtitleFontUIFontCometChatTypography.Body.regularEmpty state subtitle font
errorStateTitleColorUIColorCometChatTheme.textColorPrimaryError state title color
errorStateTitleFontUIFontCometChatTypography.Heading3.boldError state title font
newMessageIndicatorTextColorUIColor?nilNew message indicator text color
newMessageIndicatorBackgroundColorUIColor?nilNew message indicator background

Customization Matrix

What to changeWhereProperty/APIExample
Background colorStylebackgroundColorUIColor.systemBackground
Empty state textStyleemptyStateTitleColor, emptyStateTitleFontCustom colors and fonts
Error state textStyleerrorStateTitleColor, errorStateTitleFontCustom colors and fonts
Loading shimmerStyleshimmerGradientColor1, shimmerGradientColor2Custom gradient colors
Outgoing bubbleStylemessageBubbleStyle.outgoingUIColor.systemBlue
Incoming bubbleStylemessageBubbleStyle.incomingUIColor.systemGray5
Hide avatarsPropertyhideAvatarmessageList.hideAvatar = true
Hide receiptsPropertyhideReceiptsmessageList.hideReceipts = true
Custom headerView Slotset(headerView:)See Custom View Slots section

Props

All props are optional. Sorted alphabetically.

disableSoundForMessages

Disables notification sounds for new messages.
TypeBool
Defaultfalse

hideAvatar

Hides the sender’s avatar in message bubbles.
TypeBool
Defaultfalse

hideCopyMessageOption

Hides the copy message option in message actions.
TypeBool
Defaultfalse

hideDateSeparator

Hides the date separator between messages.
TypeBool
Defaultfalse

hideDeleteMessageOption

Hides the delete message option in message actions.
TypeBool
Defaultfalse

hideEditMessageOption

Hides the edit message option in message actions.
TypeBool
Defaultfalse

hideEmptyView

Hides the empty state view when no messages are available.
TypeBool
Defaultfalse

hideErrorView

Hides the error view when an error occurs.
TypeBool
Defaultfalse

hideFooterView

Hides the footer view of the message list.
TypeBool
Defaultfalse

hideGroupActionMessages

Hides group action messages (join/leave notifications).
TypeBool
Defaultfalse

hideHeaderView

Hides the header view of the message list.
TypeBool
Defaultfalse

hideLoadingView

Hides the loading view when fetching messages.
TypeBool
Defaultfalse

hideMessageInfoOption

Hides the message info option in message actions.
TypeBool
Defaultfalse

hideMessagePrivatelyOption

Hides the option to message privately.
TypeBool
Defaultfalse

hideNewMessageIndicator

Hides the new message indicator.
TypeBool
Defaultfalse

hideReactionOption

Hides the reaction option on messages.
TypeBool
Defaultfalse

hideReceipts

Hides message read receipts (ticks).
TypeBool
Defaultfalse

hideReplyInThreadOption

Hides the reply in thread option.
TypeBool
Defaultfalse

hideTranslateMessageOption

Hides the message translation option.
TypeBool
Defaultfalse

messageAlignment

Sets the alignment of messages in the list.
TypeMessageAlignment
Default.standard

messagesRequestBuilder

Custom request builder for filtering messages.
TypeMessagesRequest.MessageRequestBuilder?
Defaultnil

reactionsRequestBuilder

Custom request builder for fetching reactions.
TypeReactionsRequest.ReactionsRequestBuilder?
Defaultnil

scrollToBottomOnNewMessages

Automatically scrolls to bottom when new messages arrive.
TypeBool
Defaulttrue

showMarkAsUnreadOption

Shows the “Mark as unread” option in message actions.
TypeBool
Defaultfalse

startFromUnreadMessages

Starts the message list from the first unread message.
TypeBool
Defaultfalse

textFormatters

Array of text formatters for customizing message text display.
Type[CometChatTextFormatter]
Default[]

Events

The MessageList component does not emit any global UI events. It relies on SDK listeners for real-time updates.

Date Time Formatter

Customize how timestamps appear in the message list using the CometChatDateTimeFormatter.

Global Level Formatting

import CometChatUIKitSwift

// Customize time format
CometChatMessageList.dateTimeFormatter.time = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm"
    return formatter.string(from: date)
}

// Customize today format
CometChatMessageList.dateTimeFormatter.today = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "h:mm a"
    return "Today • " + formatter.string(from: date)
}

// Customize older dates format
CometChatMessageList.dateTimeFormatter.otherDay = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "dd MMM yyyy"
    return formatter.string(from: date)
}

Instance Level Formatting

import CometChatUIKitSwift

let messageList = CometChatMessageList()

messageList.set(datePattern: { timestamp in
    guard let timestamp = timestamp else { return "" }
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
    let formatter = DateFormatter()
    formatter.dateFormat = "dd-MM-yyyy"
    return formatter.string(from: date)
})

messageList.set(timePattern: { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm"
    return formatter.string(from: date)
})

Date Separator Pattern

import CometChatUIKitSwift

let messageList = CometChatMessageList()

messageList.set(dateSeparatorPattern: { timestamp in
    guard let timestamp = timestamp else { return "" }
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    
    if Calendar.current.isDateInToday(date) {
        return "Today"
    } else if Calendar.current.isDateInYesterday(date) {
        return "Yesterday"
    } else {
        let formatter = DateFormatter()
        formatter.dateFormat = "EEEE, MMM d"
        return formatter.string(from: date)
    }
})

Available Formatters

FormatterPurposeDefault Format
timeStandard time displayh:mm a
todayMessages sent todayToday
yesterdayMessages from yesterdayYesterday
lastweekMessages within last weekDay name
otherDayOlder messagesMMM d, yyyy
datePatternCustom date patternConfigurable
timePatternCustom time patternConfigurable
dateSeparatorPatternDate separator between messagesConfigurable

Troubleshooting

IssueSolution
Messages not loadingEnsure user/group is set and SDK is initialized
Real-time updates not workingCheck SDK connection status and listeners
Reactions not showingVerify reactions are enabled in dashboard
Thread replies not workingEnsure hideReplyInThreadOption is not set to true
Custom views not appearingEnsure custom view has proper constraints and non-zero frame
Scroll position issuesCheck scrollToBottomOnNewMessages setting