消息已读回执
更新时间: 2024/11/21 15:38:17
当发送方需要知道接收方是否已经阅读了自己发送的消息时,需要使用已读回执的功能。
网易云信 NIM iOS SDK 的NIMChatManagerDelegate
协议和NIMChatManager
协议,分别提供监听单聊/群聊消息已读回执和发送单聊消息已读回执的方法。调用发送已读回执的方法时,传入的消息即为需要显示为已读的消息。
单聊消息已读回执
sequenceDiagram
note over NIM SDK: 初始化
消息发送方 ->> NIM SDK: 初始化SDK
消息接收方 ->> NIM SDK: 初始化SDK
note over NIM SDK: 发送方注册监听并登录
消息发送方 ->> NIM SDK: 监听已读回执
消息发送方 ->> NIM SDK: 登录
note over NIM SDK: 接收方注册监听并登录
消息接收方 ->> NIM SDK: 监听消息接收
消息接收方 ->> NIM SDK: 登录
note over NIM SDK: 消息收发
消息发送方 ->> NIM SDK: 构建并发送消息
NIM SDK ->> 消息接收方: NIMMessage
note over NIM SDK: 消息转发
消息接收方 ->> NIM SDK: 发送已读回执
NIM SDK ->> 消息发送方: NIMMessageReceipt
前提条件
-
已完成 SDK 初始化。
-
消息接收方已注册
onRecvMessages:
回调函数,监听消息接收。
实现流程
-
消息发送方调用
addDelegate:
方法添加委托,注册onRecvMessageReceipts:
消息已读回执回调函数,监听已读回执。示例代码如下:
- (void)onRecvMessageReceipts:(NSArray<NIMMessageReceipt *> *)receipts { //your code }
-
消息接收方在收到消息并阅读后,调用
sendMessageReceipt:completion:
方法发送已读回执,调用时传入接收到的消息。- 如在会话界面中调用该方法并传入当前会话的最后一条消息,即表示这之前的消息本方都已读。
- 在单聊场景中,不判断消息是否需要已读回执,即默认都需要已读回执。但是当该消息转发至群聊,群聊场景下需要判断是否需要已读回执,会读取单聊消息对象的
teamReceiptEnabled
字段,YES 才发送群聊已读回执。因此在单聊场景下也建议用户按需设置该参数。
参数 类型 说明 receipt
NIMMessageReceipt
消息回执,具体参数见左侧链接。其中 timestamp
即为需要发送已读回执的消息的时间戳completion
NIMSendMessageReceiptBlock
完成回调 示例代码如下:
NIMMessageReceipt *receipt = [[NIMMessageReceipt alloc] initWithMessage:message]; // 发送回执 [[[NIMSDK sharedSDK] chatManager] sendMessageReceipt:receipt completion:nil];
-
SDK 触发回调函数,将已读回执(
NIMMessageReceipt
)发送给消息发送方。 -
消息发送方通过
NIMMessage
中的isRemoteRead
来判断对方是否已读该消息。
群聊消息已读回执
本节以发送方与接收方的消息交互为例,介绍群聊消息已读回执的实现流程。
sequenceDiagram
note over NIM SDK: 初始化
消息发送方 ->> NIM SDK: 初始化SDK
note left of 消息发送方 : 初始化时启用<br>群聊消息已读回执
消息接收方 ->> NIM SDK: 初始化SDK
note left of 消息接收方 : 初始化时启用<br>群聊消息已读回执
note over NIM SDK: 发送方注册监听并登录
消息发送方 ->> NIM SDK: 监听群聊消息已读回执
消息发送方 ->> NIM SDK: 登录
note over NIM SDK: 接收方注册监听并登录
消息接收方 ->> NIM SDK: 监听消息接收
消息接收方 ->> NIM SDK: 登录
note over NIM SDK: 人员加入群组
消息发送方 ->> NIM SDK: 创建群组
消息接收方 ->> NIM SDK: 加入群组
note over NIM SDK: 消息收发
消息发送方 ->> NIM SDK: 构建并发送消息
note left of 消息发送方 : 需标记该消息需要已读回执
NIM SDK ->> 消息接收方: NIMMessage
note over NIM SDK: 发送已读回执
消息接收方 ->> 消息接收方: 通过标记判断消息<br>是否需要发送已读回执
消息接收方 ->> NIM SDK: 发送群聊消息已读回执
NIM SDK ->> 消息发送方: NIMMessageReceipt
前提条件
-
已创建相应数量的云信 IM 账号。
-
已在控制台开通群聊消息已读回执功能,具体请参见配置群组功能。
-
消息接收方已注册
onRecvMessages:
回调函数,监听消息接收。 -
已创建群组且消息接收方已加入群组。
使用限制
使用群聊消息已读回执功能,需将群成员控制在 200 人以内。
实现流程
-
发送方和接收方在初始化 SDK时,将
NIMSDKConfig
的teamReceiptEnabled
设置为true
,启用群聊消息已读回执功能。 -
发送方在登录 IM 前,调用
addDelegate:
方法添加委托,注册onRecvMessageReceipts:
回调函数,监听群聊消息的已读回执。-
参数说明
参数 类型 说明 receipts
NSArray<NIMMessageReceipt*> *
群聊消息已读回执数组,具体参数见 NIMMessageReceipt
-
示例代码
- (void)onRecvMessageReceipts:(NSArray<NIMMessageReceipt *> *)receipts { //your code }
-
-
发送方调用
sendMessage:toSession:error:
或sendMessage:toSession:completion:
方法发送群聊消息时,需将NIMMessageSetting
的teamReceiptEnabled
设置为开启(标记该消息需要已读回执)。示例代码如下:
NIMMessage *message = [[NIMMessage alloc] init]; message.text = [NSString stringWithFormat:@"Test messasge %@", @(i)]; message.apnsPayload = @{ @"apns-collapse-id": message.messageId, }; // 设置示例 NIMMessageSetting *setting = [[NIMMessageSetting alloc] init]; setting.scene = NIMNOSSceneTypeMessage; setting.teamReceiptEnabled = YES; message.setting = setting;
-
接收方接收到消息后,通过消息配置(
NIMMessageSetting
)的teamReceiptEnabled
的开启状态,判断该消息是否需要发送已读回执。 -
如需要发送已读回执,接收方可调用
NIMMessage
中的isTeamReceiptSended
方法判断是否已发送过该消息的已读回执。 -
接收方调用
sendTeamMessageReceipts:completion:
方法发送已读回执。-
参数说明
参数 类型 说明 receipt
NIMMessageReceipt
消息回执,具体参数见左侧链接。其中 messageId
即为需要发送已读回执的消息的 IDcompletion
NIMSendTeamMessageReceiptsBlock
完成回调 -
示例代码
NSMutableArray *receipts = [NSMutableArray array]; for (NIMMessage *item in messages) { NIMMessage *message = nil; if ([item isKindOfClass:[NIMMessage class]]) { message = item; } if (message) { if (!message.isOutgoingMsg && message.setting.teamReceiptEnabled) { NIMMessageReceipt *receipt = [[NIMMessageReceipt alloc] initWithMessage:message]; [receipts addObject:receipt]; } } } if([receipts count]) { [[[NIMSDK sharedSDK] chatManager] sendTeamMessageReceipts:receipts completion:nil]; }
-
-
SDK 触发回调函数,将已读回执发送至消息发送方。
群聊已读回执后续操作
消息发送方获取到群聊消息已读回执后,可调用如下方法刷新消息的未读数、查询已读/未读账号列表或查询单条消息的已读/未读数。
您还可通过NIMMessage
的teamReceiptInfo
来获取当前消息的群已读回执信息。
手动刷新已读回执信息
调用refreshTeamMessageReceipts:
方法,可批量刷新群聊消息集合的已读和未读数。消息已读变化后,会通过NIMChatManager
的代理的 onRecvMessageReceipts:
回调给上层,刷新的消息必须为群组消息。
-
API 原型
objc
@protocol NIMChatManager <NSObject> - (void)refreshTeamMessageReceipts:(NSArray<NIMMessage *> *)messages; @end
-
参数说明
参数 类型 说明 message
NSArray<NIMMessage *> * 要更新的消息集合 -
示例代码
- (NSDictionary *)checkTeamReceipts:(NSArray<NIMMessageReceipt *> *)receipts { NSMutableSet *filtedMessaegeIds = nil; if (receipts.count) { //说明只要局部更新 filtedMessaegeIds = [[NSMutableSet alloc] init]; for (NIMMessageReceipt *receipt in receipts) { [filtedMessaegeIds addObject:receipt.messageId]; } } NSMutableDictionary *dict = [NSMutableDictionary dictionary]; BOOL hasConfig = self.sessionConfig && [self.sessionConfig respondsToSelector:@selector(shouldHandleReceiptForMessage:)]; NSMutableArray *queryMessages = [NSMutableArray array]; for (NSInteger i = [[self.dataSource items] count] - 1; i >= 0; i--) { id item = [[self.dataSource items] objectAtIndex:i]; if ([item isKindOfClass:[NIMMessageModel class]]) { // NIMMessageModel 为自定义的NIMMessage 包装类 NIMMessageModel *model = (NIMMessageModel *)item; NIMMessage *message = [model message]; if (filtedMessaegeIds && ![filtedMessaegeIds containsObject:message.messageId]) { //本次刷新不包含此消息,略过 continue; } if (!receipts) { //说明是全部刷新,这个时候消息的回执数可能是过期的,查刷一下 [queryMessages addObject:message]; } if (message.isOutgoingMsg) { if (message.setting.teamReceiptEnabled && hasConfig && [self.sessionConfig shouldHandleReceiptForMessage:message]) { model.shouldShowReadLabel = YES; dict[@(i)] = model; } } } } if ([queryMessages count]) { [[NIMSDK sharedSDK].chatManager refreshTeamMessageReceipts:queryMessages]; } return dict; }
查询所有成员的已读回执详情
您可查询服务端或者本地数据库的已读回执详情(已读和未读的账号列表)。
查询服务端数据
调用queryMessageReceiptDetail:completion:
方法可查询某条指定群聊消息在该群组所有成员中的已读和未读账号列表。
查询结果不会随消息的已读人数变化而变化。如需获取最新的详情,必须再次调用此方法。
-
API 原型
objc
- (void)queryMessageReceiptDetail:(NIMMessage *)message completion:(NIMQueryReceiptDetailBlock)completion;
-
示例代码
__weak typeof(self) weakSelf = self; [SVProgressHUD show]; [[NIMSDK sharedSDK].chatManager queryMessageReceiptDetail:self.message completion:^(NSError * _Nullable error, NIMTeamMessageReceiptDetail * _Nullable detail) { [SVProgressHUD dismiss]; if (error != nil) { [weakSelf.view makeToast:@"请求失败请重试".ntes_localized duration:2.0 position:CSToastPositionCenter]; detail = [[NIMSDK sharedSDK].chatManager localMessageReceiptDetail:self.message]; NSLog(@"localMessageReceiptDetail,read:%@, unread:%@", detail.readUserIds, detail.unreadUserIds); } }];
查询本地数据
调用localMessageReceiptDetail:
方法,可从本地数据库查询某条群聊消息在该群组所有成员中的已读和未读账号列表。
从本地数据库查询的已读未读账号列表通常比离线前的数据更陈旧。如需获取准确数据,请调用queryMessageReceiptDetail:completion:
方法查询已读未读账号列表。
-
API 原型
objc
- (nullable NIMTeamMessageReceiptDetail *)localMessageReceiptDetail:(NIMMessage *)message;
-
示例代码
__weak typeof(self) weakSelf = self; [SVProgressHUD show]; [[NIMSDK sharedSDK].chatManager queryMessageReceiptDetail:self.message completion:^(NSError * _Nullable error, NIMTeamMessageReceiptDetail * _Nullable detail) { [SVProgressHUD dismiss]; if (error != nil) { [weakSelf.view makeToast:@"请求失败请重试".ntes_localized duration:2.0 position:CSToastPositionCenter]; detail = [[NIMSDK sharedSDK].chatManager localMessageReceiptDetail:self.message]; NSLog(@"localMessageReceiptDetail,read:%@, unread:%@", detail.readUserIds, detail.unreadUserIds); } }];
查询部分成员的已读回执详情
您可从服务端或者本地数据库查询该群组部分成员的已读回执详情(已读和未读的账号列表)。
查询服务端数据
调用queryMessageReceiptDetail:accountSet:completion:
方法,可指定群组中的部分成员,查询他们对于某条群聊消息的已读和未读详情。调用成功将返回这些成员的已读和未读账号列表。
-
API 原型
- (void)queryMessageReceiptDetail:(NIMMessage *)message accountSet:(NSSet *)accountSet completion:(NIMQueryReceiptDetailBlock)completion;
-
参数说明
参数 类型 说明 message
NIMMessage
待查询的群聊消息 accountSet
NSSet 指定的用户的账号( accid
)组成的 NSSetcompletion
NIMQueryReceiptDetailBlock
查询群聊消息回执详情回调 -
示例代码
NSSet *set=[NSSet setWithObjects:@"jack",@"yang",nil]; __weak typeof(self) weakSelf = self; [SVProgressHUD show]; [[NIMSDK sharedSDK].chatManager queryMessageReceiptDetail:self.message accountSet:set completion:^(NSError * _Nullable error, NIMTeamMessageReceiptDetail * _Nullable detail) { [SVProgressHUD dismiss]; if (error != nil) { [weakSelf.view makeToast:@"请求失败请重试".ntes_localized duration:2.0 position:CSToastPositionCenter]; detail = [[NIMSDK sharedSDK].chatManager localMessageReceiptDetail:self.message]; NSLog(@"localMessageReceiptDetail,read:%@, unread:%@", detail.readUserIds, detail.unreadUserIds); } }];
查询本地数据
调用localMessageReceiptDetail:accountSet:
方法,可指定该群组的部分成员,从本地数据库查询他们对于某条群聊消息的已读和未读详情。调用成功将返回这些成员的已读和未读账号列表。
从本地数据库查询的已读未读账号列表通常比离线前的数据更陈旧。如需获取准确数据,请调用queryMessageReceiptDetail:accountSet:completion:
方法查询部分成员的已读未读账号列表。
-
API 原型
objc
- (nullable NIMTeamMessageReceiptDetail *)localMessageReceiptDetail:(NIMMessage *)message accountSet:(NSSet *)accountSet;
-
参数说明
参数 类型 说明 message
NIMMessage 要查询的消息 accountSet
NSSet 指定的用户的账号( accid
)组成的 NSSet -
示例代码
NSSet *set=[NSSet setWithObjects:@"jack",@"yang",nil]; __weak typeof(self) weakSelf = self; [SVProgressHUD show]; [[NIMSDK sharedSDK].chatManager queryMessageReceiptDetail:self.message accountSet:set completion:^(NSError * _Nullable error, NIMTeamMessageReceiptDetail * _Nullable detail) { [SVProgressHUD dismiss]; if (error != nil) { [weakSelf.view makeToast:@"请求失败请重试".ntes_localized duration:2.0 position:CSToastPositionCenter]; detail = [[NIMSDK sharedSDK].chatManager localMessageReceiptDetail:self.message]; NSLog(@"localMessageReceiptDetail,read:%@, unread:%@", detail.readUserIds, detail.unreadUserIds); } }];