Group Channel

Group Channel is a chat that provides close interactions among limited number of people. The group channel can be private or a public. A private group channel can let a user join the chat through an invitation by another user who is already a member of the chatroom. For 1-on-1 messaging, you can create a private group channel with two members. A public group chat can let a user join the chat without invitation. A group channel can consist of one to one hundred members by default setting. This default number of members can increase per request.

Users receive all messages from the group channels that they are a member of. And they can receive push notifications, typing indicators, unread counts and read receipts from these channels when they go offline. For more information, see the Push Notifications section which describes how to turn on and manage the push preference.


Choose a type of a group channel

With our iOS SDK, you can use a variety of behaviour related properties when creating different Group Channels. You can create a group channel after configuring these properties.

Private vs. Public

A Private group channel (default setting) can be accessed only by users that have accepted an invitation by an existing member of that group. On the other hand, a Public group channel can be accessed by any user without an invitation, like an open channel.

Ephemeral vs. Persistent

Messages sent in an Ephemeral group channel are not saved in SendBird infrastructure's database. As such, the old messages that scroll up beyond the user's display due to new messages cannot be retrieved. On the other hand, messages sent in a Persistent group channel (default setting) are stored permanently in the database.

1-on-1 vs. 1-on-N

A Distinct group channel can be reused for the same members. If a new member is invited, or if a member leaves the channel, then the Distinct property is disabled automatically. For example, when attempting to create new group channel with 3 members, A, B, and C, if a channel with same members already exists, a reference to the existing channel is just returned to who has attempted new channel.

Consequently, we recommend turning on the Distinct property in 1-on-1 messaging channels to reuse the existing channel when a user directly sends a message to a friend. If the property is turned off, a new group channel is created with the same friend even if there is a previous chat between them, and you can't see the old messages or data.


Create a group channel with additional information

A group channel can be created on demand by a user through the client SDK. Pass in two user IDs to create a 1-on-1 chat between two users.

You typically want a 1-on-1 chat to be Distinct. If the Distinct property is turned off, a user can create a new channel with the same friend even if there is a previous conversation between them. In this case, multiple 1-on-1 chats between the same two users can exist, each with its own chat history and data.

[SBDGroupChannel createChannelWithUserIds:USER_IDS isDistinct:IS_DISTINCT completionHandler:^(SBDGroupChannel * _Nullable channel, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }

    // ...
}];
SBDGroupChannel.createChannel(withUserIds: USER_IDS, isDistinct: IS_DISTINCT) { (channel, error) in
    guard error == nil else {    // Error. 
        return
    }

    // ...
}

When creating a channel, you can also append additioanl information like cover image and description by specifying several arguments.

[SBDGroupChannel createChannelWithName:NAME isDistinct:IS_DISTINCT userIds:USER_IDS coverUrl:COVER_IMAGE_URL data:DATA customType:CUSTOM_TYPE completionHandler:^(SBDGroupChannel * _Nullable channel, SBDError * _Nullable error) {

}];
SBDGroupChannel.createChannel(withName: NAME, isDistinct: IS_DISTINCT, userIds: USER_IDS, coverUrl: COVER_IMAGE_URL, data: DATA, customType: CUSTOM_TYPE) { (channel, error) in

}
  • NAME: the name of a channel, or the channel topic.
  • COVER_IMAGE_URL: the URL of the cover image, which you can fetch to render into the UI. Alternatively, you can pass in an image file by changing coverUrl to coverImage.
  • DATA: the String field to store structured information, such as a JSON String.
  • CUSTOM_TYPE: the String field that allows you to subclassify your channel.

You can get a cover image URL using the coverUrl property of a SBDGroupChannel instance, and update the channel's cover image by calling one of updateChannelWithName:isDistinct:coverUrl:data:completionHandler:, updateChannelWithName:isDistinct:coverUrl:data:customType:completionHandler:, and updateChannelWithName:coverUrl:data:completionHandler:.

Note: You can also create a group channel via the SendBird Platform API. If you want to control channel creations and member invitations on the server-side, use the Platform API.

Otherwise, you can configure SBDGroupChannelParams and create a new channel with that like below.

