iOS

消息已读回执

更新时间: 2024/03/14 17:08:37

当发送方需要知道接收方是否已经阅读了自己发送的消息时,需要使用已读回执的功能。

网易云信 NIM iOS SDK 的NIMChatManagerDelegate协议和NIMChatManager协议,分别提供监听单聊/群聊消息已读回执和发送单聊消息已读回执的方法。调用发送已读回执的方法时,传入的消息即为需要显示为已读的消息。

单聊消息已读回执

uml diagram

前提条件

实现流程

  1. 消息发送方调用 addDelegate: 方法添加委托,注册onRecvMessageReceipts:消息已读回执回调函数,监听已读回执。

    示例代码如下:

    - (void)onRecvMessageReceipts:(NSArray<NIMMessageReceipt *> *)receipts
    {
        //your code 
    
    }
    
    
  2. 消息接收方在收到消息并阅读后,调用 sendMessageReceipt:completion:方法发送已读回执,调用时传入接收到的消息。

    • 如在会话界面中调用该方法并传入当前会话的最后一条消息,即表示这之前的消息本方都已读。
    • 在单聊场景中,不判断消息是否需要已读回执,即默认都需要已读回执。但是当该消息转发至群聊,群聊场景下需要判断是否需要已读回执,会读取单聊消息对象的 teamReceiptEnabled 字段,YES 才发送群聊已读回执。因此在单聊场景下也建议用户按需设置该参数。
    参数 类型 说明
    receipt NIMMessageReceipt 消息回执,具体参数见左侧链接。其中timestamp即为需要发送已读回执的消息的时间戳
    completion NIMSendMessageReceiptBlock 完成回调

    示例代码如下:

    NIMMessageReceipt *receipt = [[NIMMessageReceipt alloc] initWithMessage:message];
    // 发送回执
    [[[NIMSDK sharedSDK] chatManager] sendMessageReceipt:receipt
                                            completion:nil]; 
    
  3. SDK 触发回调函数,将已读回执(NIMMessageReceipt)发送给消息发送方。

  4. 消息发送方通过NIMMessage中的isRemoteRead来判断对方是否已读该消息。

群聊消息已读回执

本节以发送方与接收方的消息交互为例,介绍群聊消息已读回执的实现流程。

uml diagram

前提条件

  • 已创建相应数量的云信 IM 账号

  • 已在控制台开通群聊消息已读回执功能,具体请参见配置群组功能

  • 消息接收方已注册onRecvMessages:回调函数,监听消息接收。

  • 已创建群组且消息接收方已加入群组。

使用限制

使用群聊消息已读回执功能,需将群成员控制在 200 人以内。

实现流程

  1. 发送方和接收方在初始化 SDK时,将NIMSDKConfigteamReceiptEnabled设置为true,启用群聊消息已读回执功能。

  2. 发送方在登录 IM 前,调用 addDelegate: 方法添加委托,注册onRecvMessageReceipts:回调函数,监听群聊消息的已读回执。

    • 参数说明

      参数 类型 说明
      receipts NSArray<NIMMessageReceipt*> * 群聊消息已读回执数组,具体参数见NIMMessageReceipt
    • 示例代码

      - (void)onRecvMessageReceipts:(NSArray<NIMMessageReceipt *> *)receipts
      {
          //your code 
      }
      
  3. 发送方调用sendMessage:toSession:error:sendMessage:toSession:completion:方法发送群聊消息时,需将NIMMessageSettingteamReceiptEnabled设置为开启(标记该消息需要已读回执)。

    示例代码如下:

    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;
    
  4. 接收方接收到消息后,通过消息配置(NIMMessageSetting)的teamReceiptEnabled的开启状态,判断该消息是否需要发送已读回执。

  5. 如需要发送已读回执,接收方可调用NIMMessage中的isTeamReceiptSended方法判断是否已发送过该消息的已读回执。

  6. 接收方调用sendTeamMessageReceipts:completion:方法发送已读回执。

    • 参数说明

      参数 类型 说明
      receipt NIMMessageReceipt 消息回执,具体参数见左侧链接。其中messageId即为需要发送已读回执的消息的 ID
      completion 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];
      }
      
  7. SDK 触发回调函数,将已读回执发送至消息发送方。

群聊已读回执后续操作

消息发送方获取到群聊消息已读回执后,可调用如下方法刷新消息的未读数、查询已读/未读账号列表或查询单条消息的已读/未读数。

您还可通过NIMMessageteamReceiptInfo来获取当前消息的群已读回执信息。

手动刷新已读回执信息

调用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)组成的 NSSet
    completion 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);
        }
    }];
    
此文档是否对你有帮助?
有帮助
去反馈
  • 单聊消息已读回执
  • 前提条件
  • 实现流程
  • 群聊消息已读回执
  • 前提条件
  • 使用限制
  • 实现流程
  • 群聊已读回执后续操作
  • 手动刷新已读回执信息
  • 查询所有成员的已读回执详情
  • 查询服务端数据
  • 查询本地数据
  • 查询部分成员的已读回执详情
  • 查询服务端数据
  • 查询本地数据