iOS

消息收发

更新时间: 2024/03/15 16:12:31

NetEase IM iOS SDK(以下简称“NIM SDK”)支持收发多种消息类型,助您快速实现多样化的消息业务场景。

本文介绍通过网易云信 NIM SDK 实现消息收发的技术原理、前提条件以及具体的实现流程。

超大群、聊天室和圈组的消息收发,需单独配置。具体实现流程请分别参见超大群聊天室消息收发圈组消息收发

技术原理

应用集成 NIM SDK 并完成 SDK 初始化后,消息收发流程如下图所示(本地提示消息通知消息除外)。

上图中的流程可归纳为如下三步:

  1. 账号集成与登录。

    1. 开发者将应用的用户账号传入云信 IM 服务器(又称 accid)。
    2. 云信 IM 服务器返回 Token 给应用服务器。
    3. 应用客户端登录应用服务器。
    4. 应用服务器将 Token 返回给应用客户端。
    5. 用户带 Token 登录云信 IM 服务器。
  2. 用户A 发送一条消息到云信 IM 服务器。

  3. 云信 IM 服务器投递消息至其他用户,分为如下两种情况:

    • 如为单聊消息,IM 服务器将其投递至用户B。
    • 如为群聊消息,IM 服务器将其投递至群内其他每一位用户。

    上图仅以静态 Token 登录为例展示消息收发流程。网易云信 IM 还支持动态 Token 登录鉴权和第三方回调登录鉴权,相关详情请参见登录鉴权

前提条件

在实现消息收发之前,请确保:

API使用限制

发送消息(sendMessage)的方法调用存在频控,一分钟内默认最多可调用 300 次。

实现消息收发

NIM SDK 提供NIMChatManagerDelegate协议和NIMChatManager协议,支持构建、监听和收发多种类型的消息。SDK 中定义消息的结构为NIMMessage(不支持继承扩展),不同消息类型以messageType作区分。

公共参数说明

发送不同类型消息的方法均为sendMessage:toSession:error:方法或sendMessage:toSession:completion:异步方法。

这两个方法的参数说明如下:

参数
类型
说明
message NIMMessage 需要发送的消息,开发者构造出 message 后,需要选择构造对应的 messageObject 注入 (文本消息直接填入 text 即可,无须消息附件注入),即可传入此接口进行发送
session NIMSession 需要发送到的会话。通过sessionType参数,可设置发送的文本消息为单聊消息或群聊消息。如设置为群聊消息,请确保已创建相应的群组。
error NSError * 开发者需要自己构造出一个 NSError 对象,并将对象引用传入。如果在准备发送消息阶段发生错误,这个对象会被填充相应的信息。通常为参数检查错误或者登录状态错误,可参考错误码说明定位具体的出错类型
completion void(^)(NSError *error) 发送完成后的回调,这里的回调完成只表示当前这个函数调用完成,需要后续的回调才能判断消息是否已经发送至服务器

可通过消息配置选项NIMMessageSetting设置该消息是否存入云端、写入漫游、计入未读数等。具体配置示例请参见消息配置选项

收发文本消息

uml diagram

以下信息仅对上图中标为橙色的流程进行详细说明,其他流程请参考相关文档。

  1. 发送方在登录前,调用 addDelegate: 方法添加委托(具体回调函数如下),完成对消息发送相关事件的监听。

    示例代码如下:

    // 在某处添加代理对象
    [NIMSDK sharedSDK].chatManager addDelegate:self];
    //...
    
    //回调方法监听,此处为消息即将发送事件
    -(void)willSendMessage:(NIMMessage *)message 
    {
        //your code
    }
    
    //发送进度回调
    -(void)sendMessage:(NIMMessage *)message progress:(float)progress
    {
        //your code
    }
    
    //消息发送完成回调
    //发送结果
    - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error
    {
        //your code
    }
    
  2. 接收方在登录前,调用 addDelegate: 方法添加委托,注册onRecvMessages:回调函数,监听消息接收。

    示例代码如下:

    //收到消息
    - (void)onRecvMessages:(NSArray *)messages
    {
        //your code
    }
    
  3. 发送方构建一条文本消息,并调用sendMessage:toSession:error:或针对大文件的sendMessage:toSession:completion:异步方法发送该消息。

    以发送一条文本消息 hello 至好友 Id 为user的业务场景为例,示例代码如下:

    // 构造出具体会话:P2P单聊,对方账号为user
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 构造出具体消息
    NIMMessage *message = [[NIMMessage alloc] init];
    message.text        = @"hello";
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
  4. onRecvMessage:回调函数触发,投递文本消息至接收方。

收发多媒体消息

uml diagram

多媒体消息包括图片消息、语音消息、视频消息和文件消息。

