flowchart LR
A[User Action]
A -->|Leave Channel| F[Update Channel Draft]
A -->|Leave Thread| E[Update Thread Draft]
A -->|Clear Composer Content| D[Delete Draft]
A -->|Publish Message| D[Delete Draft]
Draft Messages
The UIKit SDK provides a way to create and manage draft messages.
Drafts are disabled by default. In order to enable this feature, you need to enable the Components.default.isDraftMessagesEnabled
.
Drafts on UIKit are available since version 4.73.0.
Draft messages are synchronized across devices and work seamlessly offline as well.
Basic Usage
When draft messages are enabled, the logic of saving and deleting drafts will be handled automatically by the SDK. When a user starts typing a message and then navigates away from the conversation, the message content is automatically saved as a draft, for both Channels and Threads.
When the user returns to the conversation, the draft message will be loaded automatically into the composer. If the user clears the composer content or publishes the message, the draft will be deleted.
The draft messages user flow is described in the following diagram:
Customization
By default, the only additional UI added to the SDK when drafts are enabled is a preview of the draft message in the channel list and thread list. When a draft is saved, the draft message is shown in the channel list and thread list until the message is published or deleted.
Channel Draft Preview
The channel draft preview is displayed in the ChatChannelListItemView
when thechannel.draftMessage
exists. You can customize the default logic by overriding this component.
One simple customization is to change the prefix text. You can do this by overriding the ChatChannelListItemView.draftPrefixText
property.
By default, the timestamp of the last message is still displayed, since this is common in most apps. If you want to hide it, you can override the ChatChannelListItemView.updateContent
property.
class CustomChannelListItemView: ChatChannelListItemView {
override func updateContent() {
super.updateContent()
// Hide the timestamp
let hasDraftMessage = content?.channel.draftMessage != nil
timestampLabel.isHidden = hasDraftMessage
previewMessageDeliveryStatusView.isHidden = hasDraftMessage
}
}
Result:
Before | After |
---|---|
![]() | ![]() |
Thread Draft Preview
The thread draft preview is displayed in the ChatThreadListItemView
when the thread.parentMessage.draftReply
exists.
Like the channel draft preview, you can also customize the prefix text and hide the timestamp.
As an extra customization, let’s change the reply preview text when there is a draft:
class DemoChatThreadListItemView: ChatThreadListItemView {
override func updateContent() {
super.updateContent()
let hasDraftMessage = content?.thread.parentMessage.draftReply != nil
replyTimestampLabel.isHidden = hasDraftMessage
if let draftReply = content?.thread.parentMessage.draftReply {
replyDescriptionLabel.text = "\(draftReply.text) (Draft)"
replyDescriptionLabel.textColor = appearance.colorPalette.accentPrimary.withAlphaComponent(0.7)
replyDescriptionLabel.font = appearance.fonts.footnoteBold
} else {
replyDescriptionLabel.text = replyPreviewText
replyDescriptionLabel.textColor = appearance.colorPalette.subtitleText
replyDescriptionLabel.font = appearance.fonts.footnote
}
}
}
Result:
Before | After |
---|---|
![]() | ![]() |
Draft List Query
At the moment, the SDK does not provide a default UI component to render the list of drafts from the current user. However, you can easily build your own component by using the ChatClient.shared.currentUserController()
to fetch the drafts.
The current user controller has a loadDraftMessages()
method that is responsible to query the drafts. It accepts a query: DraftListQuery
parameter that allows you to customize the query.
The DraftListQuery
has the following properties:
pagination
: The initial pagination information. By default isPagination(pageSize: 25, offset: 0)
sorting
: The sorting criteria for the drafts. By default is[.init(key: .createdAt, isAscending: false)]
Fetching Drafts
Below is an example on how to fetch the drafts with the default query:
// Load the draft messages for the current user
let currentUserController = chatClient.currentUserController()
currentUserController.loadDraftMessages { result in
switch result {
case .success(let drafts):
print("Draft messages loaded: \(drafts)")
case .failure(let error):
print("Failed to load draft messages: \(error)")
}
}
Pagination
In case the currentUserController.hasLoadedAllDrafts
is false
, you can load the next page of drafts by calling currentUserController.loadMoreDraftMessages()
. This method has an optional completion handler that will be called when the page finishes loading.
currentUserController.loadMoreDraftMessages { result in
switch result {
case .success(let drafts):
print("Draft messages loaded: \(drafts)")
case .failure(let error):
print("Failed to load more draft messages: \(error)")
}
}
Delegate
Whenever the drafts are updated or deleted, the CurrentChatUserControllerDelegate
will be notified in the following function:
func currentUserController(
_ controller: CurrentChatUserController,
didChangeDraftMessages draftMessages: [DraftMessage]
)
To see a full drafts list example implementation, you can check our Demo App example here.
Draft Events
By default, the SDK will react whenever a draft is updated or deleted. But, in case your app needs additional logic, these are the available draft events:
DraftUpdatedEvent
: Triggered when a draft is updated.DraftDeletedEvent
: Triggered when a draft is deleted.