Message threads
The message threads feature organizes conversations into threads, making it easier to follow and respond to specific topics.
Users need threads to:
-
Set topic-specific discussions - within group chats or 1:1 conversations, you can initiate or participate in separate threads dedicated to specific topics, ensuring focused and structured discussions.
-
Clarify and resolve issues - message threads let you address specific problems or questions within a conversation, allowing you to provide relevant input and ensure clarity and efficient problem-solving.
-
Reduce confusion - by allowing you to focus the conversation in a separate thread, you minimize cross-talk and make the conversation easier to follow.
In the Chat SDK, a thread is a separate channel created for a selected message. Such a thread channel gets the ID starting with PUBNUB_INTERNAL_THREAD
followed by the channel ID and message ID (separated by underscores). The message for which you create a thread channel has the hasThread
parameter set to true
to distinguish it from other messages. All messages in a thread (thread messages) are ordered by timetokens containing info on when messages were published.
Each thread message and thread channel are separate entities in the Chat SDK that extend the Message
and Channel
entities. This means that they offer separate methods for handling threads but also let you use all methods under the Message
and Channel
entities (for example, for editing or deleting).
Create thread
createThread()
creates a thread (channel) for a selected message.
Method signature
This method has the following signature:
createThread(completion: ((Swift.Result<ThreadChannelImpl, Error>) -> Void)? = nil)
Input
This method doesn't take any parameters.
Output
Type | Description |
---|---|
((Swift.Result<ThreadChannelImpl, Error>) -> Void) | Object returning the thread channel metadata for the message updated with the hasThread parameter (and a threadRootId action type underneath). |
Basic usage
Create a thread for a message on the support
channel.
chat?.getChannel(
channelId: "support"
) {
switch $0 {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
/// Timetoken for the message you're trying to get
let timetoken: Timetoken = 16200000000000001
/// Get the message with the specified timetoken
channel.getMessage(timetoken: timetoken) {
switch $0 {
case let .success(message):
if let message = message {
debugPrint("Message fetched successfully: \(message.text)")
show all 39 linesSend thread message
Reply to a message in a thread by calling the sendText()
method from the previously created ThreadChannel
object.
Method signature
Head over to the sendText()
method section for details.
Basic usage
Send a message in a thread created for the last message on the support
channel.
chat?.getChannel(
channelId: "support"
) {
switch $0 {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
/// Timetoken for the message you're trying to get
let timetoken: Timetoken = 16200000000000001
/// Get the message with the specified timetoken
channel.getMessage(timetoken: timetoken) {
switch $0 {
case let .success(message):
if let message = message {
debugPrint("Message fetched successfully: \(message.text)")
show all 39 linesGet thread
Get the thread channel on which the thread message is published.
Method signature
This method has the following signature:
message.getThread(completion: ((Swift.Result<ThreadChannelImpl, Error>) -> Void))
Input
This method doesn't take any parameters.
Output
Type | Description |
---|---|
((Swift.Result<ThreadChannelImpl, Error>) -> Void)) | Object returning the thread channel metadata. |
Basic usage
Get the thread channel created from the message with the 16200000000000001
timetoken.
chat?.getChannel(
channelId: "support"
) {
switch $0 {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
/// Timetoken for the message you're trying to get
let timetoken: Timetoken = 16200000000000001
/// Get the message with the specified timetoken
channel.getMessage(timetoken: timetoken) {
switch $0 {
case let .success(message):
if let message = message {
debugPrint("Message fetched successfully: \(message.text)")
show all 38 linesCheck if message starts thread
You can access the hasThread
property of the Message
object to check if a given message starts a thread.
Basic usage
Check if the message with the 16200000000000001
timetoken starts a thread.
chat?.getChannel(
channelId: "support"
) {
switch $0 {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
/// Timetoken for the message you're trying to get
let timetoken: Timetoken = 16200000000000001
/// Get the message with the specified timetoken
channel.getMessage(timetoken: timetoken) {
switch $0 {
case let .success(message):
show all 33 linesGet thread message updates
You can receive updates when specific message threads and related message reactions are added, edited, or removed on other clients using the streamUpdatesOn()
on the ThreadMessage
object.
This method accepts a callback function as an argument. The Chat SDK invokes this callback whenever someone adds, edits or deletes a message, or adds or removes a message reaction to/from a list of thread messages.
Underneath, this method subscribes the current user to a channel and adds a message reactions event listener to receive all messageAction
events of type added
or removed
. These methods also return the unsubscribe
function you can invoke to stop receiving messageAction
events and unsubscribe from the channel.
Method signature
This method takes the following parameters:
threadMessage.streamUpdatesOn(
messages: [ThreadMessageImpl],
callback: @escaping (([ThreadMessageImpl]) -> Void)
) -> AutoCloseable
Input
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
messages | [ThreadMessageImpl] | Yes | n/a | Collection of ThreadMessageImpl objects for which you want to get updates on changed message threads or related message reactions. |
callback | (([ThreadMessageImpl]) -> Void) | Yes | n/a | Callback function passed to the method as a parameter. It defines the custom behavior to be executed when detecting changes in message threads or related message reactions. |
Output
Type | Description |
---|---|
AutoCloseable | Interface that lets you stop receiving message thread-related updates by invoking the close() method. |
Basic usage
Get message threads and message reaction-related updates for the first page of messages published in a thread on the support
channel.
/// Assuming you have a "ThreadChannelImp" representation of "support"
threadChannel.getHistory(count: 25) { historyResult in
switch historyResult {
case let .success(historyResponse):
let messages = historyResponse.messages
debugPrint("Fetched messages: \(messages.map { $0.text })")
// Messages that are part of threads
let threadMessages = messages
// Stream updates for the fetched thread messages
let autoCloseable = ThreadMessageImpl.streamUpdatesOn(messages: threadMessages) { updatedThreadMessages in
updatedThreadMessages.forEach { updatedThreadMessage in
debugPrint("-=Updated thread message: \(updatedThreadMessage)")
}
}
// To stop streaming updates at some later point, use:
show all 20 linesGet thread channel updates
You can receive updates when specific ThreadChannel
object(s) are edited or removed on other clients using the streamUpdatesOn()
method that checks updates on a channel list and it's tied to the ThreadChannel
class.
This method accepts a callback function as an argument. The Chat SDK invokes this callback whenever someone adds, changes, or removes channel thread metadata.
Underneath, these methods subscribe the current user to a channel and add an objects event listener to receive all objects
events of type channel
. These methods also return the unsubscribe
function you can invoke to stop receiving objects
events and unsubscribe from the channel.
Method signature
This method takes the following parameters:
threadChannel.streamUpdatesOn(
channels: [ThreadChannelImpl],
callback: @escaping (([ThreadChannelImpl]) -> Void)
) -> AutoCloseable
Input
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
channels | ThreadChannelImpl] | Yes | n/a | Array of ThreadChannelImpl objects for which you want to get updates on changed channel threads. |
callback | @escaping (([ThreadChannelImpl]) -> Void) | Yes | n/a | Callback function passed to the method as a parameter. It defines the custom behavior to be executed when detecting changes in channel threads. |
Output
Type | Description |
---|---|
AutoCloseable | Interface that lets you stop receiving message thread-related updates by invoking the close() method. |
Basic usage
// Get the support channel
/// Assuming you have a "ThreadChannelImp" representation of "support"
/// Fetch the first page of messages
threadChannel.getHistory(count: 25) { historyResult in
switch historyResult {
case let .success(historyResponse):
let messages = historyResponse.messages
debugPrint("Fetched messages: \(messages.map { $0.text })")
// Filter messages that are part of threads
let threadMessages = messages
// Stream updates for the fetched thread messages
if !threadMessages.isEmpty {
let threadChannels = [threadChannel]
let autoCloseable = ThreadChannelImpl.streamUpdatesOn(channels: threadChannels) { updatedThreadChannels in
show all 30 linesGet historical thread message
getHistory()
called on the ThreadChannel
object fetches historical thread messages from that thread channel.
Method signature
This method takes the following parameters:
threadChannel.getHistory(
startTimetoken: Timetoken? = nil,
endTimetoken: Timetoken? = nil,
count: Int = 25,
completion: ((Swift.Result<(messages: [ThreadMessageImpl], isMore: Bool), Error>) -> Void)?
)
Input
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
startTimetoken | Timetoken | No | n/a | Timetoken delimiting the start of a time slice (exclusive) to pull thread messages from. For details, refer to the Fetch History section. |
endTimetoken | Timetoken | No | n/a | Timetoken delimiting the end of a time slice (inclusive) to pull thread messages from. For details, refer to the Fetch History section. |
count | Int | Yes | 25 | Number of historical thread messages to return for the channel in a single call. Since each call returns all attached message reactions by default, the maximum number of returned thread messages is 25 . For more details, refer to the description of the includeMessageActions parameter in the Swift SDK docs. |
Output
Parameter | Type | Description |
---|---|---|
((Swift.Result<(messages: [ThreadMessageImpl], isMore: Bool), Error>) -> Void) | object | completion holding HistoryResponse containing a list of ThreadMessageImpl objects and a Boolean flag indicating if there are more messages available. |
By default, each call returns all message reactions and metadata attached to the retrieved thread messages.
Basic usage
From the thread created for the last message in the support
channel, fetch 10
historical thread messages that are older than the timetoken 15343325214676133
.
// Assuming you have a "ThreadChannelImp" representation of "support"
// Get the last message on the channel, which is the root message for the thread
threadChannel.getHistory(count: 1) { messageResult in
switch messageResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult {
case let .success(threadChannel):
debugPrint("Fetched thread channel with ID: \(threadChannel.id)")
// Fetch the required historical messages from the thread channel
threadChannel.getHistory(
startTimetoken: nil,
show all 43 linesRemove thread
removeThread()
removes a thread (channel) for a selected message.
Method signature
This method has the following signature:
message.removeThread(completion: ((Swift.Result<ChannelImpl, Error>) -> Void)? = nil)
Input
This method doesn't take any parameters.
Output
Type | Description |
---|---|
((Swift.Result<ChannelImpl, Error>) -> Void) | A pair of values containing an object with details about the result of the remove message action (indicating whether the message was successfully removed and potentially including additional metadata or information about the removal) and the updated channel object after the removal of the thread. |
Basic usage
Remove a thread for the last message on the support
channel.
// Reference the "support" channel
chat.getChannel(channelId: "support") { result in
switch result {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message from the channel's history
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Remove the thread for the last message
message.removeThread { removeThreadResult in
switch removeThreadResult {
show all 38 linesPin thread message to thread channel
pinMessage()
called on the ThreadChannel
object pins a selected thread message to the thread channel.
Method signature
This method takes the following parameters:
threadChannel.pinMessage(
message: MessageImpl,
completion: ((Swift.Result<ThreadChannelImpl, Error>) -> Void)? = nil
)
Input
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
message | MessageImpl | Yes | n/a | MessageImpl object you want to pin to the selected thread channel. |
Output
Type | Description |
---|---|
((Swift.Result<ThreadChannelImpl, Error>) -> Void) | Object returning the thread channel metadata updated with these custom fields: pinnedMessageTimetoken to mark the timetoken when the message was pinned pinnedMessageChannelID to mark the channel on which the message was pinned to the thread channel (unpinning was performed either directly on the parent channel or on a thread channel). |
Basic usage
A thread was created for the last message in the support
parent channel. Pin the last message from this thread to the thread channel.
// Reference the "support" channel
chat.getChannel(channelId: "support") { result in
switch result {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult {
show all 63 linesPin thread message to parent channel
You can pin a selected thread message to the parent channel with pinMessageToParentChannel()
and pinToParentChannel()
. They give the same output, and the only difference is that you call a given method either on the ThreadChannel
or the ThreadMessage
object. Depending on the object, these methods take different input parameters - you have to specify the thread message you want to pin or not because it's already known.
Method signature
These methods take the following parameters:
-
pinMessageToParentChannel()
threadChannel.pinMessageToParentChannel(
message: ThreadMessageImpl,
completion: ((Swift.Result<ChannelImpl, Error>) -> Void)? = nil
) -
pinToParentChannel()
threadMessage.pinToParentChannel(completion: ((Swift.Result<ChannelImpl, Error>) -> Void)?)
Input
Parameter | Type | Required in pinMessageToParentChannel() | Required in pinToParentChannel() | Default | Description |
---|---|---|---|---|---|
message | ThreadMessageImpl | Yes | No | n/a | ThreadMessageImpl object you want to pin to the selected parent channel. |
Output
Type | Description |
---|---|
((Swift.Result<ChannelImpl, Error>) -> Void) | Object returning the channel metadata updated with these custom fields: pinnedMessageTimetoken to mark the timetoken when the message was pinned pinnedMessageChannelID to mark the channel on which the message was pinned to the parent channel (pinning was performed either directly on the parent channel or on a thread channel). |
Basic usage
A thread was created for the last message in the support
parent channel. Pin the last message from this thread to the parent channel.
-
pinMessageToParentChannel()
show all 63 lines// Reference the "support" channel
chat.getChannel(channelId: "support") { result in
switch result {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult { -
pinToParentChannel()
show all 62 lineschat.getChannel(channelId: "support") { result in
switch result {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult {
case let .success(threadChannel):
Unpin thread message from thread channel
unpinMessage()
called on the ThreadChannel
object unpins the previously pinned thread message from the thread channel.
Method signature
This method has the following signature:
threadChannel.unpinMessage(completion: ((Swift.Result<ThreadChannelImpl, Error>) -> Void)? = nil)
Input
This method doesn't take any parameters.
Output
Type | Description |
---|---|
((Swift.Result<ThreadChannelImpl, Error>) -> Void) | Object returning the thread channel metadata updated with these custom fields: pinnedMessageTimetoken to mark the timetoken when the message was unpinned pinnedMessageChannelID to mark the channel on which the message was unpinned from the thread channel (unpinning was performed either directly on the parent channel or on a thread channel). |
Basic usage
Unpin the thread message from the thread (channel) created for the last message on the support
channel.
// Reference the "support" channel where the root message for the thread is published
chat?.getChannel(channelId: "support") { channelResult in
switch channelResult {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult {
show all 63 linesUnpin thread message from parent channel
You can unpin the previously pinned thread message from the parent channel with unpinMessageFromParentChannel()
and unpinFromParentChannel()
.
Method signature
These methods have the following signatures:
-
unpinMessageFromParentChannel()
threadChannel.unpinMessageFromParentChannel(completion: ((Swift.Result<ChannelImpl, Error>) -> Void)? = nil)
-
unpinFromParentChannel()
threadMessage.unpinFromParentChannel(completion: ((Swift.Result<ChannelImpl, Error>) -> Void)?)
Input
These methods don't take any parameters.
Output
Type | Description |
---|---|
((Swift.Result<ChannelImpl, Error>) -> Void) | Object returning the channel metadata updated with these custom fields: pinnedMessageTimetoken to mark the timetoken when the message was unpinned pinnedMessageChannelID to mark the channel on which the message was unpinned from the parent channel (unpinning was performed either directly on the parent channel or on a thread channel). |
Basic usage
Unpin the thread message from the support
parent channel.
-
unpinMessageFromParentChannel()
show all 63 lines// Reference the "support" channel where the root message for the thread is published
chat?.getChannel(channelId: "support") { channelResult in
switch channelResult {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult { -
unpinFromParentChannel()
show all 63 lines// Reference the "support" channel where the root message for the thread is published
chat?.getChannel(channelId: "support") { channelResult in
switch channelResult {
case let .success(channel):
if let channel = channel {
debugPrint("Fetched channel metadata with ID: \(channel.id)")
// Get the last message on the channel, which is the root message for the thread
channel.getHistory(count: 1) { historyResult in
switch historyResult {
case let .success(historyResponse):
if let message = historyResponse.messages.first {
debugPrint("Fetched last message: \(message.text)")
// Get the thread channel
message.getThread { threadChannelResult in
switch threadChannelResult {