The Agora Chat SDK supports sending and receiving various types of messages:
You can send a message to a peer user, a chat group, or a chat room. After sending a message, you can recall the message, listen for the message read receipt.
To manage the messages, for example, to delete a conversation, you can also retrieve historical messages from the local device or from the server.
This page introduces how to use the Agora Chat SDK to implement these functionalities in your app.
The Agora Chat SDK provides a Message
class that defines the message type, and a ChatManager
class that allows you to send, receive, recall, and retrieve messages.
Before proceeding, ensure that you meet the following requirements:
The process of sending and receiving a message is as follows:
Followings are the core methods for sending, receiving, and recalling messages:
sendMessage
: Sends a message to the specified user, chat group, or chat room.recallMessage
: Recall a message that has been sent.addMessageListener
: Adds a message event listener.Refer to the following code sample to create, send, and receive a text message:
// Call initWithText to create a text message. Set content as the text content and toChatUsername to the username to whom you want to send this text message.
TextMessageBody *textMessageBody = [[TextMessageBody alloc] initWithText:content];
Message *message = [[Message alloc] initWithConversationID:toChatUsername from:fromChatUsername to:toChatUsername body:textMessageBody ext:messageExt];
// Set the chat type as Group chat. You can also set is as chat (one-to-one chat) or chat room.
message.chatType = AgoraChatTypeGroupChat;
// Call sendMessage to send the text message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message progress:nil completion:nil];
// Set the completion callback to get the state of sending message.
// You can update the message state in this callback, for example, popping a tip if the message sending failes.
[[AgoraChatClient sharedClient].chatManager sendMessage:message progress:nil completion:^(Message *message, AgoraError *error) {
if (!error) {
// If the message is successfully sent
} else {
// If the message fails to be sent
}
}];
// Call addDelegate to add a message listener.
[[AgoraChatClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
// The SDK triggers the messageDidReceive callback when it receives a message.
- (void)messagesDidReceive:(NSArray *)aMessages
{
for ( Message *message in aMessages )
{
// After receiving this callback, the SDK parses the message and displays it.
}
}
// Call removeDelegate to remove the event listener.
- (void)dealloc
{
[[AgoraChatClient sharedClient].chatManager removeDelegate:self];
}
Attachment messages include voice, image, video, and file message. When you send an attachment message, the attachment is uploaded to the Agora Chat server.
Before you create an attachment message, you need to implement the function of getting the attachment in your app, for example, to send an audio message, you need to implement the recording function.
To create an attachment message, call the corresponding create
method in the ChatMessage
class according to the type of the attachment.
// Call initWithLocalPath to create a voice message.
// Set local path as the path of the local audio file, and displayName as the name of the audio.
VoiceMessageBody *body = [[VoiceMessageBody alloc] initWithLocalPath:localPath
displayName:displayName];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
// Call initWithData to create an image message.
// Set imageData as the path of the local iamge file, and displayName as the name of the image file.
ImageMessageBody *body = [[ImageMessageBody alloc] initWithData:imageData
displayName:displayName];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
// Call initWithLocalPath to create a video message
// Set localPath as the path of the local video file, displayName as the name of the video file, and duration as the duration of the video file
VideoMessageBody *body = [[VideoMessageBody alloc] initWithLocalPath:localPath displayName:@"displayName"];
body.duration = duration;
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
// Call initWithData to create a file message
// Set fileData as the local file and displayName as the name of the file.
EMFileMessageBody *body = [[EMFileMessageBody alloc] initWithData:fileData displayName:fileName];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
To send the attachment message, call sendMessage
in the ChatMessage
class.
// Set the chat type as group chat. You can also set it as chat (one-to-one chat) or chat room.
message.chatType = AgoraChatTypeGroupChat;
// Call sendMessage to send the message.
ChatClient.getInstance().chatManager().sendMessage(message);
To validate that a message is successfully sent, or to track the upload progress of an attachment, use the progress
callback to listen for the message status:
// When sending the message, you can set the EMCallback instance to get the state of sending the message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message progress:^(int progress) {
// The progress of uploading the attachement附件上传进度百分比
} completion:^(Message * message, AgoraError * error) {
// Error-handling logics for sending the message
}];
});
Call the addMessageListener
to add a message listener. If the received message is an image, video, or file message, download the attachment before opening it.
The following code shows how to receive an image message:
// Get the image file and thumbnail when receiving the message.
ImageMessageBody *body = (ImageMessageBody *) message.body;
// Get the image file from the server.
NSString *remotePath = body.remotePath;
// Get the image thumbnail from the server.
NSString *thumbnailPath = body.thumbnailRemotePath;
// Get the path of the image file on the local device.
NSString *localPath = body.localPath;
// Get the image thumbnail from the local device.
NSString *thumbnailLocalPath = body.thumbnailLocalPath;
To send and receive a location message, you need to integrate a third-party map service provider. When sending a location message, you get the longtitude and latitude information of the location from the map service provider; when receiving the location message, you extract the received longtitude and latitude information and displays the location on the third-party map.
The following code sample shows how to send a location message:
// Set the longtitude and latitude information and description of the location.
LocationMessageBody *body = [[LocationMessageBody alloc] initWithLatitude:latitude longitude:longitude address:aAddress];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
message.chatType = AgoraChatTypeChat;
// Set the chat type as group chat. You can also set it as chat (one-to-one chat) or chat room.
message.chatType = AgoraChatTypeGroupChat;
// Sends the message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message
progress:nil
completion:nil];
CMD messages are command messages that tell the specified user to take a certain action. The receiver deals with the command message themselves.
em_
and easemob::
are internal fields. Do not use them.The following code sample shows how to send and receive a CMD message:
// Set action as the custom command content in NSString.
CmdMessageBody *body = [[CmdMessageBody alloc] initWithAction:action];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
message.chatType = AgoraChatTypeChat;
// Set the chat type as group chat. You can also set it as chat (one-to-one) chat or chat room.
message.chatType = AgoraChatTypeGroupChat;
// Send the CMD message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message
progress:nil
completion:nil];
// Occurs when the CMD message is received.
- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages
{
for ( Message *message in aCmdMessages )
{
CmdMessageBody *body = (CmdMessageBody *) message.body;
// Parse the CMD message.
}
}
Custom messages are self-defind key-value pairs that include the message type and the message content.
The following code sample shows how to send a custom message:
// Set event as the custom event, for example, as userCard to send a namecard message.
// Set ext as the extented message, such as uid, nickname, and avatar.
CustomMessageBody * body = [[CustomMessageBody alloc] initWithEvent:@"userCard" ext:@ { @"uid":aUid, @"nickname":aNickName, @"avatar":aUrl }];
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:body
ext:messageExt];
message.chatType = AgoraChatTypeChat;
// Set the chat type as group chat. You can also set it as chat (one-to-one) chat or chat room.
message.chatType = AgoraChatTypeGroupChat;
// Send the message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message
progress:nil
completion:nil];
You can also custom the message content by extending the message type. The following code sample shows how to send an extended message:
TextMessageBody *textMessageBody = [[TextMessageBody alloc] initWithText:content];
// Add an extended attribute.
NSDictionary *messageExt = @ { @"attribute" : @"value" };
Message *message = [[Message alloc] initWithConversationID:toChatUsername
from:fromChatUsername
to:toChatUsername
body:textMessageBody
ext:messageExt];
// Set the chat type as group chat. You can also set it as chat (one-to-one) chat or chat room.
message.chatType = AgoraChatTypeChat;
// Send the message.
[[AgoraChatClient sharedClient].chatManager sendMessage:message
progress:nil
completion:nil];
// Retrieve the extended attribute when the message is received.
- (void)messagesDidReceive:(NSArray *)aMessages
{
// Parse the message when receiving it.
for ( Message *message in aMessages )
{
// value represents the attribute in the extended message.
NSString *value = [message.ext objectForKey:@"attribute"];
}
}
After sending a message, you can recall it using the recallMessage
method. The default time limit for recalling a message is 2 minutes after this message is sent. To custom this time limit, contact support@agora.io.
Refer to the following code sample to recall a message:
[[AgoraChatClient sharedClient].chatManager recallMessageWithMessageId:@"messageId" completion:^(AgoraChatError *aError) {
if (!aError) {
NSLog(@"Message recell succeeds.");
} else {
NSLog(@"Message recall fails with the error desctiption — %@", aError.errorDescription);
}
}];
The Agora Chat SDK stores the sent and received messages in the local database, and you can manage these messages on conversations.
Followings are the core methods for managing the local messages:
getAllConversations
: Retrieves all the conversations on the local device.deleteConversation
: Deletes the concersation on the local device.unreadMessagesCount
: Retrieves the count of the unread messages in the specified conversation.loadMessagesStartFromId
: Searches the specified message from the local database.importMessages
: Imports the specified historial message to the database.insertMessage
: Inserts the specified historial message to the converation.Call getAllConversations
to retrieve all the conversations to the local device.
NSArray *conversations = [[AgoraChatClient sharedClient].chatManager getAllConversations];
Refer to the following code sample for retrieving all the messages of the current conversation:
// Retrieve the specified conversation.
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
// Only one message is loaded during SDK initialization. Call loadMessagesStartFromId to retrieve more messages.
NSArray<Message *> *messages = [conversation loadMessagesStartFromId:startMsgId count:count searchDirection:MessageSearchDirectionUp];
Call unreadMessageCount
to retrieve the count of unread messages in the current conversation.
// Get the current conversation
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
// Retrieve the count of unread messages in the current conversation.
NSInteger unreadCount = conversation.unreadMessagesCount;
You can also call unreadMessagesCount
to retrieve the count of all the unread messages in all the conversations.
NSArray *conversations = [[AgoraChatClient sharedClient].chatManager getAllConversations];
NSInteger unreadCount = 0;
for ( AgoraConversation *conversation in conversations )
{
unreadCount += conversation.unreadMessagesCount;
}
You can mark the specified message or all the messages in the specified conversation as read. Refer to the following code sample:
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
// Mark all the messages in the current conversation as read
[conversation markAllMessagesAsRead:nil];
// Mark the specified message in the current conversation as read.
[onversation markMessageAsReadWithId:messageId error:nil];
You can delete the specified conversation, or the specified message in the current conversation from the local device. Refer to the following code:
// Set whether to delete all the historial messages in the current conversation.
[[AgoraChatClient sharedClient].chatManager deleteConversation:conversationId isDeleteMessages:YES completion:nil];
// Delete the specified conversations.
NSArray *conversations = @ { @"conversationID1", @"conversationID2" };
[[AgoraChatClient sharedClient].chatManager deleteConversations:conversations isDeleteMessages:YES completion:nil];
// Delete the specified historial messages in the current conversation.
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
[conversation deleteMessageWithId:.messageId error:nil];
Refer to the following code to search the historial messages. You can use fields such as keyword, timestamp, and sender for the search.
// Call searchMsgFromDB to search for messages in the current conversation.
NSArray<Message *> *messages = [conversation loadMessagesWithKeyword:keyword timestamp:0 count:50 fromUser:nil searchDirection:MessageSearchDirectionDown];
You can import a historial message to the local database by constructing a ChatMessage
object:
[[AgoraChatClient sharedClient].chatManager importMessages:messages completion:nil];
Refer to the following code sample to insert a message in the current conversation:
// Insert a message in the current conversation.
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
[conversation insertMessage:message error:nil];
The Agora Chat SDK also stores historial messages on the chat server, and you can retrieve these historial messages by conversations.
Followings are the core methods for retrieving historical messages from the server
getConversationsFromServer
: Retrieves the conversation list from the server.asyncFetchHistoryMessagesFromServer
: Retrieves the messages in the specified conversation from the server.Agora recommends calling these methods when the app is first installed or before any conversation occurs on the local device.
Refer to the following code sample to retrieve the conversation list from the server. For each message call, a maximum number of 100 conversations is returned.
[[AgoraChatClient sharedClient].chatManager getConversationsFromServer:^(NSArray * aCoversations, AgoraError * aError) {
if ( !aError )
{
for ( AgoraConversation * conversation in aCoversations )
{
/* Parse theconversation.*/
}
}
}];
Refer to the following code to retrieve the historial messages from the specified conversation buy pagination. For each method call, a maximum number of 50 messages is returned.
[[AgoraChatClient sharedClient].chatManager asyncFetchHistoryMessagesFromServer:conversationId conversationType:conversationType startMessageId:messageId pageSize:pageSize completion:^(AgoraCursorResult * aResult, AgoraError * aError) {
AgoraConversation *conversation = [[AgoraChatClient sharedClient].chatManager getConversation:conversationId type:type createIfNotExist:YES];
[conversation loadMessagesStartFromId:messageId count:count searchDirection:MessageSearchDirectionUp completion:nil];
}];
After you send a message, once this message is delivered or read, the Agora Chat SDK supports sending a receipt to you, informing you that your message has been delievered or read.
Followings are the core methods for implementing message delievery and read receipt:
enableRequireReadAck
: Enables message read receipt.ackConversationRead
: Sends the receipt when the specified conversation is read.sendMessageReadAck
: Sends the receipt when the specified message is read.sendGroupMessageReadAck
: Sends the receipt when the specified group message is read.Call setRequireDeliveryAck
to enable the message delievery receipt feature. The following code sample shows how to implement message delivery receipt:
// Call enableRequireReadAck to enable message delivery receipt.
options.enableRequireReadAck = YES;
- (void)messagesDidRead:(NSArray *)aMessages
{
for ( EMMessage *message in aMessages )
{
// Handling message delievery receipts.
}
}
// Remove the delegate.
[[AgoraChatClient sharedClient].chatManager removeDelegate:self];
Once the message sender enables message read receipt by calling enableRequireReadAck
, the message receiver sends this receipt after reading the message.
Refer to the following code to implement read receipt for all the messages in the specified conversation:
The message receiver
// The message receiver calls ackConversationRead to send the converation read receipt.
// This is an asynchronous method.
[[AgoraChatClient sharedClient].chatManager ackConversationRead:conversationId completion:nil];
The message sender
// The message sender receives the onConversationRead callback.
- (void)onConversationRead:(NSString *)from to:(NSString *)to
{
// Add handling logics
}
In scenarios where a user has logged into multiple devices, once the read receipt is sent from one of these devices, the server marks the unmessage on the other devices as read as well.
Refer to the following code to implement read receipt for the specified message:
The message receiver
You can send the message read receipt when entering the conversation:
// The message receiver calls sendMessageReadAck to send the message read receipt.
[[AgoraChatClient sharedClient].chatManager sendMessageReadAck:messageId toUser:conversationId completion:nil];
}
You can also send the message read receipt when receiving a message:
// Occurs when the message is received
- (void)messagesDidReceive:(NSArray *)aMessages
{
for ( Message *message in aMessages )
{
// Send the message read receipt
[self sendReadAckForMessage:message];
}
}
// Send the message read receipt
- (void)sendReadAckForMessage:(Message *)aMessage
{
// Receive the message
if ( aMessage.direction == EMMessageDirectionSend || aMessage.isReadAcked || aMessage.chatType != EMChatTypeChat )
return;
MessageBody *body = aMessage.body;
// For voice, video, and file messages, you need to send the receipt after clicking the files.
if ( body.type == MessageBodyTypeFile || body.type == MessageBodyTypeVoice || body.type == MessageBodyTypeImage )
return;
[[AgoraChatClient sharedClient].chatManager sendMessageReadAck:aMessage.messageId toUser:aMessage.conversationId completion:nil];
}
The message sender
// Occurs when the message is read.
- (void)messagesDidRead:(NSArray *)aMessages
{
for ( Message *message in aMessages )
{
// Add handling logics.
}
}
For chat group messages, when the group owner or admin sends a messge, they can set whether to require a message read receipt.
To receive the chat message receipt, the message sender needs to set isNeedGroupAck
as YES
when sending the message.
// Set isNeedGroupAck as YES when initializing the group message
Message *message = [[Message alloc] initWithConversationID:to from:from to:to body:aBody ext:aExt];
message.isNeedGroupAck = YES;
The following code sample shows how to implement chat message receipts:
The message receiver
// Send the group message read receipt.
- (void)sendGroupMessageReadAck:(Message *)msg
{
if ( msg.isNeedGroupAck && !msg.isReadAcked )
{
[[AgoraChatClient sharedClient].chatManager sendGroupMessageReadAck:msg.messageId toGroup:msg.conversationId content:@"123" completion:^(EMError * error) {
if ( error )
{
}
}];
}
}
The message sender
// Occurs when the group message is read.
- (void)groupMessageDidRead:(Message *)aMessage groupAcks:(NSArray *)aGroupAcks
{
for ( GroupMessageAck *messageAck in aGroupAcks )
{
// Receive the group message read receipt
}
}
After receiving the groupMessageDidRead
callback, the message sender can also call asyncFetchGroupReadAcks
to know the details of the receipt.
/**
* Fetches the details of the group message read receipt.
* @param messageId The message ID.
* @param groupId The group ID.
* @param startGroupAckId The receipt ID from which you want to fetch. If you set it as null, the SDK fetches from the latest receipt.
* @param pageSize The page size.
* @return The message receipt list and a cursor.
*/
[[AgoraChatClient sharedClient].chatManager asyncFetchGroupMessageAcksFromServer:messageId groupId:groupId startGroupAckId:nil pageSize:pageSize completion:^(EMCursorResult * aResult, EMError * error, int totalCount) {
// 页面刷新等操作。
}];