iOS
Group Channel Advanced

Group Channel: Advanced

This page explains the advanced features for group channels. Some of them are the premium features available only to a paying user.


Add other users as friends

A user can easily search for other users, add them as friends, and chat with them. In order to add a friend, the user should first upload the other user’s unique key to Sendbird server, such as a phone number that is hashed for privacy. When the unique key is uploaded, the server will identify and match its corresponding user based on the key, and notify the results to the matched user. Then, the matched user can decide whether to add or ignore the friend request.

Note: Before using this feature, make sure the user information includes at least one at least one unique key. If not, update the user information to incorporate unique keys. We recommend that you include unique keys when creating a user.

Objective-C
Swift
Light Color Skin
Copy
// Uploading other users' unique keys to Sendbird server for discovering the corresponding users
NSMutableDictionary<NSString *, NSString *> *discoveryMap = [[NSMutableDictionary alloc] init]; // Store names of friends by unique key
discoveryMap[@"0001-0002-0003"] = @"John";  // A pair of a unique key and a friend's name
discoveryMap[@"0004-0005-0006"] = @"Jay";
discoveryMap[@"0007-0008-0009"] = @"Jin";

[SBDMain uploadFriendDiscoveries:discoveryMap completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];

// Ignoring particular users when Sendbird server discovers the matched users  
NSMutableArray<NSString *> *discoveryKeys = [[NSMutableArray alloc] init]; 
[discoveryKeys addObject:@"0001-0002-0003"];
[discoveryKeys addObject:@"0004-0005-0006"];
[discoveryKeys addObject:@"0007-0008-0009"];