NSMutableArray<NSString *> *users = [[NSMutableArray alloc] init];
[users addObject:@"John"];
[users addObject:@"Harry"];
[users addObject:@"Jay"];

NSMutableArray<NSString *> *ops = [[NSMutableArray alloc] init];
[ops addObject:@"Jeff"];

SBDGroupChannelParams *params = [[SBDGroupChannelParams alloc] init];
[params setPublic:NO];
[params setEphemeral:NO];
[params setDistinct:NO];
[params addUserIds:users];
[params setOperatorUserIds:ops];
[params setName:@"NAME"];
[params setCoverImage:FILE];
[params setCoverUrl:@"COVER_URL"];
[params setData:@"DATA"];
[params setCustomType:@"CUSTOM_TYPE"];

[SBDGroupChannel createChannelWithParams:params completionHandler:^(SBDGroupChannel * _Nullable channel, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }
}];
var users: [String] = []
users.append("John")
users.append("Harry")
users.append("Jay")

var ops: [String] = []
ops.append("Jeff")

var params = SBDGroupChannelParams()
params.isPublic = false
params.isEphemeral = false
params.isDistinct = false
params.addUserIds(users)
params.operatorUserIds = ops
params.name = "NAME"
params.coverImage = FILE
params.coverUrl = "COVER_URL"
params.data = "DATA"
params.customType = "CUSTOM_TYPE"

SBDGroupChannel.createChannel(with: params) { (channel, error) in
    guard error == nil else {    // Error.
        return
    }
}

Invite users to a group channel as members

Only members of a group channel can invite new users into the channel. You can determine whether the newly invited user sees the past messages in the channel or not. In your Dashboard Settings - Messages section, there is an option to show channel history. If this option is turned on, new users can view all messages sent before they have joined the channel. If not, new users can see only messages sent after they have been invited.

Note: By default, Show channel history is turned on.

[channel inviteUserIds:userIds completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // ...
}];
channel.inviteUserIds(userIds) { (error) in
    guard error == nil else {    // Error.
        return
    }

    // ...
}

Accept or decline an invitation from other user

A user who is invited to a group channel can accept or decline the invitation. If the invitation is accepted, the user joins the group as a new member and can start chatting with other members. If the user declines the invitation, it is cancelled.

BOOL autoAccept = NO;    // If YES, a user will automatically join a group channel with no choice of accepting and declining an invitation.
[SBDMain setChannelInvitationPreferenceAutoAccept:autoAccept completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];

// In case of accepting an invitation
[channel acceptInvitationWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }
}];

// In case of declining an invitation
[channel declineInvitationWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
// In case of accepting an invitation
var autoAccept = false // If true, a user will automatically join a group channel with no choice of accepting and declining an invitation.
SBDMain.setChannelInvitationPreferenceAutoAccept(autoAccept) { (error) in
    guard error == nil else {    // Error.
        return
    }
}

channel.acceptInvitation { (error) in
    guard error == nil else {    // Error. 
        return
    }
}

// In case of declining an invitation
channel.declineInvitation { (error) in
    guard error == nil else {    // Error. 
        return
    }
}

Join a public group channel as a member

Any user can join a Public Group Channel as a member without an invitation and chat with other members in the channel.

if (channel.isPublic) {
    [channel joinWithCompletionHandler:^(SBDError * _Nullable error) {
        if (error != nil) {    // Error.
            return;
        }
    }];
}
if channel.isPublic {
    channel.join { (error) in
        guard error == nil else {    // Error. 
            return
        }
    }
}

Leave a group channel

If a user leaves the group channel, the user can't receive messages from the channel anymore.

[channel leaveChannelWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }
}];
channel.leave { (error) in
    guard error == nil else {    // Error. 
        return
    }
}

Retrieve a list of group channels

You can obtain a list of all group channels by creating a SDBGroupChannelListQuery instance. The loadNextPageWithCompletionHandler: method returns a list of SBDGroupChannel objects.

Note: You can also set an option to include empty channels with includeEmptyChannel. Empty channels are channels that have been created but contain no sent messages. By default, empty channels are not included (displayed).

