====== 消息 ====== ---- 消息:IM 交互实体,在 SDK 中对应的类型是 **EMMessage**。**EMMessage** 由 EMMessageBody 组成。 ===== 构造消息 ===== ==== 构造文字消息 ==== EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"要发送的消息"]; NSString *from = [[EMClient sharedClient] currentUsername]; //生成Message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造图片消息 ==== EMImageMessageBody *body = [[EMImageMessageBody alloc] initWithData:data displayName:@"image.png"]; // body.compressionRatio = 1.0f; 1.0表示发送原图不压缩。默认值是0.6,压缩的倍数是0.6倍 NSString *from = [[EMClient sharedClient] currentUsername]; //生成Message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造位置消息 ==== EMLocationMessageBody *body = [[EMLocationMessageBody alloc] initWithLatitude:39 longitude:116 address:@"地址"]; NSString *from = [[EMClient sharedClient] currentUsername]; // 生成message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造语音消息 ==== EMVoiceMessageBody *body = [[EMVoiceMessageBody alloc] initWithLocalPath:@"audioPath" displayName:@"audio"]; body.duration = duration; NSString *from = [[EMClient sharedClient] currentUsername]; // 生成message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造视频消息 ==== EMVideoMessageBody *body = [[EMVideoMessageBody alloc] initWithLocalPath:@"videoPath" displayName:@"video.mp4"]; NSString *from = [[EMClient sharedClient] currentUsername]; // 生成message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造文件消息 ==== EMFileMessageBody *body = [[EMFileMessageBody alloc] initWithLocalPath:@"filePath" displayName:@"file"]; NSString *from = [[EMClient sharedClient] currentUsername]; // 生成message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造透传消息 ==== SDK 提供的一种特殊类型的消息,即 CMD,不会存 db,也不会走 APNS 推送,类似一种指令型的消息。比如您的服务器要通知客户端做某些操作,您可以服务器和客户端提前约定好某个字段,当客户端收到约定好的字段时,执行某种特殊操作。另,以“em_”和“easemob::”开头的action为内部保留字段,注意不要使用 EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:action]; NSString *from = [[EMClient sharedClient] currentUsername]; // 生成message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 ==== 构造扩展消息 ==== 当 SDK 提供的消息类型不满足需求时,开发者可以通过扩展自 SDK 提供的文本、语音、图片、位置等消息类型,从而生成自己需要的消息类型。 这里是扩展自文本消息,如果这个自定义的消息需要用到语音或者图片等,可以扩展自语音、图片消息,亦或是位置消息。 // 以单聊消息举例 EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"要发送的消息"]; NSString *from = [[EMClient sharedClient] currentUsername]; //生成Message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 message.ext = @{@"key":@"value"}; // 扩展消息部分 ==== 插入消息 ==== === 方法一 === 直接插入,这样插入会不验证消息所在的EMConversation对象是否存在,直接将消息插入到数据库中 EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"要插入的消息"]; NSString *from = [[EMClient sharedClient] currentUsername]; //生成Message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:messageExt]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 [[EMClient sharedClient].chatManager importMessages:@[message] completion:^(EMError *aError) { //code }]; === 方法二 === 这种插入方式,会验证所在的EMConversation对象的存在,插入后,会更新EMConversation对象中的属性,比如LatestMessage EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"要插入的消息"]; NSString *from = [[EMClient sharedClient] currentUsername]; //生成Message EMMessage *message = [[EMMessage alloc] initWithConversationID:@"6001" from:from to:@"6001" body:body ext:nil]; message.chatType = EMChatTypeChat;// 设置为单聊消息 //message.chatType = EMChatTypeGroupChat;// 设置为群聊消息 //message.chatType = EMChatTypeChatRoom;// 设置为聊天室消息 //message.timestamp = 1509689222137; 消息时间 EMConversation *conversation = [[EMClient sharedClient].chatManager getConversation:message.conversationId type:EMConversationTypeChat createIfNotExist:YES]; // type: 会话类型 // EMConversationTypeChat // 单聊 // EMConversationTypeGroupChat // 群聊 // EMConversationTypeChatRoom // 聊天室 [conversation insertMessage:message error:nil]; ==== 更新消息属性 ==== /*! * 更新消息到 DB * * @param aMessage 消息 * * @result 是否成功 */ - (BOOL)updateMessage:(EMMessage *)aMessage; //调用:[[EMClient sharedClient].chatManager updateMessage:aMessage]; ===== 会话 ===== 会话:操作聊天消息 **EMMessage** 的容器,在 SDK 中对应的类型是 **EMConversation**。 ==== 新建/获取一个会话 ==== 根据 conversationId 创建一个 conversation。 [[EMClient sharedClient].chatManager getConversation:@"8001" type:EMConversationTypeChat createIfNotExist:YES]; //EMConversationTypeChat 单聊会话 //EMConversationTypeGroupChat 群聊会话 //EMConversationTypeChatRoom 聊天室会话 * getConversation:创建与8001的会话 * type:会话类型 * createIfNotExist:不存在是否创建 ==== 删除会话 ==== === 删除单个会话 === [[EMClient sharedClient].chatManager deleteConversation:@"8001" isDeleteMessages:YES completion:^(NSString *aConversationId, EMError *aError){ //code }]; * deleteConversation: 删除与8001的会话 * deleteMessages: 删除会话中的消息 === 根据 conversationId 批量删除会话 === EMConversation *conversation = [[EMClient sharedClient].chatManager getConversation:@"8001" type:EMConversationTypeChat createIfNotExist:NO]; [[EMClient sharedClient].chatManager deleteConversations:@[conversation] isDeleteMessages:YES completion:^(EMError *aError){ //code }]; * deleteConversations: 要删除的会话 * deleteMessages: 删除会话中的消息 ==== 获取会话列表 ==== SDK中提供了三种获取会会话列表的方法。 === 获取或创建 === EMConversation *conversation = [[EMClient sharedClient].chatManager getConversation:@"8001" type:EMConversationTypeChat createIfNotExist:YES]; * getConversation: 获取或创建与8001的会话 * type:EMConversationTypeChat: 会话类型 === 获取所有会话(内存中有则从内存中取,没有则从db中取) === NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations]; ==== 获取会话未读消息数 ==== [EMConversation unreadMessagesCount]; ==== 消息检索 ==== 可以通过关键字、消息类型、开始结束时间检索某个会话中的消息。 /*! * 从数据库获取指定数量的消息,取到的消息按时间排序,并且不包含参考的消息,如果参考消息的ID为空,则从最新消息取 * * @param aMessageId 参考消息的ID * @param count 获取的条数 * @param aDirection 消息搜索方向 * @param aCompletionBlock 完成的回调 */ - (void)loadMessagesStartFromId:(NSString *)aMessageId count:(int)aCount searchDirection:(EMMessageSearchDirection)aDirection completion:(void (^)(NSArray *aMessages, EMError *aError))aCompletionBlock; /*! * 从数据库获取指定类型的消息,取到的消息按时间排序,如果参考的时间戳为负数,则从最新消息取,如果aCount小于等于0当作1处理 * * @param aType 消息类型 * @param aTimestamp 参考时间戳 * @param aCount 获取的条数 * @param aUsername 消息发送方,如果为空则忽略 * @param aDirection 消息搜索方向 * @param aCompletionBlock 完成的回调 */ - (void)loadMessagesWithType:(EMMessageBodyType)aType timestamp:(long long)aTimestamp count:(int)aCount fromUser:(NSString*)aUsername searchDirection:(EMMessageSearchDirection)aDirection completion:(void (^)(NSArray *aMessages, EMError *aError))aCompletionBlock; /*! * 从数据库获取包含指定内容的消息,取到的消息按时间排序,如果参考的时间戳为负数,则从最新消息向前取,如果aCount小于等于0当作1处理 * * @param aKeywords 搜索关键字,如果为空则忽略 * @param aTimestamp 参考时间戳 * @param aCount 获取的条数 * @param aSender 消息发送方,如果为空则忽略 * @param aDirection 消息搜索方向 * @param aCompletionBlock 完成的回调 */ - (void)loadMessagesWithKeyword:(NSString*)aKeyword timestamp:(long long)aTimestamp count:(int)aCount fromUser:(NSString*)aSender searchDirection:(EMMessageSearchDirection)aDirection completion:(void (^)(NSArray *aMessages, EMError *aError))aCompletionBlock; /*! * 从数据库获取指定时间段内的消息,取到的消息按时间排序,为了防止占用太多内存,用户应当制定加载消息的最大数 * * @param aStartTimestamp 毫秒级开始时间 * @param aEndTimestamp 结束时间 * @param aCount 加载消息最大数 * @param aCompletionBlock 完成的回调 */ - (void)loadMessagesFrom:(long long)aStartTimestamp to:(long long)aEndTimestamp count:(int)aCount completion:(void (^)(NSArray *aMessages, EMError *aError))aCompletionBlock; ===== 聊天 ===== 登录成功之后才能进行聊天操作。发消息时,单聊和群聊调用的是统一接口,区别只是要设置下 message.chatType。 ==== 发送消息 ==== /*! @property @brief 发送消息 @discussion 异步方法 */ - (void)sendMessage:(EMMessage *)aMessage progress:(void (^)(int progress))aProgressBlock completion:(void (^)(EMMessage *message, EMError *error))aCompletionBlock; //调用:[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {}]; ==== 接收消息 ==== 注册消息回调 //消息回调:EMChatManagerDelegate //移除消息回调 [[EMClient sharedClient].chatManager removeDelegate:self]; //注册消息回调 [[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil]; 在线普通消息会走以下回调: /*! @method @brief 接收到一条及以上非cmd消息 */ - (void)messagesDidReceive:(NSArray *)aMessages; 透传(cmd)在线消息会走以下回调: /*! @method @brief 接收到一条及以上cmd消息 */ - (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages; ==== 解析普通消息 ==== // 收到消息的回调,带有附件类型的消息可以用 SDK 提供的下载附件方法下载(后面会讲到) - (void)messagesDidReceive:(NSArray *)aMessages { for (EMMessage *message in aMessages) { EMMessageBody *msgBody = message.body; switch (msgBody.type) { case EMMessageBodyTypeText: { // 收到的文字消息 EMTextMessageBody *textBody = (EMTextMessageBody *)msgBody; NSString *txt = textBody.text; NSLog(@"收到的文字是 txt -- %@",txt); } break; case EMMessageBodyTypeImage: { // 得到一个图片消息body EMImageMessageBody *body = ((EMImageMessageBody *)msgBody); NSLog(@"大图remote路径 -- %@" ,body.remotePath); NSLog(@"大图local路径 -- %@" ,body.localPath); // // 需要使用sdk提供的下载方法后才会存在 NSLog(@"大图的secret -- %@" ,body.secretKey); NSLog(@"大图的W -- %f ,大图的H -- %f",body.size.width,body.size.height); NSLog(@"大图的下载状态 -- %lu",body.downloadStatus); // 缩略图sdk会自动下载 NSLog(@"小图remote路径 -- %@" ,body.thumbnailRemotePath); NSLog(@"小图local路径 -- %@" ,body.thumbnailLocalPath); NSLog(@"小图的secret -- %@" ,body.thumbnailSecretKey); NSLog(@"小图的W -- %f ,大图的H -- %f",body.thumbnailSize.width,body.thumbnailSize.height); NSLog(@"小图的下载状态 -- %lu",body.thumbnailDownloadStatus); } break; case EMMessageBodyTypeLocation: { EMLocationMessageBody *body = (EMLocationMessageBody *)msgBody; NSLog(@"纬度-- %f",body.latitude); NSLog(@"经度-- %f",body.longitude); NSLog(@"地址-- %@",body.address); } break; case EMMessageBodyTypeVoice: { // 音频sdk会自动下载 EMVoiceMessageBody *body = (EMVoiceMessageBody *)msgBody; NSLog(@"音频remote路径 -- %@" ,body.remotePath); NSLog(@"音频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在(音频会自动调用) NSLog(@"音频的secret -- %@" ,body.secretKey); NSLog(@"音频文件大小 -- %lld" ,body.fileLength); NSLog(@"音频文件的下载状态 -- %lu" ,body.downloadStatus); NSLog(@"音频的时间长度 -- %lu" ,body.duration); } break; case EMMessageBodyTypeVideo: { EMVideoMessageBody *body = (EMVideoMessageBody *)msgBody; NSLog(@"视频remote路径 -- %@" ,body.remotePath); NSLog(@"视频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在 NSLog(@"视频的secret -- %@" ,body.secretKey); NSLog(@"视频文件大小 -- %lld" ,body.fileLength); NSLog(@"视频文件的下载状态 -- %lu" ,body.downloadStatus); NSLog(@"视频的时间长度 -- %lu" ,body.duration); NSLog(@"视频的W -- %f ,视频的H -- %f", body.thumbnailSize.width, body.thumbnailSize.height); // 缩略图sdk会自动下载 NSLog(@"缩略图的remote路径 -- %@" ,body.thumbnailRemotePath); NSLog(@"缩略图的local路径 -- %@" ,body.thumbnailLocalPath); NSLog(@"缩略图的secret -- %@" ,body.thumbnailSecretKey); NSLog(@"缩略图的下载状态 -- %lu" ,body.thumbnailDownloadStatus); } break; case EMMessageBodyTypeFile: { EMFileMessageBody *body = (EMFileMessageBody *)msgBody; NSLog(@"文件remote路径 -- %@" ,body.remotePath); NSLog(@"文件local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在 NSLog(@"文件的secret -- %@" ,body.secretKey); NSLog(@"文件文件大小 -- %lld" ,body.fileLength); NSLog(@"文件文件的下载状态 -- %lu" ,body.downloadStatus); } break; default: break; } } } ==== 解析透传消息 ==== - (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages { for (EMMessage *message in aCmdMessages) { EMCmdMessageBody *body = (EMCmdMessageBody *)message.body; NSLog(@"收到的action是 -- %@",body.action); } } ==== 解析消息扩展属性 ==== - (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages { for (EMMessage *message in aCmdMessages) { // cmd消息中的扩展属性 NSDictionary *ext = message.ext; NSLog(@"cmd消息中的扩展属性是 -- %@",ext) } } // 收到消息回调 - (void)messagesDidReceive:(NSArray *)aMessages { for (EMMessage *message in aMessages) { // 消息中的扩展属性 NSDictionary *ext = message.ext; NSLog(@"消息中的扩展属性是 -- %@",ext); } } ==== 自动下载消息中的附件 ==== SDK 接收到消息后,会默认下载:图片消息的缩略图,语音消息的语音,视频消息的视频第一帧。 **请先判断你要下载附件没有下载成功之后,在调用以下下载方法,否则SDK下载方法会再次从服务器上获取附件。** [[EMClient sharedClient].chatManager downloadMessageThumbnail:message progress:nil completion:^(EMMessage *message, EMError *error) { if (!error) { NSLog(@"下载成功,下载后的message是 -- %@",aMessage); } }]; ==== 下载消息中的原始附件 ==== [[EMClient sharedClient].chatManager downloadMessageAttachment:message progress:nil completion:^(EMMessage *message, EMError *error) { if (!error) { NSLog(@"下载成功,下载后的message是 -- %@",aMessage); } }]; ==== 消息已送达回执 ==== SDK提供了已送达回执,当对方收到您的消息后,您会收到以下回调。 /*! @method @brief 接收到一条及以上已送达回执 */ - (void)messagesDidDeliver:(NSArray *)aMessages; ==== 消息已读回执 ==== 已读回执需要开发者主动调用的。当用户读取消息后,由开发者主动调用方法。 === 发送已读回执 === // 发送已读回执。在这里写只是为了演示发送,在APP中具体在哪里发送需要开发者自己决定。 [[EMClient sharedClient].chatManager sendMessageReadAck:message completion:^(EMMessage *message, EMError *error) { if (!error) { NSLog(@"发送成功"); } }]; === 接收已读回执 === /*! * 接收到一条及以上已读回执 * * @param aMessages 消息列表 */ - (void)messagesDidRead:(NSArray *)aMessages; ==== 消息漫游 ==== SDK提供了从服务器获取历史消息的接口 /** * \~chinese * 从服务器获取指定会话的历史消息 * * @param aConversationId 要获取漫游消息的Conversation id * @param aConversationType 要获取漫游消息的Conversation type * @param aStartMessageId 参考起始消息的ID * @param aPageSize 获取消息条数 * @param pError 错误信息 * * @return 获取的消息结果 * * * \~english * Fetch conversation roam messages from server. * @param aConversationId The conversation id which select to fetch roam message. * @param aConversationType The conversation type which select to fetch roam message. * @param aStartMessageId The start search roam message, if empty start from the server leastst message. * @param aPageSize The page size. * @param pError EMError used for output. * * @return The result */ - (EMCursorResult *)fetchHistoryMessagesFromServer:(NSString *)aConversationId conversationType:(EMConversationType)aConversationType startMessageId:(NSString *)aStartMessageId pageSize:(int)aPageSize error:(EMError **)pError; /** * \~chinese * 从服务器获取指定会话的历史消息 * * 异步方法 * * @param aConversationId 要获取漫游消息的Conversation id * @param aConversationType 要获取漫游消息的Conversation type * @param aStartMessageId 参考起始消息的ID * @param aPageSize 获取消息条数 * @param aCompletionBlock 获取消息结束的callback * * * \~english * Fetch conversation roam messages from server. * @param aConversationId The conversation id which select to fetch roam message. * @param aConversationType The conversation type which select to fetch roam message. * @param aStartMessageId The start search roam message, if empty start from the server leastst message. * @param aPageSize The page size. * @param aCompletionBlock The callback block of fetch complete */ - (void)asyncFetchHistoryMessagesFromServer:(NSString *)aConversationId conversationType:(EMConversationType)aConversationType startMessageId:(NSString *)aStartMessageId pageSize:(int)aPageSize complation:(void (^)(EMCursorResult *aResult, EMError *aError))aCompletionBlock; ==== 消息撤回 ==== /*! * \~chinese * 撤回消息 * * 异步方法 * * @param aMessage 消息 * @param aCompletionBlock 完成的回调 * * \~english * Recall a message * * * @param aMessage Message instance * @param aCompletionBlock The callback block of completion * */ - (void)recallMessage:(EMMessage *)aMessage completion:(void (^)(EMMessage *aMessage, EMError *aError))aCompletionBlock; 注:消息撤回为增值功能。请提供租户id,联系环信商务经理开通。 ---- 上一页:[[im:300iosclientintegration:140iosquickstart|iOS SDK 快速集成]] 下一页:[[im:300iosclientintegration:50buddymgm|好友管理]]