[SBDMain deleteFriendsWithDiscoveries:discoveryKeys completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
// Uploading other users' unique keys to Sendbird server for discovering the corresponding users
var discoveryMap:[String:String] = [:]   // Store names of friends by unique key
discoveryMap["0001-0002-0003"] = "John" // A pair of a unique key and a friend's name
discoveryMap["0004-0005-0006"] = "Jay"
discoveryMap["0007-0008-0009"] = "Jin"

SBDMain.uploadFriendDiscoveries(discoveryMap, completionHandler: { (error) in
    guard error == nil else {   // Error.
        return
    }
})

// Ignoring particular users when Sendbird server discovers the matched users  
var discoveryKeys:[String] = []
discoveryKeys.append("0001-0002-0003")
discoveryKeys.append("0004-0005-0006")
discoveryKeys.append("0007-0008-0009")

SBDMain.deleteFriends(withDiscoveries: discoveryKeys, completionHandler: { (error) in
    guard error == nil else {   // Error.
        return
    }
})

Through the didDiscoverFriends: in the user event delegate, the current user will receive a callback from the server which contains a list of users whose keys match with the ones uploaded. Based on the list, the user can add them as friends through the addFriendsWithUserIds:userIds:completionHandler: method:

Objective-C
Swift
Light Color Skin
Copy
// ViewController.m
@interface ViewController : UIViewController<SBDConnectionDelegate, SBDUserEventDelegate, SBDChannelDelegate>

@end

@implementation ViewController

- (void)initViewController {
    ...
    
    [SBDMain addUserEventDelegate:self identifier:UNIQUE_DELEGATE_ID];
    ...

}

- (void)didDiscoverFriends:(NSArray<SBDUser *> * _Nullable)friends {
    if (friend != nil) {
        NSMutableArray<NSString *> *userIds = [[NSMutableArray alloc] init];
        // You can show users who match the uploaded unique keys on a list view.
        // Then the current user can decide to add them as friends or ignore them.
        for (SBDUser *user in friends) {
            [userIds addObject:user.userId];
        }

        // Adding new friends
        [SBDMain addFriendsWithUserIds:userIds completionHandler:^(NSArray<SBDUser *> * _Nullable users, SBDError * _Nullable error) {
            if (error != nil) { // Error.
                return;
            }
        }];
    }
}

@end
Light Color Skin
Copy
// ViewController.swift
class ViewController: UIViewController, SBDConnectionDelegate, SBDUserEventDelegate, SBDChannelDelegate {
    func initViewController() {
        ...

        SBDMain.add(self as SBDUserEventDelegate, identifier: UNIQUE_DELEGATE_ID)
        ...

    }

    func didDiscoverFriends(_ friends: [SBDUser]) {
        if friend != nil {
            var userIds:[String] = []
            // You can show users who match the uploaded unique keys on a list view.
            // Then the current user can decide to add them as friends or ignore them.
            for user in friends [] {
                userIds.append(user.userId)
            }

            // Adding new friends
            SBDMain.addFriends(withUserIds: userIds, completionHandler: { (users, error) in
                guard error == nil else {   // Error.
                    return
                }
            })
        }
    }
}

The current user can also remove existing friends by the deleteFriendsWithUserIds:completionHandler: method:

Objective-C
Swift
Light Color Skin
Copy
// Deleting existing friends
[SBDMain deleteFriendsWithUserIds:userIds completionHandler:^(SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
// Deleting existing friends
SBDMain.deleteFriends(withUserIds: userIds, completionHandler: { (error) in
    guard error == nil else {   // Error.
        return
    }
})

Through the SBDFriendListQuery's loadNextPageWithCompletionHandler: method, a list of friends of the current user can be retrieved.

Objective-C
Swift
Light Color Skin
Copy
SBDFriendListQuery *friendListQuery = [SBDMain createFriendListQuery];
[friendListQuery loadNextPageWithCompletionHandler:^(NSArray<SBDUser *> * _Nullable users, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
let friendListQuery = SBDMain.createFriendListQuery()
friendListQuery?.loadNextPage(completionHandler: { (users, error) in
    guard error == nil else {   // Error.
        return
    }
})

Send typing indicators to other members

If the startTyping and endTyping methods are called while the current user is typing a message in a group channel, the channelDidUpdateTypingStatus: in the channel delegate will be invoked on all channel members' devices except the one that belongs to the current user.

Objective-C
Swift
Light Color Skin
Copy
[groupChannel startTyping];
[groupChannel endTyping];
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateTypingStatus:' with things to do when notified.
@interface GroupChannelChattingViewController : UIViewController<SBDChannelDelegate>

@end

@implementation GroupChannelChattingViewController

- (void)initGroupChannelChattingViewController {
    [SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];
}

- (void)channelDidUpdateTypingStatus:(SBDGroupChannel * _Nonnull)sender {
    if ([sender.channelUrl isEqualToString:groupChannel.channelUrl]) {
        NSArray<SBDUser *> *members = [sender getTypingMembers];
        
        // Refresh typing status of members within the channel.
    }
}

@end
Light Color Skin
Copy
self.groupChannel.startTyping()
self.groupChannel.endTyping()
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateTypingStatus' with things to do when notified.
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {
    SBDMain.add(self as SBDChannelDelegate, identifier: UNIQUE_DELEGATE_ID)

    func channelDidUpdateTypingStatus(_ sender: SBDGroupChannel) {
        if sender.channelUrl == self.groupChannel.channelUrl {
            let members = sender.getTypingMembers()
        
            // Refresh typing status of members within the channel.
        }
    }
}

Mark messages as delivered

To mark messages as delivered when a group channel member successfully receives a push notification for the message from APNs, the markAsDeliveredWithRemoteNotificationPayload:completionHandler: method should be implemented in Notification Service Extension.

Objective-C
Swift
Light Color Skin
Copy
#import <SendBirdSDK/SendBirdSDK.h>
...

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    ...

    // Modify the notification content here
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    ...

    [SBDMain setAppGroup:@"APP_GROUP"];
    [SBDMain markAsDeliveredWithRemoteNotificationPayload:self.bestAttemptContentuserInfo completionHandler:^(SBDError * _Nullable error) {
        if (error != nil) { // Error.
            return;
        }
    }];
    ...

    self.contentHandler(self.bestAttemptContent);
}
Light Color Skin
Copy
import SendBirdSDK

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
            ...

            SBDMain.setAppGroup("APP_GROUP")
            SBDMain.markAsDelivered(withRemoteNotificationPayload: bestAttemptContent.userInfo) { (error) in
                if error != nil {   // Error.
                    return
                }
            }
            ...

            contentHandler(bestAttemptContent)
        }
    }
    ...
}

When a message is delivered to an online group channel member, it is automatically marked as delivered and the other online members are notified of delivery receipt through the channelDidUpdateDeliveryReceipt: method of SBDChannelDelegate. However, when it is delivered to an offline member as a push notification, the message can be marked as delivered through the markAsDeliveredWithRemoteNotificiationPayload method of SBDMain, and other online members are notified of the delivery receipt through the channelDidUpdateDeliveryReceipt: method.

Objective-C
Swift
Light Color Skin
Copy
@interface GroupChannelChattingViewController : UIViewController<SBDChannelDelegate>

@end

@implementation GroupChannelChattingViewController

- (void)initGroupChannelChattingViewController {
    [SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];
}

- (void)channelDidUpdateDeliveryReceipt:(SBDGroupChannel * _Nonnull)sender {
    ...

}

@end
Light Color Skin
Copy
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {
    SBDMain.add(self as SBDChannelDelegate, identifier: UNIQUE_DELEGATE_ID)
    
    func channelDidUpdateDeliveryReceipt(_ sender: SBDGroupChannel) {
        ...

    }
}

Mark messages as read

To keep the most up-to-date and accurate read status of messages for all group channel members, the markAsRead() method should be called every time one of the members reads messages by entering the channel from a channel list or bringing the opened channel view to the foreground.

If the current user opens a channel and the markAsReadAll() is called, Sendbird server will update both the unread message count of the individual channel and the total unread message count of all the group channels joined by the user. Then the server triggers the channelDidUpdateReadReceipt: method of the channel event delegate to notify the change of read status to all channel members' devices, except the one that is being used by the current user.

Note : When a channel member sends a message to the channel, Sendbird server updates the member's read receipt to the time when the message has sent. Meanwhile, the read receipts of other channel members can be updated when the markAsRead() method is called. If a new member joins the channel, the method works differently based on the value of the display_past_message property of your Sendbird application. If the property is set to YES, the new member’s read receipt will be updated to the sent time of the last message in the channel. If NO, it will be updated to 0.

Objective-C
Swift
Light Color Skin
Copy
// Call the 'markAsRead' when the current user views unread messages in a group channel.
[groupChannel markAsRead];
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateReadReceipt:' with things to do when notified.
@interface GroupChannelChattingViewController : UIViewController<SBDChannelDelegate>

@end

@implementation GroupChannelChattingViewController

- (void)initGroupChannelChattingViewController {
    [SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];
}

- (void)channelDidUpdateReadReceipt:(SBDGroupChannel * _Nonnull)sender {
    if ([currentGroupChannel.channelUrl isEqualToString:sender.channelUrl]) {
        // For example, code for redrawing a channel view.
    }
}

@end
Light Color Skin
Copy
// Call the 'markAsRead' when the current user views unread messages in a group channel.
groupChannel?.markAsRead()
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateReadReceipt' with things to do when notified.
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {
    SBDMain.add(self as SBDChannelDelegate, identifier: UNIQUE_DELEGATE_ID)

    func channelDidUpdateReadReceipt(_ sender: SBDGroupChannel) {
        if currentGroupChannel?.channelUrl == sender.channelUrl {
            // For example, code for redrawing a channel view.
        }
    }
}

Note: The display_past_message property determines whether to display past messages to newly joined members when they enter the channel. This property is also linked to the Chat history option, which can be adjusted in your dashboard under Settings > Chat > Messages.


Retrieve number of members who haven’t received a message

You can retrieve the number of members who haven’t received a specific message in a group channel. If zero is returned, it means that the message has been delivered to all the other members.

Objective-C
Swift
Light Color Skin
Copy
int count = [channel getUndeliveredMemberCount:message];
Light Color Skin
Copy
let count = channel?.getUndeliveredMemberCount(message)

Retrieve members who have read a message

Using the getReadMembersWithMessage: method, you can view members who have read a specific message in a group channel. The method returns a list of channel members who have read the message by comparing the message’s creation time and the channel members’ read receipt. The list will exclude the current user and the message sender.

Note: Read receipt indicates the timestamp of the latest time when each user has read messages in the channel, in Unix milliseconds.

If you want to keep track of who has read a new message, we recommend to use the getReadMembersWithMessage: in the channelDidUpdateReadReceipt: of the channel event delegate. Then the client app will receive a callback from Sendbird server whenever a channel member reads a message. To do so, you should pass the new message object as an argument to a parameter in the getReadMembersWithMessage: through the channelDidUpdateReadReceipt.

Objective-C
Swift
Light Color Skin
Copy
NSArray *readMembers = [groupChannel getReadMembersWithMessage:baseMessage];
Light Color Skin
Copy
let readMembers = groupChannel.getReadMembers(with: baseMessage)

Note: Using the getUnreadMembersWithMessage: method, you can also view members who have not read a specific message in a group channel, except the current user and the message sender. In the meantime, you can get information on each channel member's read receipt through the getReadStatus method.


Retrieve number of members who have't read a message

Using the getReadReciptOfMessage: method, you can get the number of members who have not read a specific message in a group channel. To get the exact value, the channel should be updated first through the markAsRead before calling the getReadReceiptOfMessage:.

Objective-C
Swift
Light Color Skin
Copy
// Call the 'markAsRead' when the current user views unread messages in a group channel.
[groupChannel markAsRead];
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateReadReceipt:' with things to do when notified.
@interface GroupChannelChattingViewController : UIViewController<SBDChannelDelegate>

@end

@implementation GroupChannelChattingViewController

- (void)initGroupChannelChattingViewController {
    [SBDMain addChannelDelegate:self identifier:UNIQUE_DELEGATE_ID];
}

- (void)channelDidUpdateReadReceipt:(SBDGroupChannel * _Nonnull)sender {
    if ([currentGroupChannel.channelUrl isEqualToString:sender.channelUrl]) {
        ...

        for (SBDBaseMessage *msg in messages) {
            int unreadCount = [sender getReadReceiptOfMessage:msg];
            if (unreadCount <= 0) {
                // All members have read the message.
            }
            else {
                // Some of members haven't read the message yet.
            }
        }   
    }
}

@end
Light Color Skin
Copy
// Call the 'markAsRead' when the current user views unread messages in a group channel.
groupChannel?.markAsRead()
...

// To listen to an update from all the other channel members' client apps, implement the 'channelDidUpdateReadReceipt' with things to do when notified.
class GroupChannelChattingViewController: UIViewController, SBDChannelDelegate {
    SBDMain.add(self as SBDChannelDelegate, identifier: UNIQUE_DELEGATE_ID)

    func channelDidUpdateReadReceipt(_ sender: SBDGroupChannel) {
        if currentGroupChannel?.channelUrl == sender.channelUrl {
            ...

            for msg in messages {
                let unreadCount = sender.getReadReceipt(of: msg)
                if unreadCount <= 0 {
                    // All members have read the message.
                }
                else {
                    // Some of members haven't read the message.
                }
            }
        }
    }
}

Retrieve the last message of a channel

You can retrieve the last message of a group channel.

Objective-C
Swift
Light Color Skin
Copy
SBDBaseMessage *lastMessage = groupChannel.lastMessage;
Light Color Skin
Copy
let lastMessage = groupChannel.lastMessage

Retrieve number of unread messages in a channel

You can retrieve the total number of the current user's unread messages in a group channel.

Objective-C
Swift
Light Color Skin
Copy
NSUInteger unreadMessageCount = groupChannel.unreadMessageCount;
Light Color Skin
Copy
let unreadMessageCount = groupChannel.unreadMessageCount

Retrieve number of unread messages in all channels

You can retrieve the total number of the current user's unread messages in all joined group channels.

Objective-C
Swift
Light Color Skin
Copy
[SBDMain getTotalUnreadMessageCountWithCompletionHandler:^(NSUInteger unreadCount, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
SBDMain.getTotalUnreadMessageCount(completionHandler: { (unreadCount, error) in
    guard error == nil else {   // Error.
        return
    }
})

Retrieve number of channels with unread messages

You can retrieve the total number of the current user's joined group channels which one or more unread messages.

Objective-C
Swift
Light Color Skin
Copy
[SBDMain getTotalUnreadChannelCountWithCompletionHandler:^(NSUInteger unreadCount, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
SBDMain.getTotalUnreadChannelCount(completionHandler: { (unreadCount, error) in
    guard error == nil else {   // Error.
        return
    }
})

Send an admin message

If you are using the Custom Plan, you can send admin messages to a group channel using Sendbird Dashboard or Chat Platform API. To send an admin message through your dashboard, go to the Chat > Group channels, select a group channel, find the message box below, click the Admin message tab, and then write your message in the box. An admin message is limited to 1,000 characters.

Unlike other types of messages, a push notification for an admin message is not available by default. If you want assistance on this, contact our sales team.

Note: For clients using the Free Plan, a suggesting message to upgrade to the Custom Plan will be returned when attempting to send an admin message using the Chat API.


Add extra data to a message

You have the option to create further actions in a channel by using extra data in a message. You can add one or more key-values items to a message which you can save, update, or remove, when necessary. Based on those items, you can design and implement several different actions such as measuring user engagement within a chosen time limit through polls or counting how many times a message has been copied by members.

Note: For the quality of performance, every Sendbird application has its own limits to how many key-values items you can add to a single message, as well as the maximum number of values an item can have. If you would like more information on these limits, contact our sales team.

Objective-C
Swift
Light Color Skin
Copy
// When a message has been successfully sent to a channel, create items with keys.
[groupChannel createMessageMetaArrayKeysWithMessage:MESSAGE keys:@[@"referees", @"games"] completionHandler:^(SBDBaseMessage * _Nullable message, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];

// Add values to specific items by their keys.
NSDictionary *valuesToAdd = @{
    @"referees": @[@"John", @"Brandon", @"Harry", @"Jay"],
    @"games": @[@"soccer", @"baseball", @"basketball"],
};

[groupChannel addMessageMetaArrayValuesWithMessage:MESSAGE keyValues:valuesToAdd completionHandler:^(SBDBaseMessage * _Nullable message, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];

// Remove existing values of specific items by their keys.
NSDictionary *valuesToRemove = @{
    @"referees": @[@"Brandon", @"Jay"],
};

[groupChannel removeMessageMetaArrayValuesWithMessage:MESSAGE keyValues:valuesToRemove completionHandler:^(SBDBaseMessage * _Nullable message, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];

// Delete items by their keys.
[groupChannel deleteMessageMetaArrayKeysWithMessage:MESSAGE keys:@[@"referees", @"games"] completionHandler:^(SBDBaseMessage * _Nullable message, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
// When a message has been successfully sent to a channel, creating items with keys.
groupChannel.createMessageMetaArrayKeys(with: MESSAGE, keys: ["referees", "games"], completionHandler: { (message, error) in
    guard error == nil else {   // Error.
        return
    }
})

// Adding values to specific items by their keys.
let valuesToAdd = [
    "referees": ["John", "Brandon", "Harry", "Jay"],
    "games": ["soccer", "baseball", "basketball"]
]

groupChannel.addMessageMetaArrayValues(with: MESSAGE, keyValues: valuesToAdd, completionHandler: { (message, error) in
    guard error == nil else {   // Error.
        return
    }
})

// Removing existing values of specific items by their keys.
let valuesToRemove = [
    "referees": ["Brandon", "Jay"]
]

groupChannel.removeMessageMetaArrayValues(with: MESSAGE, keyValues: valuesToRemove, completionHandler: { (message, error) in
    guard error == nil else {   // Error.
        return
    }
})

// Deleting items by their keys.
groupChannel.deleteMessageMetaArrayKeys(with: MESSAGE, keys: ["referees", "games"], completionHandler: { (message, error) in
    guard error == nil else {   // Error.
        return
    }
})

To get the key-values items of a message, read the message.metaArrays.


Display Open Graph tags in a message

The Chat SDK supports the URL link preview when a message text contains the URL of a web page.

Note: Starting in 2021, this feature will be turned on by default. For the time being, contact our support team to enable the feature.

OGMetaData for iOS

SBDOGMetaData

If a SBDBaseMessage object includes a valid URL of a website, the object can contain SBDOGMetaData, a class that holds the OG metadata such as title, URL, description, and default image of an OG object.

Note: Some websites don’t provide the OG metadata. In that case, even though the Open Graph protocol states them as requirements, all of the four properties can be null.

PropertyDescription

title

The title of the OG object as it should appear within the graph. The value can be null.

url

The canonical URL of the object that can be used as its permanent ID in the graph. The value can be null.

desc

The description of the object. The value can be null.

defaultImage

An SBDOGImage instance that contains information about the image that this Open Graph points to. The SBDOGImage also holds its own properties such as type, URL, and size. However, the value can be null.

SBDOGImage

A SBDOGImage, the image contained in a SBDOGMetaData object, is a class that holds image-related data for the SBDOGImage object. The SBDOGImage class can also have its own optional structured properties of URL, secure URL, type, width, height, and alt.

Note: Except for width and height, other fields such as URL, secure URL, type, and alt can be null. If the target website doesn’t provide width and height data, the value of those two fields are set to 0.

PropertyDescription

url

The URL of an image object within the Open Graph. The value can be null.

secureUrl

An alternative URL to use if the webpage requires HTTPS. The value can be null.

type()

A media type or MIME type of this image. The value can be null.

width()

The number of pixels horizontal. When the value is unavailable, this method returns 0.

height()

The number of pixels vertical. When the value is unavailable, this method returns 0.

alt()

The description of what is in the image, not a caption of the image. The alt attribute is designed to provide a fuller context of the SBDOGImage object and help users better understand it when they can’t load or see the image. The value can be null.

How it works

If a user sends a message with a web page URL and the linked web page possesses Open Graph (OG) tags, or OG metadata, Sendbird server parses the message content, extracts the URL in the message, gets the OG metadata from it, and creates an OG metadata object for the message. Then message recipients will get the parsed message with its OG metadata through the didReceiveMessage: method in the channel event delegate of the SDK. On the other hand, the message sender will do the same through the didUpdateMessage:.

Displaying an OG metadata object is available for two subtypes of a SBDBaseMessage: SBDUserMessage and SBDAdminMessage. If the content of either a SBDUserMessage or SBDAdminMessage object includes a web page URL containing OG metadata, the didReceiveMessage: method returns SBDOGMetaData and SBDOGImage objects.

If Sendbird server doesn’t have cache memory of the OG metadata of the given URL, the SBDBaseMessage.ogMetaData can be null due to the time it takes to fetch the OG metadata from a remote web page. In the meantime, the message text containing the URL will be delivered first to message recipients’ client app through the didReceiveMessage: method. When the server completes fetching, the didUpdateMessage: method will be called and the message with its SBDOGMetaData object will be delivered to the recipients’ client app. However, if Sendbird server has already cached the OG metadata of the URL, the SBDBaseMessage.ogMetaData returns the message and its SBDOGMetaData object instantly and the didUpdateMessage: method won’t be called.

Objective-C
Swift
Light Color Skin
Copy
// Send a user message containing the URL of a web page.
SBDUserMessageParams *params = [[SBDUserMessageParams alloc] init];
[params setMessage:"sendbird.com"];
...

[groupChannel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
    ...

}];
Light Color Skin
Copy
// Send a user message containing the URL of a web page.
var params = SBDUserMessageParams()
params.message = "sendbird.com"
...

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

})
Objective-C
Swift
Light Color Skin
Copy
// ViewController.m
// Receive a user message containing OG metadata of the web page through a channel event delegate.
- (void)channel:(SBDBaseChannel * _Nonnull)sender didReceiveMessage:(SBDBaseMessage * _Nonnull)message {
    if (message.ogMetaData != nil) {
        // TODO: Your custom implementation for the Open Graph metadata of the message.
        NSString *url = message.ogMetaData.url;
        NSInteger *width = message.ogMetaData.width;
        ...

    } else {
        // If the `message.ogMetaData` is `nil`, wait until the `channel:didiUpdateMessage:` method receives a callback from Sendbird server.
    }
    ...
}

- (void)channel:(SBDBaseChannel * _Nonnull)sender didUpdateMessage:(SBDBaseMessage * _Nonnull)message {
    if (message.ogMetaData != nil) {
        // TODO: Your custom implementation for the Open Graph metadata of the message.
        NSString *url = message.ogMetaData.url;
        NSInteger *width = message.ogMetaData.width;
        ...
    
    }
    ...
}
Light Color Skin
Copy
// ViewController.swift
// Receive a user message containing OG metadata of the web page through a channel event delegate.
func channel(_ sender: SBDBaseChannel, didReceive message: SBDBaseMessage) {
    if message.ogMetaData != nil {
        // TODO: Your custom implementation for the Open Graph metadata of the message.
        let url = message.ogMetaData.url
        let width = message.ogMetaData.width
        ...

    } else {
        // If the `message.ogMetaData` is `nil`, wait until the `channel:didiUpdate` method receives a callback from Sendbird server.
    }
    ...

}
    
func channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) {
    if message.ogMetaData != nil {
        // TODO: Your custom implementation for the Open Graph metadata of the message.
        let url = message.ogMetaData.url
        let width = message.ogMetaData.width
        ...
    }
    ...
}

Categorize channels by custom type

When creating a group channel, you can additionally specify a custom channel type for further channel grouping. This custom type takes on the form of a NSString, and can be handy in searching or filtering channels.

Note: The DATA and CUSTOM_TYPE are both NSString fields that allow you to append additional information to your channels. As mentioned above, a common use case for the CUSTOM_TYPE is to contain information for channel grouping (for example, categorizing School and Work channels). However, both these fields can be flexibly utilized.

Objective-C
Swift
Light Color Skin
Copy
NSMutableArray<NSString *> *users = [[NSMutableArray alloc] init];
[users addObject:@"Jed"];
[users addObject:@"Young"];

SBDGroupChannelParams *params = [[SBDGroupChannelParams alloc] init];
[params addUserIds:users];
[params setName:@"NAME"];
[params setCustomType:@"CUSTOM_TYPE"];
...

[SBDGroupChannel createChannelWithParams:params completionHandler:^(SBDGroupChannel * _Nullable groupChannel, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
var users: [String] = []
users.append("Jed")
users.append("Young")

var params = SBDGroupChannelParams()
params.addUserIds(users)
params.name = "NAME"
params.customType = "CUSTOM_TYPE"
...

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

To get a channel's custom type, read the groupChannel.customType.


Categorize messages by custom type

When sending a message, you can additionally specify a custom message type for further message grouping. This custom type takes on the form of a NSString, and can be useful in searching or filtering messages.

Note: The DATA and CUSTOM_TYPE are both NSString fields that allow you to append additional information to your messages. As mentioned above, a common use case for the CUSTOM_TYPE is to contain information for message grouping (for example, categorizing Notes and Contacts messages). However, both these fields can be flexibly utilized.

To embed a custom type into a message, set a value of the type to a SBDUserMessageParams or SBDFileMessageParams object, and pass the object as an argument to a parameter in the sendUserMessageWithParams:completionHandler: or sendFileMessageWithParams:completionHandler: method.

Objective-C
Swift
Light Color Skin
Copy
SBDUserMessageParams *params = [[SBDUserMessageParams alloc] init];
[params setMessage:MESSAGE];
[params setCustomType:CUSTOM_TYPE];
...

[groupChannel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
var params = SBDUserMessageParams()
params.message = MESSAGE
params.customType = CUSTOM_TYPE
...

groupChannel.sendUserMessage(with: params, completionHandler: { (userMessage, error) i
    guard error == nil else {   // Error.
        return
    }
})

To get a message's custom type, read the message.customType.


Search channels by name, URL, or other filters

You can search for specific group channels by adding keywords to a SBDGroupChannelListQuery instance. There are two types of keywords: a Name and a URL. The code sample below shows the query instance which returns a list of the current user's group channels that partially match the specified Name keyword in the names of the channels.

Objective-C
Swift
Light Color Skin
Copy
SBDGroupChannelListQuery *myChannelListQuery = [SBDGroupChannel createMyGroupChannelListQuery];
myChannelListQuery.includeEmptyChannel = YES;
[myChannelListQuery setSearchFilter:@"Sendbird" fields: SBDGroupChannelListQuerySearchFieldChannelName];
[myChannelListQuery loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable groupChannels, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }

    // A list of group channels that partially match "Sendbird" in their names is returned.
}];
Light Color Skin
Copy
let myChannelListQuery = SBDGroupChannel.createMyGroupChannelListQuery()
myChannelListQuery.includeEmptyChannel = true
myChannelListQuery.setSearchFilter(with: "Sendbird", fields: .channelName)
myChannelListQuery.loadNextPage(completionHandler: { (groupChannels, error) in
    guard error == nil else {   // Error.
        return
    }

    // A list of group channels that partially match "Sendbird" in their names is returned.
})

The following shows the query instance which returns a list of the current user's group channels that partially match the specified URL keyword in the URLs of the channels.

Objective-C
Swift
Light Color Skin
Copy
SBDGroupChannelListQuery *myChannelListQuery = [SBDGroupChannel createMyGroupChannelListQuery];
myChannelListQuery.includeEmptyChannel = YES;
myChannelListQuery channelUrlsFilter = @[@"seminar", @"lecture"];

[myChannelListQuery loadNextPageWithCompletionHandler:^(NSArray<SBDGroupChannel *> * _Nullable groupChannels, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
    
    // A list of group channels that partially match "seminar" or "lecture" in their names is returned.
}];
Light Color Skin
Copy
let myChannelListQuery = SBDGroupChannel.createMyGroupChannelListQuery()
myChannelListQuery.includeEmptyChannel = true

var channelUrls: [String] = []
channelUrls.append("seminar")
channelUrls.append("lecture")

myChannelListQuery.channelUrlsFilter = channelUrls

myChannelListQuery.loadNextPage(completionHandler: { (groupChannels, error) in
    guard error == nil else {   // Error.
        return
    }
    
    // A list of group channels that partially match "seminar" or "lecture" in their names is returned.
})

The following table shows all the supported filters for SBDGroupChannelListQuery to search for group channels you want to retrieve. You can use any filters in a similar fashion with the sample code above.

NameFilters...

customTypesFilter

Group channels with the specified one or more custom types.

customTypeStartsWithFilter

Group channels with the custom type which starts with the specified value.

channelNameContainsFilter

Group channels that contain the specified value in their names.

channelUrlsFilter

Group channels with the specified one or more channel URLs.

superChannelFilter

Either super or nonsuper group channels.

publicChannelFilter

Either public or private group channels.

unreadChannelFilter

Group channels with one or more unread messages.

channelHiddenStateFilter

Group channels with the specified state and operating behavior.

memberStateFilter

Group channels based on whether or not the user has accepted an invitation, or whether or not the user was invited by a friend.

userIdsExactFilter

Group channels where the members with the specified one or more user IDs are exactly in.

userIdsIncludeFilter

Group channels that include one or more members with the specified user IDs.

nicknameContainsFilter

Group channels with members whos nicknames contain the specified value.

metaDataOrderKeyFilter

Group channels with metadata containing an item with the specified value as its key. This filter is effective only when the metadata are sorted in the alphabetical order.


Generate thumbnails of a file message

When sending an image file, you can determine whether to create thumbnails of the image, which you can fetch and render into your UI. You can specify up to 3 different dimensions to generate thumbnail images in, which can be useful for supporting various display densities.

Note: Supported file types are files whose media type is image/* or video/*. The Chat SDK does not support creating thumbnails when sending a file message with a file URL.

The sendFileMessageWithParams:completionHandler: method requires passing a SBDFileMessageParams object as an argument to a parameter, containing a NSArray of SBDThumbnailSize objects which each specify the maximum values of width and height of a thumbnail image with its makeWithMaxCGSize: or makeWithMaxWidth:maxHeight: constructors. The completionHandler: callback subsequently returns a NSArray of SBDThumbnail objects that each contain the URL of the generated thumbnail image file.

Objective-C
Swift
Light Color Skin
Copy
NSMutableArray<SBDThumbnailSize *> *thumbnailSizes = [[NSMutableArray alloc] init];

// Creating and adding a SBDThumbnailSize object (allowed number of thumbnail images: 3).
[thumbnailSizes addObject:[SBDThumbnailSize makeWithMaxCGSize:CGSizeMake(100.0, 100.0)]];
[thumbnailSizes addObject:[SBDThumbnailSize makeWithMaxWidth:200.0 maxHeight:200.0]];

SBDFileMessageParams *params = [[SBDFileMessageParams alloc] init];
...
[params setFile:FILE]; 
[params setFileName:FILE_NAME];
[params setFileSize:FILE_SIZE];
[params setMimeType:MIME_TYPE];
[params setThumbnailSizes:thumbnailSizes];  // Set a SBDThumbnailSize object to a SBDFileMessageParams object.

[groupChannel sendFileMessageWithParams:params completionHandler:^(SBDFileMessage * _Nonnull fileMessage, SBDError * _Nullable error) {
    if (error != nil) {     // Error.
        return;
    }

    SBDThumbnail *first = fileMessage.thumbnails[0];
    SBDThumbnail *second = fileMessage.thumbnails[1];

    CGSize maxSizeFirst = first.maxSize;    // 100
    CGSize maxSizeSecond = second.maxSize;  // 200

    NSString *urlFirst = first.url;
    NSString *urlSecond = second.url;
}];
Light Color Skin
Copy
var thumbnailSizes = [SBDThumbnailSize]()

// Creating and adding a SBDThumbnailSize object (allowed number of thumbnail images: 3).
thumbnailSizes.append(SBDThumbnailSize.make(withMaxCGSize: CGSize(width: 100.0, height: 100.0))!)
thumbnailSizes.append(SBDThumbnailSize.make(withMaxWidth: 200.0, maxHeight: 200.0)!)

var params = SBDFileMessageParams()
...
params.file = FILE 
params.fileName = FILE_NAME
params.fileSize = FILE_SIZE
params.mimeType = MIME_TYPE
params.thumbnailSizes = thumbnailSizes  // Set a SBDThumbnailSize object to a SBDFileMessageParams object.

groupChannel.sendFileMessage(with: params, completionHandler: { (fileMessage, error) in
    guard error == nil else {       // Error.
        return
    }

    let first = fileMessage?.thumbnails?[0]
    let second = fileMessage?.thumbnails?[1]

    let maxSizeFirst = first?.maxSize       // 100
    let maxSizeSecond = second?.maxSize     // 200

    let urlFirst = first?.url
    let urlSecond = second?.url
}

A thumbnail image is generated evenly to fit within the bounds of (maxWidth, maxHeight), which are provided through the constructor. Note that if the original image is smaller than the specified dimensions, the thumbnail isn't resized. The url returns the location of the generated thumbnail file within Sendbird server.

This is one of Sendbird's premium features. Contact our sales team for further assistance.


Track file upload progress using a handler

If needed, you can track the file upload progress by passing the progressHandler: code block as an argument to a parameter when calling the sendFileMessageWithParams:progressHandler:completionHandler: method.

Objective-C
Swift
Light Color Skin
Copy
SBDFileMessageParams *params = [[SBDFileMessageParams alloc] init];
...
[params setFileName:FILE_NAME];
[params setData:FILE_DATA]; 
[params setCustomType:CUSTOM_TYPE];

SBDFileMessage *fileMessage = [groupChannel sendFileMessageWithParams:params progressHandler:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
    CGFloat progress = totalBytesSent / totalBytesExpectedToSend;

} completionHandler:^(SBDFileMessage * _Nonnull fileMessage, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        // Do something when an error happens.
        return;
    }

    // Do something with the sent file message.
}];
Light Color Skin
Copy
let fileMessage = channel.sendFileMessage(
    with: params,
    progressHandler: { bytesSent, totalBytesSent, totalBytesExpectedToSend in
        let progress = CGFloat(totalBytesSent) / CGFloat(totalBytesExpectedToSend)
    },
    completionHandler: { message, error in
        guard error == nil else { 
            // Do something when an error happens.
            return
        }

        // Do something with the sent file message.
    }
)

Cancel an in-progress file upload

Using the cancelUploadingFileMessageWithRequestId:completionHandler: method, you can cancel an in-progress file upload while it's not completed yet. If the function operates successfully, true is returned to the completionHandler:.

Note: If you attempt to cancel the uploading after it is already completed, or canceled, or returned an error, the function returns NO.

Objective-C
Swift
Light Color Skin
Copy
SBDFileMessageParams *params = [[SBDFileMessageParams alloc] init];
...
[params setFileName:FILE_NAME];
[params setData:FILE_DATA]; 
[params setCustomType:CUSTOM_TYPE];

SBDFileMessage *fileMessage = [groupChannel sendFileMessageWithParams:params progressHandler:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
    ...
} completionHandler:^(SBDFileMessage * _Nonnull fileMessage, SBDError * _Nullable error) {
    ...
}];

// Cancel uploading a file in the file message.
[SBDGroupChannel cancelUploadingFileMessageWithRequestId:fileMessage.requestId completionHandler:^(BOOL result, SBDError * _Nullable error) {
    ...
}];
Light Color Skin
Copy
let fileMessage = channel.sendFileMessage(
    with: params,
    progressHandler: { bytesSent, totalBytesSent, totalBytesExpectedToSend in
        ...
    },
    completionHandler: { message, error in
        ...
    }
)

// Cancel uploading a file in the file message.
SBDGroupChannel.cancelUploadingFileMessage(withRequestId: fileMessage.requestId(), completionHandler: { (result, error) in
    ...
})

Share an encrypted file with other members

This file encryption feature prevents users without access from opening and reading encrypted files that have been shared within a group of users. When this feature is turned on, all types of sent files and thumbnail images will be first uploaded to Sendbird server, and then encrypted by AES256.

In a group channel, encrypted files and thumbnail images will be decrypted and accessed securely only by the members. Anyone outside of the channel and application will not have access to those files and thumbnail images. The following explains how this data security works and what to do at the SDK level to apply it to your client apps.

The Sendbird system enables secure encryption and decryption of files by generating and distributing an opaque and unique encryption key for each user. An encryption key is managed internally by the system, and is valid for 3 days. It is generated every time the user logs in to Sendbird server through the Chat SDK, which then gets delivered to the Chat SDK from the server.

When the Chat SDK requests an encrypted file by its URL, the parameter auth should be added to the URL to access the file, which is specified with an encryption key of the user such as ?auth=RW5jb2RlIHaXMgdGV4eA==. With the specified key in the auth, Sendbird server first decrypts the file, then checks if the user is a member of the group channel, and finally, allows the user to access and open the file in the channel.

This can be easily done by retrieving the fileMessage.url property, which returns the unique file URL containing the parameter auth with an encryption key of the current user.


Spam flood protection

This feature allows you to customize the number of messages a member can send in a group channel per second. By doing so, all excess messages will be deleted and only the number of messages allowed to be sent per member per second will be delivered. This feature protects your app from some members spamming others in the channel with the same messages.

Note: Our default system setting is 5 messages per second. This limit can be manually adjusted only from our side. This is one of Sendbird's premium features. Contact our sales team for further assistance.


Message auto-translation

It is possible for text messages to be sent in different languages through the Sendbird's auto-translation feature. When sending a text message, set a NSArray of language codes to a SBDUserMessageParams object and then pass the object as an argument to parameter in the sendUserMessageWithParams:completionHandler: method to request translated messages in the corresponding languages.

Note: This message auto-translation feature is powered by Google Cloud Translation API recognition engine and Microsoft Translator engine. It is by default that Google's Cloud Translation API recognition engine is used for message auto-translation, whereas if your Sendbird application has been created before June 26, 2019, Microsoft Translator engine is used.

Objective-C
Swift
Light Color Skin
Copy
SBDUserMessageParams *params = [[SBDUserMessageParams alloc] init];
...
[params setTargetLanguages:@[@"es", @"ko"]];    // Spanish and Korean

[groupChannel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nonnull userMessage, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
}];
Light Color Skin
Copy
var params = SBDUserMessageParams()
...
params.targetLanguages = ["es", "ko"]   // Spanish and Korean

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

You can retrieve translations of a text message using the userMessage.translations property which has a NSArray object containing the language codes and translations.

Note: The two translation engines support a wide variety of languages, and you can see their language code tables in the Miscellaneous > Language support section.

Objective-C
Swift
Light Color Skin
Copy
- (void)channel:(SBDBaseChannel * _Nonnull)sender didReceiveMessage:(SBDBaseMessage * _Nonnull)message {
    NSArray *translations = ((SBDUserMessage *)message).translations;
    NSString *esTranslatedMessage = translations[@"es"];    // Spanish
    ...
    // Display translation in UI.
}
Light Color Skin
Copy
func channel(_ sender: SBDBaseChannel, didReceive message: SBDBaseMessage) {
    If let message = message as? SBDUserMessage {
        let esTranslatedMessage = message.translations[“es”]    // Spanish
        ...
        // Display translation in UI.
    }
}

This is one of Sendbird's premium features. Contact our sales team for further assistance.


Message on-demand translation

Using the translateUserMessage:targetLanguages:completionHandler: method, you can allow your users to translate text messages into other languages when needed.

Note: This message on-demand translation is powered by Google Cloud Translation API recognition engine, and you can see its language code table in the Miscellaneous > Language support section.

Objective-C
Swift
Light Color Skin
Copy
NSArray<NSString *> *targetLanguages = @[@"es", @"de"];     // Spanish and German

// The USER_MESSAGE below indicates a SBDUserMessage object which represents an already sent or received text message.
[groupChannel translateUserMessage:USER_MESSAGE targetLanguages:targetLanguages completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
    if (error != nil) { // Error.
        return;
    }
        
    NSArray *translations = ((SBDUserMessage *)userMessage).translations;
    NSString *esTranslatedMessage = translations[@"es"];    // Spanish
    NSString *deTranslatedMessage = translations[@"de"];    // German 
    ... 
    // TODO: Display translations in UI.
}];
Light Color Skin
Copy
let targetLanguages = ["es", "de"]

// The USER_MESSAGE below indicates a SBDUserMessage object which represents an already sent or received text message.
groupChannel.translateUserMessage(USER_MESSAGE, targetLanguages: targetLanguages, completionHandler: { (userMessage, error) in
    guard error == nil else {   // Error.
        return
    }

    let translations = (userMessage as! SBDUserMessage).translations
    let esTranslatedMessage = translations["es"] 
    let deTranslatedMessage = translations["de"]
    ... 
    // TODO: Display translations in UI.
})

Based on this method, you can implement features such as real-time or instant translation to your app. You can also implement to only translate a selected message into preferred languages.

This is one of Sendbird's premium features. Contact our sales team for further assistance.