SBDGroupChannelListQuery *query = [SBDGroupChannel createMyGroupChannelListQuery];
query.includeEmptyChannel = YES;
[query loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable channels, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // ...
}];
let query = SBDGroupChannel.createMyGroupChannelListQuery()!
query?.includeEmptyChannel = true
query?.loadNextPage(completionHandler: { (channels, error) in
    guard error == nil else {    // Error. 
        return
    }

    // ...
})

Retrieve a group channel by its URL

Since a channel URL is a unique identifier of a group channel, you can use a URL to retrieve a channel instance. Store channel URLs to handle lifecycle or state changes in your app. For example, if a user disconnects from SendBird by temporarily switching to another app, you can provide a smooth restoration of the user's state using a stored URL to fetch the appropriate channel instance, then re-entering the user into the channel.

[SBDGroupChannel getChannelWithUrl:CHANNEL_URL completionHandler:^(SBDGroupChannel * _Nonnull groupChannel, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // Successfully retrieving a group channel.
    // Do something with the channel.
}
}];
SBDGroupChannel.getWithUrl(CHANNEL_URL) { (groupChannel, error) in
    guard error == nil else {    // Error.
        return
    }

    // Successfully retrieving a group channel.
    // Do something with the channel.
}

Hide a group channel from the list

The following code will allow you to hide specific group channels so that it is not included in the list of group channels when a list is retrieved. The hidden channels will re-appear in the list when a new message is delivered within those channels.

[channel hideChannelWithHidePreviousMessages:YES completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }
}];
channel.hide(withHidePreviousMessages: true) { (error) in
    guard error == nil else {    // Error.
        return
    }
}

Filter group channels by user IDs

To filter a channel search by user ID, call setUserIdsExactFilter: or setUserIdsIncludeFilter:queryType: method of SBDGroupChannelListQuery.

Given an example where a user (with the ID "User") is part of two group channels:

  • channelA: { "User", "John", "Jay" }
  • channelB: { "User", "John", "Jay", "Jin" }

An ExactFilter returns the list of channels containing exactly the queried userIDs.

[filteredQuery setUserIdsExactFilter:@[@"John", @"Jay"]];

[query2 loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable channels, SBDError * _Nullable error) {
    // Only 'channelA' is returned.
}];
let query = SBDGroupChannel.createMyGroupChannelListQuery()
query?.setUserIdsExactFilter(["John", "Jay"])
query?.loadNextPage(completionHandler: { (channels, error) in
    // Only 'channelA' is returned.
})

An IncludeFilter returns channels where the userIDs are included. This method can return one of two different results, based on the parameter queryType.

SBDGroupChannelListQuery *filteredQuery = [SBDGroupChannel createMyGroupChannelListQuery];
[filteredQuery setUserIdsIncludeFilter:@[@"John", @"Jay", @"Jin"] queryType:SBDGroupChannelListQueryTypeAnd];
[filteredQuery loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable channels, SBDError * _Nullable error) {
    // Only 'channelB' that include the ids { John, Jay, Jin } as a subset is returned.
}];

[filteredQuery setUserIdsIncludeFilter:@[@"John", @"Jay", @"Jin"] queryType:SBDGroupChannelListQueryTypeOr];
[filteredQuery loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable channels, SBDError * _Nullable error) {
    // 'channelA' and 'channelB' that include { John }, plus 'channelA' and 'channelB' that include { Jay }, plus 'channelB' that include { Jin }.
    // Actually 'channelA' and 'channelB' are returned.
}];
let query = SBDGroupChannel.createMyGroupChannelListQuery()
query?.setUserIdsIncludeFilter(["John", "Jay", "Jin"], queryType: SBDGroupChannelListQueryType.and)
query?.loadNextPage(completionHandler: { (channels, error) in
    // Only 'channelB' that include the ids { John, Jay, Jin } as a subset is returned.

})

query?.setUserIdsIncludeFilter(["John", "Jay", "Jin"], queryType: SBDGroupChannelListQueryType.or)
query?.loadNextPage(completionHandler: { (channels, error) in
    // 'channelA' and 'channelB' that include { John }, plus 'channelA' and 'channelB' that include { Jay }, plus 'channelB' that include { Jin }.
    // Actually 'channelA' and 'channelB' are returned.
})

Send messages to a group channel

In an entered channel, a user can send messages of the following types:

  • UserMessage: text message sent by user.
  • FileMessage: binary message sent by user.