以下信息仅对上图中标为橙色的流程进行详细说明,其他流程请参考相关文档。

  1. 发送方在登录前,调用 addDelegate: 方法添加委托,完成对消息发送相关事件的监听。

    //附件上传完成
    - (void)uploadAttachmentSuccess:(NSString *)urlString
                        forMessage:(NIMQChatMessage *)message
    {
    //your code
    }
    
  2. 接收方在登录前,调用addDelegate:方法添加委托,注册如下回调函数。

  3. 发送方通过初始化附件实例获取多媒体附件,并构建消息对象,注入附件,最后调用sendMessage:toSession:error:或针对大文件的sendMessage:toSession:completion:异步方法发送该消息。

    可在初始化附件实例时预设其NOS 资源存储场景,指定其在网易对象存储(Netease Object Storage, NOS)服务上的存活时长。

    多媒体附件 说明
    NIMImageObject 图片实例对象,左侧链接内包含图片实例的参数说明与图片实例初始化说明
    NIMAudioObject 语音实例对象,左侧链接内包含语音实例的参数说明与语音实例初始化说明 NIM SDK 提供了高清语音的录制与播放的功能,用于处理语音消息的语音附件。相关详情请参见语音消息处理
    NIMVideoObject 视频实例对象,左侧链接内包含视频实例的参数说明与视频实例初始化说明
    NIMFileObject 文件实例对象,左侧链接内包含文件实例的参数说明与文件实例初始化说明
    • 发送图片消息的示例代码
    图片(以UIImage初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得图片附件对象
    NIMImageObject *object = [[NIMImageObject alloc] initWithImage:image];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    图片(以图片路径初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得图片附件对象
    NIMImageObject *object = [[NIMImageObject alloc] initWithFilepath:path];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    
    图片(以图片数据初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得图片附件对象
    NIMImageObject *object = [[NIMImageObject alloc] initWithData:data extension:@"png"];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    • 发送语音消息示例代码
    语音(以语音路径初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得语音附件对象
    NIMAudioObject *object = [[NIMAudioObject alloc] initWithSourcePath:path];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    语音(语音数据初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得语音附件对象
    NIMAudioObject *audioObject = [[NIMAudioObject alloc] initWithData:data extension:@"aac"];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    • 发送视频消息示例代码
    视频(以视频路径初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得视频附件对象
    NIMVideoObject *object = [[NIMVideoObject alloc] initWithSourcePath:path];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    视频(以视频数据初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得视频附件对象
    NIMVideoObject *object = [[NIMVideoObject alloc] initWithData:data extension:@"mp4"];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    • 发送文件消息示例代码
    文件(以文件路径初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得文件附件对象
    NIMFileObject *object = [[NIMFileObject alloc] initWithSourcePath:path];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
    文件(以文件数据初始化)
    // 构造出具体会话
    NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
    // 获得视频附件对象
    NIMFileObject *audioObject = [[NIMFileObject alloc] initWithData:data extension:@"data"];
    // 构造出具体消息并注入附件
    NIMMessage *message = [[NIMMessage alloc] init];
    message.messageObject = object;
    // 错误反馈对象
    NSError *error = nil;
    // 发送消息
    [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
    
  4. (可选)发送方在发送消息后,可进行如下操作。

    示例代码如下:

    //判断是否在传输
    BOOL isInTransport = [[NIMSDK sharedSDK].chatManager messageInTransport: msg];
    ...
    
    //获取进度
    float progress = [[NIMSDK sharedSDK].chatManager messageTransportProgress:msg];
    
    
  5. onRecvMessage:函数触发,接收方收到文本消息。

  6. (可选)接收方接收消息后,可进行如下操作。

  7. (可选)图片消息的缩略图、语音消息的语音文件和视频消息的视频封面,默认由 SDK 自动下载。如果自动下载失败(本地没有这些文件),那么接收方可调用fetchMessageAttachment:error:方法获取。

    示例代码如下:

    //下载附件
    NSError *error;
    [[NIMSDK sharedSDK].chatManager fetchMessageAttachment:msg error:&error];
    
    

收发位置消息

地理位置消息收发流程与文本消息收发流程基本一致,区别在于需要构建的消息对象不同(位置消息对象为NIMLocationObject)。提供了初始化位置实例的initWithLatitude:longitude:title:方法。本节仅简要展示调用示例,具体实现流程请参考上文的收发文本消息

以下示例代码的业务场景为:发送一条位置消息至 IM 账号为 user 的好友, 位置的经纬度为 (30.27415,120.15515) ,地点名为 address。

// 构造出具体会话
NIMSession *session = [NIMSession session:@"user" type:NIMSessionTypeP2P];
// 获得位置附件对象
NIMLocationObject *object = [[NIMLocationObject alloc] initWithLatitude:120.15515 longitude:30.27415 title:@"address"];
// 构造出具体消息并注入附件
NIMMessage *message = [[NIMMessage alloc] init];
message.messageObject = object;
// 错误反馈对象
NSError *error = nil;
// 发送消息
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];

收发提示消息

提示消息主要用于会话内的通知提醒,消息使用场景例如:进入会话时出现的欢迎消息,或是会话过程中命中敏感词后的提示消息等场景。也可以用自定义消息实现,但相对复杂。

提示消息附件内部没有额外的信息字段,提示内容建议放入 NIMMessage 中的 text 字段,额外信息可以存储在 NIMMessageremoteExtlocalExt 字段中。

附件原型:

objc@interface NIMTipObject : NSObject<NIMMessageObject>
@end

以下示例代码的业务场景为:发送一条提示消息至云信 IM 账号为 user 的好友 , 文案内容为 welcome

objc// 构造出具体会话
NIMSession *session  = [NIMSession session:@"user" type:NIMSessionTypeP2P];
// 获得文件附件对象
NIMTipObject *object = [[NIMTipObject alloc] init];
// 构造出具体消息并注入附件
NIMMessage *message = [[NIMMessage alloc] init];
message.messageObject = object;
message.text = @"welcome";
// 错误反馈对象
NSError *error = nil;
// 发送消息
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];

接收通知消息

针对一些特定场景的事件,云信服务器预置了一些通知消息,在事件发生时下发到 SDK。通知消息也是一种特定消息,开发者需解析消息中附带的信息,来获取通知内容。如最常见的通知消息——群通知事件,如有新成员进群,则群内已有成员将收到此通知消息。

  • 通知消息属于会话内的一种消息,其对应的数据结构为 NIMMessage,消息类型为 NIMMessageTypeNotification。通知消息目前用于在群和聊天室的事件通知。

  • 通知消息需要进行解析,具体请参见验证入群邀请

  • 可对通知消息进行过滤,具体请参见通知消息过滤

收发自定义消息

除了上述内置消息类型以外,NIM SDK 还支持收发自定义消息类型。具体实现流程介绍,请参见自定义消息收发

常见问题

发送消息后怎么获取消息内容

实现NIMChatManagerDelegatesendMessage:didCompleteWithError:方法来接收发送消息完成回调,其中回调NIMMessage对象。可以通过isOutgoingMsg属性判断是否是发送出去的消息,通过session属性获取聊天对象的accid/群组id/聊天室id,通过text属性获取消息文本(仅适用于文本消息和提示消息),通过messageObject属性获取消息附件内容,通过deliveryState属性获取发送消息的投递状态,通过timestamp属性获取消息发送时间。

deliveryState 默认是发送失败的状态,但仅作参考;准确实时的消息状态,建议客户根据消息发送完成的回调来重新从本地数据库取消息记录用来展示。或者根据消息发送的生命周期(将要发送,发送完成等回调),在消息的扩展字段定义消息的发送状态,用来 UI 展示。

如何判断消息已发送成功

调用消息发送接口sendMessage:toSession:error:时,判断error == nil表示方法调用成功;实现NIMChatManagerDelegate的–sendMessage:didCompleteWithError:方法,判断error == nil表示消息已经发送至服务器。

如何设置消息的扩展字段

单聊或群聊消息具有服务端扩展字段和客户端扩展字段。服务端扩展字段只能在消息发送前设置,会同步到其他端;客户端扩展字段在消息发送前后设置均可,不会同步到其他端。

扩展字段,请使用 JSON 格式封装,并传入非格式化的 JSON 字符串,最大长度 1024 字节。


具体方法如下:

更新客户端扩展字段
  1. 对于单聊或群聊消息,构造NIMMessage对象时,通过localExt方法设置客户端扩展字段。

  2. 调用updateMessage:forSession:completion方法更新消息的本地扩展字段。

    设置消息的客户端扩展字段后,必须调用updateMessage:forSession:completion方法,否则无法生效。

更新服务端扩展字段

对于单聊或群聊消息,构造IMMessage对象时,通过setRemoteExtension方法设置消息的服务端扩展字段,

API参考

API
说明
addDelegate: 添加委托,可通过参数delegate配置需要委托的回调函数
removeDelegate: 移除委托,可通过参数delegate配置需要移除委托的回调函数
willSendMessage 即将发送消息回调
uploadAttachmentSuccess:forMessage: 上传资源文件成功回调
sendMessage:progress: 发送消息进度回调
sendMessage:didCompleteWithError: 发送消息完成回调
onRecvMessages: 收到消息回调
fetchMessageAttachment:progress: 收到消息附件进度回调
fetchMessageAttachment:didCompleteWithError: 收到消息附件完成回调
sendMessage:toSession:error: 发送消息
sendMessage:toSession:completion: 异步发送消息
cancelSendingMessage: 取消发送消息

更多相关 SDK API,请参见NIMChatManagerDelegate协议和NIMChatManager协议。

此文档是否对你有帮助?
有帮助
去反馈
  • 技术原理
  • 前提条件
  • API使用限制
  • 实现消息收发
  • 公共参数说明
  • 收发文本消息
  • 收发多媒体消息
  • 收发位置消息
  • 收发提示消息
  • 接收通知消息
  • 收发自定义消息
  • 常见问题
  • 发送消息后怎么获取消息内容
  • 如何判断消息已发送成功
  • 如何设置消息的扩展字段
  • API参考