You can additionally specify a CUSTOM_TYPE to further subclassify a message. When you send a text message, you can additionally attach arbitrary strings via a data field. You can utilize this field to send structured data such as font size, font type, or a custom JSON object.

Delivery failures due to the network issues return an exception. By implementing the completionHandler, it is possible to display only the messages that are successfully sent.

[channel sendUserMessage:MESSAGE data:DATA completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // ...
}];
channel.sendUserMessage(MESSAGE, data: DATA, completionHandler: { (userMessage, error) in
    guard error == nil else {    // Error.
        return
    }

    // ...
})

A user can also send any binary file through SendBird. There are two ways in which you can send a binary file: by sending the file itself, or by sending a URL.

Note: To send a variety of metadata along with a file, you can populate the data: property.

Sending a raw file means that you upload it to the SendBird servers. Alternatively, you can choose to send a file hosted in your own servers by passing in a URL that points to the file. In this case, your file is hosted in the SendBird servers, and downloaded through your own servers instead.

Note: If you upload your file directly, a size limit is imposed per file. This limit depends on your plan, and can be viewed from your Dashboard. No file size limit is imposed if you send a file message via a URL. Your file isn't uploaded to the SendBird servers.

// Sending file message with raw file
[channel sendFileMessageWithBinaryData:FILE filename:FILE_NAME type:FILE_TYPE size:FILE_SIZE data:CUSTOM_DATA completionHandler:^(SBDFileMessage * _Nonnull fileMessage, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // ...
}];

// Sending file message with file URL 
[channel sendFileMessageWithUrl:FILE_URL size:FILE_SIZE type:FILE_TYPE data:CUSTOM_DATA completionHandler:^(SBDFileMessage * _Nonnull fileMessage, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // ...
}];
// Sending file message with raw file
channel.sendFileMessage(withBinaryData: FILE, filename: FILE_NAME, type: FILE_TYPE, size: FILE_SIZE, data: CUSTOM_DATA, completionHandler: { (fileMessage, error) in
    guard error == nil else {    // Error.
        return
    }

    // ...    
})

// Sending file message with file URL 
channel.sendFileMessage(withUrl: FILE_URL, size: FILE_SIZE, type: FILE_TYPE, data: CUSTOM_DATA) { (fileMessage, error) in
    guard error == nil else {    // Error.
        return
    }

    // ...      
}

Note: If an app is going to the background while uploading a file such as a profile image or a picture, the app can complete the uploading process by using application:handleEventsForBackgroundURLSession:completionHandler: method in your AppDelegate. To complete the uploading, a background event delegate must be added and implemented in the following delegation. If you don't want to upload the file on the background mode, remove the following delegation in the AppDelegate.

   // AppDelegate.m
   @implementation AppDelegate

       // ...

       - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
           if (completionHandler != nil) {
               completionHandler();
           }
       }
   @end
   // AppDelegate.swift
   class AppDelegate: UIResponder, UIApplicationDelegate {
       func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
           debugPrint("method for handling events for background url session is waiting to be process. background session id: \(identifier)")
           completionHandler()
       }
   ]

Receive messages through a channel delegate

Messages can be received by adding a SBDChannelDelegate. A received BaseMessage object is one of the following three message types.

  • UserMessage: text message sent by user.
  • FileMessage: binary message sent by user.
  • AdminMessage: message sent by an admin through the Platform API.

UNIQUE_DELEGATE_ID is a unique identifier to register multiple concurrent delegates.

@interface GroupChannelViewController : ViewController<SBDChannelDelegate>

@end

// ...

[SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];

// ...

- (void)channel:(SBDBaseChannel * _Nonnull)sender didReceiveMessage:(SBDBaseMessage * _Nonnull)message {
    if (message isKindOfClass:[SBDUserMessage class]]) {
        // Do something when the received message is a UserMessage.
    }
    else if (message isKindOfClass:[SBDFileMessage class]]) {
        // Do something when the received message is a FileMessage.
    }
    else if (message isKindOfClass:[SBDAdminMessage class]]) {
        // Do something when the received message is an AdminMessage.
    }
}
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {

    // ...
    SBDMain.add(self as SBDChannelDelegate, identifier: self.delegateIdentifier)

    // ...

    func channel(_ sender: SBDBaseChannel, didReceive message: SBDBaseMessage) {
        if message is SBDUserMessage {
            // Do something when the received message is a UserMessage.
        }
        else if message is SBDFileMessage {
            // Do something when the received message is a FileMessage.
        }
        else if message is SBDAdminMessage {
            // Do something when the received message is an AdminMessage.
        }
    }
}

If the UI isn't valid anymore, remove the channel delegate.

[SBDMain removeChannelDelegateForIdentifier:UNIQUE_DELEGATE_ID];
SBDMain.removeChannelDelegate(forIdentifier: UNIQUE_DELEGATE_ID)

Mention other members in a group channel

Sometimes it needs to call the attention of some members in a group channel which the push notification is restricted. If you implement the following code, the mentioned members (up to 10) will be notified.

NSMutableArray<NSString *> *mentionedUserIds = [[NSMutableArray alloc] init];
[mentionedUserIds addObject:@"John"];
[mentionedUserIds addObject:@"Jay"];
[mentionedUserIds addObject:@"Jin"];

SBDUserMessageParams *params = [[SBDUserMessageParams alloc] initWithMessage:@"MENTION_1"];
[params setMentionedUserIds:mentionedUserIds];

[channel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
var mentionedUserIds: [String] = []
mentionedUserIds.append("John")
mentionedUserIds.append("Jay")
mentionedUserIds.append("Jin")

var params = SBDUserMessageParams(message: "MENTION_1")
params?.mentionedUserIds = mentionedUserIds

channel.sendUserMessage(with: params) { (userMessage, error) in
    guard error == nil else {    // Error. 
        return
    }
}

Load previous messages in a group channel

You can load previous messages by creating a SBDPreviousMessageListQuery instance. You can display the past messages in your UI once they have loaded.

Note: Whether a user can load messages prior to joining the channel depends on your settings. In your Dashboard Settings - Messages section, there is an option to show channel history. If this option is turned on, new users can view all messages sent before joining the channel. If not, new users can't see messages sent after being invited.

SBDPreviousMessageListQuery *previousMessageQuery = [groupChannel createPreviousMessageListQuery];
[self.previousMessageQuery loadPreviousMessagesWithLimit:30 reverse:YES completionHandler:^(NSArray<SBDBaseMessage *> * _Nullable messages, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
let previousMessageQuery = self.groupChannel.createPreviousMessageListQuery()
previousMessageQuery?.loadPreviousMessages(withLimit: 30, reverse: true, completionHandler: { (messages, error) in
    guard error == nil else {    // Error. 
        return
    }

    // ...     
})

Past messages are queried in fixed numbers (30 in the above code). A new SBDPreviousMessageListQuery instance loads the most recent n messages. And loadPreviousMessagesWithLimit:reverse:completionHandler: on the same query instance returns n messages before that. So if you store your query instance as a member variable, you can traverse through your entire message history.

Note: Before invoking loadPreviousMessagesWithLimit:reverse:completionHandler: method again, you must receive completionHandler callback.


Loading messages in a group channel by timestamp or message ID

You can retrieve a set number of messages starting from a specific timestamp. To load messages sent prior to a specifed timestamp, use getPreviousMessagesByTimestamp:limit:reverse:messageType:customType:completionHandler: method.

[self.channel getPreviousMessagesByTimestamp:TIMESTAMP limit:LIMIT reverse:REVERSE messageType:MESSAGE_TYPE customType:CUSTOM_TYPE completionHandler:^(NSArray<SBDBaseMessage *> * _Nullable messages, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // A list of messages sent before timestamp is successfully retrieved.
}];
self.channel.getPreviousMessages(byTimestamp: TIMESTAMP, limit: LIMIT, reverse: REVERSE, messageType: MESSAGE_TYPE, customType: CUSTOM_TYPE) { (messages, error) in
    guard error == nil else {    // Error. 
        return
    }

    // A list of messages sent before timestamp is successfully retrieved.
}
  • TIMESTAMP: the reference timestamp.
  • LIMIT: the number of messages to load. Note that the actual number of results may be larger than the set value when there are multiple messages with the same timestamp as the earliest message.
  • REVERSE: whether to reverse the results.
  • MESSAGE_TYPE: the SBDMessageTypeFilter enum type. Possible values are limited to SBDMessageTypeFilterAll, SBDMessageTypeFilterUser, SBDMessageTypeFilterFile, or SBDMessageTypeFilterAdmin.
  • CUSTOM_TYPE: the custom type of the messages to be returned.

In a similar way, to load messages sent after a specified timestamp, call getNextMessagesByTimestamp:limit: ... :completionHandler: method. And to load results on either side of the reference timestamp, use getPreviousAndNextMessagesByTimestamp:prevLimit:nextLimit: ... :completionHandler: method.

You can also retrieve a set number of messages starting from a specific message ID. To load messages sent prior to the specified message ID, use getPreviousMessagesByMessageId:limit:reverse:messageType:customType:completionHandler: method.

[self.channel getPreviousMessagesByMessageId:MESSAGE_ID limit:LIMIT reverse:NO messageType:SBDMessageTypeFilterAll customType:CUSTOM_TYPE completionHandler:^(NSArray<SBDBaseMessage *> * _Nullable messages, SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // A list of messages, the IDs of which are prior to the specified message ID, is successfully retrieved.

}];
self.channel.getPreviousMessages(byMessageId: MESSAGE_ID, limit: LIMIT, reverse: REVERSE, messageType: MESSAGE_TYPE, customType: CUSTOM_TYPE) { (messages, error) in
    guard error == nil else {    // Error. 
        return
    }

    // A list of messages, the IDs of which are prior to the specified message ID, is successfully retrieved.
}
  • MESSAGE_ID: the reference message ID.
  • LIMIT: the number of messages to load. Note that the actual number of results may be larger than the set value when there are multiple messages with the same timestamp as the earliest message.
  • REVERSE: whether to reverse the results.
  • MESSAGE_TYPE: the SBDMessageTypeFilter enum type. Possible values are limited to SBDMessageTypeFilterAll, SBDMessageTypeFilterUser, SBDMessageTypeFilterFile, or SBDMessageTypeFilterAdmin.
  • CUSTOM_TYPE: the custom type of the messages to be returned.

Note: To load messages sent after a specified message ID, call getNextMessagesByMessageId: ... : completionHandler: in a similar fashion. To load results on either side of the reference message ID, use getPreviousAndNextMessagesByMessageId:prevLimit:nextLimit: ... :completionHandler: method.


Update a message in a group channel

Users can update any of their own text and file messages sent. An error is returned if a user attempts to update another user's messages. In addition, channel operators can update any messages sent in the channel.

// In case of a user message
[self.channel updateUserMessage:userMessage messageText:MESSAGE_TEXT data:DATA customType:CUSTOM_TYPE completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) {     // Error.
        return;
    }
}];

// In case of a file message
[self.channel updateFileMessage:fileMessage data:DATA customType:CUSTOM_TYPE completionHandler:^(SBDFileMessage * _Nullable fileMessage, SBDError * _Nullable error) {
    if (error != nil) {     // Error.
        return;
    }
}];
// In case of UserMessage
self.openChannel.update(userMessage, messageText: MESSAGE_TEXT, data: DATA, customType: CUSTOM_TYPE) { (updatedUserMessage, error) in
    guard error == nil else {       // Error.
        return
    }
}

// In case of FileMessage
self.openChannel.update(fileMessage, data: DATA, customType: CUSTOM_TYPE) { (updatedFileMessage, error) in
    guard error == nil else {       // Error.
        return
    }
}

The didUpdateMessage: callback of the channel delegate will be invoked after the message is updated, and all participants of the channel will be notified.

@interface GroupChannelViewController : ViewController<SBDChannelDelegate>

@end

- (void)channel:(SBDBaseChannel *)sender didUpdateMessage:(SBDBaseMessage *)message {

}

[SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {

    // ...
    SBDMain.add(self as SBDChannelDelegate, identifier: self.delegateIdentifier)

    // ...
    func channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) {

    }
}

Delete a message from a group channel

Users can delete any messages which were sent by themselves. An error is returned if a user attempts to delete the messages of other members. Also channel operators can delete any messages in the channel.

[channel deleteMessage:baseMessage completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {     // Error.
        return;
    }

    // ...
}];
self.groupChannel.delete(baseMessage, completionHandler: { (error) in
    guard error == nil else {       // Error.
        return
    }

    // ...
})

The messageWasDeleted: callback of channel delegate will be invoked after the message is deleted, and the result also notified to all other members in the channel.

@interface GroupChannelViewController : ViewController<SBDChannelDelegate>

@end

// ...

[SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];

// ...

- (void)channel:(SBDBaseChannel * _Nonnull)sender messageWasDeleted:(long long)messageId {

}
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {

    // ...
    SBDMain.add(self as SBDChannelDelegate, identifier: UNIQUE_DELEGATE_ID)

    // ...
    func channel(_ sender: SBDBaseChannel, messageWasDeleted messageId: Int64) {

    }
}

Clear chat history of a certain member in a group channel

Some users may want to clear their own chat history in select or all of their group channels for various reasons. By using the following code, you can help users clear their chat history and start new chat with other members in the group channels they have joined.

[channel resetMyHistoryWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
channel?.resetMyHistory(completionHandler: { (error) in
    guard error == nil else {    // Error.
        return
    }
})

When a user is online, all data in the group channels they are a member of are automatically updated. However, when a user is disconnected from SendBird and reconnects later, you must call refreshWithCompletionHandler: to update the channels with the latest information.

[channel refreshWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
channel?.refresh(completionHandler: { (error) in
    guard error == nil else {    // Error.
            return
    }
})

Retrieve a list of all members in a group channel

You can retrieve a list of members in a group channel using a getMembers() method.

NSArray<SBDMember *> *members = [channel members];
let members = channel?.members

Members of the channel are automatically updated when a user is online. But when the user is disconnected from SendBird and reconnected, you must call refreshWithCompletionHandler: to update the channels with the latest information. See the Refresh all data related to a group channel section for the sample code.


Retrieve members online status in a group channel

To stay updated on each member's connection status, call refreshWithCompletionHandler: before checking the connectionStatus property of the SBDMember instance for each member. The connectionStatus property specifies a user's connection status with a SendBird server.

Note: If your application needs to keep track of users' connection statuses in real time, we recommend calling a SBDApplicationUserListQuery instance periodically, perhaps in intervals of one minute or more.

The connectionStatus property of a SBDMember instance has one of three values:

  • User.ConnectionStatus.NON_AVAILABLE: user's status information cannot be reached.
  • User.ConnectionStatus.OFFLINE: user is disconnected from a SendBird server.
  • User.ConnectionStatus.ONLINE: user is connected to a SendBird server.

Retrieve a list of banned or muted users in a group channel

You can create a query to get a list of banned or muted users in a group channel. This query is only available for users who are registered as operators of a group channel.

// In case of retrieving banned users
SBDBannedUserListQuery *bannedListQuery = [channel createBannedUserListQuery];
[bannedListQuery loadNextPageWithCompletionHandler:^(NSArray<SBDUser *> * _Nullable users, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];

// In case of retrieving muted users
SBDGroupChannelMemberListQuery *memberUserListQuery = [channel createMemberListQuery];
[memberUserListQuery setMutedMemberFilter:SBDGroupChannelMutedMemberFilterMuted];
[memberUserListQuery loadNextPageWithCompletionHandler:^(NSArray<SBDMember *> * _Nullable users, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }
}];
// In case of retrieving banned users
let bannedListQuery = self.channel.createBannedUserListQuery()
bannedListQuery?.loadNextPage(completionHandler: { (users, error) in
    guard error == nil else {    // Error. 
        return
    }

    // ...
})

// In case of retrieving muted users
let memberUserListQuery = channel?.createMemberListQuery()
memberUserListQuery?.setMutedMemberFilter(SBDGroupChannelMutedMemberFilter.muted)
memberUserListQuery?.loadNextPage(completionHandler: { (members, error) in
    guard error == nil else {    // Error. 
        return
    }
})

Ban and unban a member in a group channel

Operators of a group channel can remove any members that behave inappropriately in the channel by using our Ban function. Users that are banned from a channel can join in the channel again after the time period set by the operators. The operators can ban and unban members in group channels using the following code.

[SBDGroupChannel getChannelWithUrl:CHANNEL_URL completionHandler:^(SBDGroupChannel * _Nullable channel, SBDError * _Nullable error) {
    if (error != nil) {    // Error. 
        return;
    }

    if (channel.myRole == SBDRoleOperator) {
        [channel banUser:USER seconds:SECONDS description:DESCRIPTION completionHandler:^(SBDError * _Nullable error) {
            if (error != nil) {    // Error. 
                return;
            }

            // Custom implementation for what should be done after banning.
        }];

        [channel unbanUser:USER completionHandler:^(SBDError * _Nullable error) {
            if (error != nil) {    // Error. 
                return;
            }

            // Custom implementation for what should be done after unbanning.
        }];
    }
}];
SBDGroupChannel.getWithUrl(CHANNEL_URL) { (channel, error) in
    guard error == nil else {    // Error. 
        return
    }

    if channel?.myRole == SBDRole.operator {
        channel?.ban(USER, seconds: SECONDS, description: DESCRIPTION, completionHandler: { (error) in
            guard error == nil else {    // Error.
                return
            }

            // Custom implementation for what should be done after banning.
        })

        channel?.unbanUser(USER, completionHandler: { (error) in
            guard error == nil else {    // Error.
                return
            }

            // Custom implementation for what should be done after unbanning.
        })
    }
}

Note: You can also use banUserWithUserId() and unbanUserWithUserId() methods, instead of banUser:seconds:completionHandler: and unbanUser:completionHandler: methods, as they have the same functionalities. Please see our API reference on banUserWithUserId:seconds:completionHandler: and unbanUserWithUserId:completionHandler:.


Mute and unmute a participant in an open channel

Operators of an open channel can prohibit selected participants from sending messages using our Mute function. Muted participants will remain in the channel and view the messages, but will not be allowed to send any messages until the operators unmute them. Operators can mute and unmute participants in open channels using the following code:

[SBDGroupChannel getChannelWithUrl:CHANNEL_URL completionHandler:^(SBDGroupChannel * _Nullable channel, SBDError * _Nullable error) {
    if (channel.myRole == SBDRoleOperator) {
        [channel muteUser:USER completionHandler:^(SBDError * _Nullable error) {
            if (error != nil) {    // Error. 
                return;
            }

            // Custom implementation for what should be done after muting.
        }];

        [channel unmuteUser:USER completionHandler:^(SBDError * _Nullable error) {
            if (error != nil) {    // Error. 
                return;
            }

            // Custom implementation for what should be done after unbanning.
        }];
    }
}];
SBDGroupChannel.getWithUrl(CHANNEL_URL) { (channel, error) in
    if channel?.myRole == SBDRole.operator {
        channel?.muteUser(USER, completionHandler: { (error) in
            guard error == nil else {    // Error.
                return
            }

            // Custom implementation for what should be done after muting.
        })

        channel?.unmuteUser(USER, completionHandler: { (error) in
            guard error == nil else {    // Error.
                return
            }

            // Custom implementation for what should be done after unbanning.
        })
    }
}

Note: You can also use muteUserWithUserId:completionHandler: and unmuteUserWithUserId(), instead of muteUser:completionHandler: and unmuteUser:completionHandler:methods, as they have same functionalities. Please see our API reference on muteUserWithUserId:completionHandler: and unmuteUserWithUserId:completionHandler:.


Freeze and unfreeze a group channel

You can temporarily inactivate various functions of a group channel to stop members from chatting in the channel, and re-activate the functions so that the members chat with each other.

// In case of freezing a channel
[channel freezeWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // Custom implementation for what should be done after freezing.
}];

// In case of unfreezing a channel
[channel unfreezeWithCompletionHandler:^(SBDError * _Nullable error) {
    if (error != nil) {    // Error.
        return;
    }

    // Custom implementation for what should be done after unfreezing.
}];
// In case of freezing a channel
channel?.freeze(completionHandler: { (error) in
    guard error == nil else {    // Error.
        return
    }

    // Custom implementation for what should be done after freezing.
})

// In case of unfreezing a channel
channel?.unfreeze(completionHandler: { (error) in
    guard error == nil else {    // Error.
        return
    }

    // Custom implementation for what should be done after unfreezing.
})