iOS IMKit SDK 开发指南

    前期准备

    注册开发者账号

    请前往融云官方网站注册开发者帐号。注册时,您需要提供真实的邮箱和手机号,以方便我们向您发送重要通知并在紧急时刻能够联系到您。如果您没有提供正确可用的邮箱和手机号,我们随时可能关闭您的应用。

    下载 SDK

    您可以到融云官方网站下载融云 SDK。

    创建应用

    注册了开发者帐号之后,在进行开发 App 之前,您需要前往融云开发者控制台创建应用。创建完应用之后,在您的应用中,会自动创建两套环境,即:开发环境和生产环境

    image
    App Key / Secret 位置

    开发环境专门用于开发测试,生产环境专门用于您的应用上线正式商用。两套环境相互隔离,每个环境都有相应的 App Key 和 App Secret。两个环境消息不互通。

    获取 Token

    Token 即用户令牌,相当于您 APP 上当前用户连接融云的身份凭证。在您连接融云服务器之前,您需要请求您的 App Server,您的 App Server 通过融云 Server API 获取 Token 并返回给您的客户端,客户端获取到这个 Token 即可连接融云服务器。

    • 为什么必须在服务器端请求 Token,客户端不提供获取 Token 的接口?

      因为获取 Token 时需要提供 App Key 和 App Secret。如果在客户端请求 Token,假如您的 App 代码一旦被反编译,则会导致您的 App Key 和 App Secret 泄露。所以,务必在您的服务器端获取 Token,查看获取 Token 流程及 Server API

    我们在开发者后台提供了 API 调试的功能,在开发初期阶段,您可以通过其中"获取 Token" 功能,手动获取 Token 进行测试。

    SDK 集成

    导入 SDK

    SDK 分类

    2.8.0 之后版本,融云提供五类 SDK:

    • 融云 IM 通讯能力库 - RongCloudIM/IMLib
    • 融云 IM 界面组件 - RongCloudIM/IMKit
    • 融云音视频通讯能力库 - RongCloudIM/CallLib
    • 融云音视频界面组件 - RongCloudIM/CallKit
    • 融云红包 - RongCloudIM/RedPacket

    2.8.0 之前版本,融云提供三类 SDK:

    • 融云 IM 界面组件 - RongCloud IMKit
    • 融云 IM 通讯能力库 - RongCloud IMLib
    • 融云 IM 音视频组件 - RongCloudIMKitWithVoip

    导入 SDK

    有两种方式可以将 SDK 导入您的项目中:

    • 通过 CocoaPods 管理依赖;
    • 手动导入 SDK 并管理依赖;

    CocoaPods 是目前最流行的 Cocoa 项目库依赖管理工具之一,考虑到便捷与项目的可维护性,我们更推荐您使用 CocoaPods 导入并管理 SDK。

    使用 CocoaPods 导入 SDK

    1、CocoaPods安装

    请参照 CocoaPods 安装

    2、 使用 CocoaPods 导入融云 SDK

    在您的工程根目录下新建一个 Podfile 文件,或者用终端,在工程目录下,执行 pod init。然后 open -e Podfile。在文件中输入以下内容。(在此以 2.8.3 版本为例,其中 “MyApp” 为自己工程名)

    target 'MyApp' do
        pod 'RongCloudIM/IMLib', '~> 2.8.3'
        pod 'RongCloudIM/IMKit', '~> 2.8.3'  
    end
    

    然后在终端中运行以下命令:

    pod install
    

    完成后,CocoaPods 会在您的工程根目录下生成一个 .xcworkspace 文件。您需要通过此文件打开您的工程,而不是之前的 .xcodeproj

    3、升级工程的 SDK 版本

    更新您工程目录中 Podfile 指定的 SDK 版本后,在终端中执行以下命令。

    pod update
    
    • 请务必保证使用的 RongCloudIM 所有模块版本号一致。
    • 关于融云 IM CocoaPods 功能模块的介绍和使用说明,可以参考说明文档
    • 如果您使用的是 2.8.0 之前的 SDK,请参考知识库文档
    • 如果搜索不到最新库,执行 pod repo update 更新一下。
    • 特殊情况下,由于网络或者别的原因,通过 CocoaPods 下载的文件可能会有问题。 这时候您可以删除 CocoaPods 的缓存(~/Library/Caches/CocoaPods/Pods/Release 目录),再次导入即可。
    • 查看当前使用的 SDK 版本,您可以在 Podfile.lock 文件中看到您工程中使用的 SDK 版本。

    手动导入 SDK

    1、SDK 文件说明

    如果您使用 IMKit,您需要将所有的文件导入您的工程中;如果您使用 IMLib,您需要导入除 RongIMKit.framework 和 RongCloud.bundle 之外的所有文件。

    文件 说明 注意事项
    RongIMKit.framework IMKit 的 framework 库 使用 IMKit 必须导入
    RongIMLib.framework IMLib 的 framework 库 使用 IMLib 与使用 IMKit 都必须导入
    libopencore-amrnb.a 第三方开源语音库 必须导入,否则语音消息将无法处理
    RongCloud.bundle 资源包,包含图片和铃声等资源 必须导入,否则界面的图片将无法显示
    Emoji.plist 表情包 必须导入,否则将无法显示表情
    zh-Hans.lproj 中文语言包 必须导入,否则界面将无法显示中文
    en.lproj 英文语言包 必须导入,否则界面有些英文将无法显示
    release_notes_ios.txt 版本变更的说明文档 仅说明,非工程必须文件
    • 融云 IM 通讯能力库 - RongCloudIM/IMLib
    • 融云 IM 界面组件 - RongCloudIM/IMKit
    • 融云音视频通讯能力库 - RongCloudIM/CallLib
    • 融云音视频界面组件 - RongCloudIM/CallKit
    • 融云红包 - RongCloudIM/RedPacket
    应用 导入
    融云 IM 通讯能力库 RongIMLib.framework
    融云 IM 界面组件 RongIMKit.frameworkRongCloud.bundleEmoji.plisten.lprojzh-Hans.lproj
    融云音视频通讯能力库 RongCallLib.frameworkAgoraRtcEngineKit.framework
    融云音视频界面组件 RongCallKit.framework
    融云红包 AlipaySDKJrmfIMLib

    2、添加系统库依赖

    您除了在工程中导入 SDK 之前,还需要添加如下系统库的引用。

    • AssetsLibrary.framework
    • AudioToolbox.framework
    • AVFoundation.framework
    • CFNetwork.framework
    • CoreAudio.framework
    • CoreGraphics.framework
    • CoreLocation.framework
    • CoreMedia.framework
    • CoreTelephony.framework
    • CoreVideo.framework
    • ImageIO.framework
    • libc++.tbd
    • libc++abi.tbd
    • libsqlite3.tbd
    • libstdc++.tbd
    • libxml2.tbd
    • libz.tbd
    • MapKit.framework
    • OpenGLES.framework
    • QuartzCore.framework
    • SystemConfiguration.framework
    • UIKit.framework
    • Photos.framework
    • SafariServices.framework

    如您集成了音视频功能,还需要在您的工程中导入 RongCallLib.framewrok、 RongCallKit.framewrok、AgoraRtcEngineKit.framework 以及系统库 CoreMotion.framework、VideoToolbox.framework、libresolv.tbd。

    如果您使用的是 Xcode 6.X 版本,则需要将上面的动态库 *.tbd 的后缀改为 *.dylib。如果您没有使用 IMKit 的所有功能,有些库可能并非必须。
    音视频 SDK CallKit 集成请参考 CallKit SDK 手动导入说明
    如您集成了红包 SDK ,请参考红包集成文档

    3、在 Xcode 项目 Build Settings -> Other Linker Flags 中,增加 "-ObjC"。

    4、设置 App 支持 HTTPS

    详细请查看 ATS 集成与设置说明

    5、查看当前使用的 SDK 版本

    手动导入的 SDK 版本,在 Folder 中打开 RongIMKit.framework 和 RongIMLib.framework,在各自的文件夹中都有一个 Info.plist 文件,打开即可看见版本号。

    image
    Info.plist 文件
    image
    SDK 版本

    视频讲解

    视频介绍如何下载和导入融云 iOS SDK ,以及解决在导入中遇见的问题。

    初始化

    在您需要使用融云 SDK 功能的类中,import 相关头文件。

    #import <RongIMKit/RongIMKit.h>
    

    如果是 Swift 的话,需要在您工程的 Bridging-Header.h 文件中加入 SDK 的引用

    #import <RongIMKit/RongIMKit.h>
    

    请使用您之前从融云开发者控制台注册得到的 App Key,通过 RCIM 的单例,传入 initWithAppKey:方法,初始化 SDK。

    您在使用融云 SDK 所有功能(包括显示 SDK 中的 View 或者显示继承于 SDK 的 View )之前,您必须先调用此方法初始化 SDK。 在 App 的整个生命周期中,您只需要将 SDK 初始化一次。

    Objective-C 代码

    [[RCIM sharedRCIM] initWithAppKey:@"YourTestAppKey"];
    

    连接服务器

    将您在上一步获取到的 Token,通过 RCIM 的单例,传入 -connectWithToken:success:error:tokenIncorrect: 方法,即可建立与服务器的连接。

    关于融云 SDK 连接的详细处理逻辑问题,请查看连接融云

    Objective-C 代码

    [[RCIM sharedRCIM] connectWithToken:@"YourTestUserToken"     success:^(NSString *userId) {
        NSLog(@"登陆成功。当前登录的用户ID:%@", userId);
    } error:^(RCConnectErrorCode status) {
        NSLog(@"登陆的错误码为:%d", status);
    } tokenIncorrect:^{
        //token过期或者不正确。
        //如果设置了token有效期并且token过期,请重新请求您的服务器获取新的token
        //如果没有设置token有效期却提示token错误,请检查您客户端和服务器的appkey是否匹配,还有检查您获取token的流程。
        NSLog(@"token错误");
    }];
    

    会话列表

    融云 IMKit 已经实现了一个默认的会话列表视图控制器,您直接使用或继承 RCConversationListViewController,即可快速启动和使用会话列表界面。

    如下面的例子,新建一个 YourTestChatViewController Class,继承于 RCConversationListViewController

    新建会话列表 View Controller

    新建一个继承于 YourTestChatViewController 的 Class,在 viewDidLoad 中设置需要显示的会话类型,需要将哪些类型的会话聚合显示。

    Objective-C 代码:

    //  YourTestChatListViewController.m
    
    #import "YourTestChatListViewController.h"
    
    @interface YourTestChatListViewController ()
    
    @end
    
    @implementation YourTestChatListViewController
    
    - (void)viewDidLoad {
        //重写显示相关的接口,必须先调用super,否则会屏蔽SDK默认的处理
        [super viewDidLoad];
    
          //设置需要显示哪些类型的会话
          [self setDisplayConversationTypes:@[@(ConversationType_PRIVATE),
                                              @(ConversationType_DISCUSSION),
                                              @(ConversationType_CHATROOM),
                                              @(ConversationType_GROUP),
                                              @(ConversationType_APPSERVICE),
                                              @(ConversationType_SYSTEM)]];
          //设置需要将哪些类型的会话在会话列表中聚合显示
          [self setCollectionConversationType:@[@(ConversationType_DISCUSSION),
                                                @(ConversationType_GROUP)]];
    }
    
    @end
    
    您需要设置在会话列表界面显示哪些类型的会话,以及将哪些类型的会话在会话列表中聚合显示。
    聚合显示指的是此类型所有会话,在会话列表中聚合显示成一条消息,点击进去会显示此类型的具体会话列表。

    初始化会话列表界面并显示

    生成会话列表 View Controller 对象,并显示。

    Objective-C 代码:

    YourTestChatListViewController *chatList = [[YourTestChatListViewController alloc] init];
    [self.navigationController pushViewController:chatList animated:YES];
    

    点击会话列表,进入聊天会话界面

    在您的会话列表 View Controller 中加入以下代码,即可点击进入聊天会话界面。

    Objective-C 代码:

    //重写RCConversationListViewController的onSelectedTableRow事件
    - (void)onSelectedTableRow:(RCConversationModelType)conversationModelType
             conversationModel:(RCConversationModel *)model
                   atIndexPath:(NSIndexPath *)indexPath {
        RCConversationViewController *conversationVC = [[RCConversationViewController alloc]init];
        conversationVC.conversationType = model.conversationType;
        conversationVC.targetId = model.targetId;
        conversationVC.title = @"想显示的会话标题";
        [self.navigationController pushViewController:conversationVC animated:YES];
    }
    

    视频讲解

    一、介绍如何使用融云 iOS IMKit SDK 会话列表功能。

    二、介绍会话列表中其他可实现的功能,如:设置在会话列表中显示的会话类型、会话类型聚合、头像和昵称的显示、昵称的字体颜色、会话列表 cell 的背景色等。

    会话页面

    融云 IMKit 中已经实现了完整的会话页面,包含发送、接收、更新等 UI,并覆盖常用的 IM 交互场景,您直接使用或继承 RCConversationViewController,即可快速启动和使用会话页面。

    如下面的例子,创建一个 RCConversationViewController 对象并设置好会话类型、目标会话 ID,显示即可进行聊天。

    Objective-C 代码

    //新建一个聊天会话View Controller对象,建议这样初始化
    RCConversationViewController *chat = [RCConversationViewController alloc] initWithConversationType:conversationType
                        targetId:targetId];
    
    //设置会话的类型,如单聊、讨论组、群聊、聊天室、客服、公众服务会话等
    chat.conversationType = ConversationType_PRIVATE;
    //设置会话的目标会话ID。(单聊、客服、公众服务会话为对方的ID,讨论组、群聊、聊天室为会话的ID)
    chat.targetId = @"targetIdYouWillChatIn";
    
    //设置聊天会话界面要显示的标题
    chat.title = @"想显示的会话标题";
    //显示聊天会话界面
    [self.navigationController pushViewController:chat animated:YES];
    
    启动会话页面之后,您可以通过下方的输入框和扩展功能模块,直接发送文本、语音、图片、位置等消息。

    :在启动会话页面前,需要保证 conversationType 和 targetId 已经赋值,否则可能会导致页面无法及时取到消息刷新。

    视频讲解

    一、如何打开一个聊天界面,如:点击一个 Button,跳转到一个最简单的单聊页面。

    二、如何设置聊天界面的背景色、背景图,修改文本消息的字体颜色等功能。

    断开连接

    在断开与融云服务器的连接时,您可以设置是否仍接收远程推送。

    如果设置了接收推送,断开连接之后,如果有人给该用户 ID 发送消息,融云的服务器会根据 deviceToken 和推送证书将消息发送到苹果推送服务器,苹果服务器会将该消息以远程推送的形式下发到客户端。

    我们针对断开连接之后是否接收远程推送,提供了以下三个接口,根据您的需求,调用其中一个即可。

    // RCIM Class
    
    /*!
     断开与融云服务器的连接
    
     @param isReceivePush   App在断开连接之后,是否还接收远程推送
    
     @discussion 因为SDK在前后台切换或者网络出现异常都会自动重连,会保证连接的可靠性。
     所以除非您的App逻辑需要登出,否则一般不需要调用此方法进行手动断开。
    
     @warning 如果您使用IMKit,请使用此方法断开与融云服务器的连接;
     如果您使用IMLib,请使用RCIMClient中的同名方法断开与融云服务器的连接,而不要使用此方法。
    
     isReceivePush指断开与融云服务器的连接之后,是否还接收远程推送。
     [[RCIM sharedRCIM] disconnect:YES]与[[RCIM sharedRCIM] disconnect]完全一致;
     [[RCIM sharedRCIM] disconnect:NO]与[[RCIM sharedRCIM] logout]完全一致。
     您只需要按照您的需求,使用disconnect:与disconnect以及logout三个接口其中一个即可。
     */
    - (void)disconnect:(BOOL)isReceivePush;
    
    /*!
     断开与融云服务器的连接,但仍然接收远程推送
    
     @discussion 因为SDK在前后台切换或者网络出现异常都会自动重连,会保证连接的可靠性。
     所以除非您的App逻辑需要登出,否则一般不需要调用此方法进行手动断开。
    
     @warning 如果您使用IMKit,请使用此方法断开与融云服务器的连接;
     如果您使用IMLib,请使用RCIMClient中的同名方法断开与融云服务器的连接,而不要使用此方法。
    
     [[RCIM sharedRCIM] disconnect:YES]与[[RCIM sharedRCIM] disconnect]完全一致;
     [[RCIM sharedRCIM] disconnect:NO]与[[RCIM sharedRCIM] logout]完全一致。
     您只需要按照您的需求,使用disconnect:与disconnect以及logout三个接口其中一个即可。
     */
    - (void)disconnect;
    
    /*!
     断开与融云服务器的连接,并不再接收远程推送
    
     @discussion 因为SDK在前后台切换或者网络出现异常都会自动重连,会保证连接的可靠性。
     所以除非您的App逻辑需要登出,否则一般不需要调用此方法进行手动断开。
    
     @warning 如果您使用IMKit,请使用此方法断开与融云服务器的连接;
     如果您使用IMLib,请使用RCIMClient中的同名方法断开与融云服务器的连接,而不要使用此方法。
    
     [[RCIM sharedRCIM] disconnect:YES]与[[RCIM sharedRCIM] disconnect]完全一致;
     [[RCIM sharedRCIM] disconnect:NO]与[[RCIM sharedRCIM] logout]完全一致。
     您只需要按照您的需求,使用disconnect:与disconnect以及logout三个接口其中一个即可。
     */
    - (void)logout;
    

    连接融云

    您在调用融云连接的方法的时候需要注意,connectWithToken 这个方法中的回调 success 、error 和 tokenIncorrect 三个只会回调其中的一个,且只会回调一次。

    您在调用完成这个方法之后如果回调的是 tokenIncorrect,那么您需要请求您的服务器重新获取 Token 并调用 connectWithToken 来连接(但是注意避免无限循环,以免影响 App 用户体验)。

    如果 connectWithToken 回调的是 error,那么需要判断 RCConnectErrorCode,开发者仅需要关注以下几种连接错误码,其余错误码 SDK 均会进行自动重连,开发者无须处理。

    您需要监听融云的链接来获取 SDK 的最新连接状态然后做出处理。

    如果您使用 IMKit,请使用 RCIM 中的连接状态监听 connectionStatusDelegate ,而不要设置 RCIMClient 的监听,否则将屏蔽 IMKit 中的 UI 刷新(比如连接状态的显示变更等)。
    如果使用的 IMLib 通过 setRCConnectionStatusChangeDelegate 方法来设置连接状态的监听。
    1. App Key 错误,请检查您使用的 App Key 是否正确。

      RC_CONN_ID_REJECT = 31002
      
    2. Token 无效

      RC_CONN_TOKEN_INCORRECT = 31004
      

      Token 无效一般有以下两种原因。

      1、Token 错误,请您检查客户端初始化使用的 App Key 和您服务器获取 Token 使用的 App Key 是否一致;

      2、Token 过期,是因为您在开发者后台设置了 Token 过期时间,您需要请求您的服务器重新获取 Token 并再次用新的 Token 建立连接。

    3. BundleID 不正确

      RC_CONN_PACKAGE_NAME_INVALID = 31007
      

      请检查您 App 的 BundleID 是否正确。

    4. App Key 被封禁或已删除

      RC_CONN_APP_BLOCKED_OR_DELETED = 31008
      

      请检查您使用的 App Key 是否正确。

    5. 用户被封禁

      RC_CONN_USER_BLOCKED = 31009
      

      请检查您使用的 Token 是否正确,以及对应的 UserId 是否被封禁。

    6. 当前用户在其他设备上登录,此设备被踢下线

      RC_DISCONN_KICK = 31010
      
    7. SDK 没有初始化

      RC_CLIENT_NOT_INIT = 33001
      

      在使用SDK任何功能之前,必须先 Init。

    8. 开发者接口调用时传入的参数错误

      RC_INVALID_PARAMETER = 33003
      

      请检查接口调用时传入的参数类型和值。

    9. 开发者接口调用时传入的参数错误

      RC_INVALID_ARGUMENT = -1000
      

      请检查接口调用时传入的参数类型和值。

    /*!
     与融云服务器建立连接
    
     @param token                   从您服务器端获取的token(用户身份令牌)
     @param successBlock            连接建立成功的回调
     [userId:当前连接成功所用的用户ID]
     @param errorBlock              连接建立失败的回调 [status:连接失败的错误码]
     @param tokenIncorrectBlock     token错误或者过期的回调
    
     @discussion 在App整个生命周期,您只需要调用一次此方法与融云服务器建立连接。
     之后无论是网络出现异常或者App有前后台的切换等,SDK都会负责自动重连。
     除非您已经手动将连接断开,否则您不需要自己再手动重连。
    
     tokenIncorrectBlock有两种情况:
     一是token错误,请您检查客户端初始化使用的AppKey和您服务器获取token使用的AppKey是否一致;
     二是token过期,是因为您在开发者后台设置了token过期时间,您需要请求您的服务器重新获取token并再次用新的token建立连接。
    
     @warning 如果您使用IMLib,请使用此方法建立与融云服务器的连接;
     如果您使用IMKit,请使用RCIM中的同名方法建立与融云服务器的连接,而不要使用此方法。
    
     在tokenIncorrectBlock的情况下,您需要请求您的服务器重新获取token并建立连接,但是注意避免无限循环,以免影响App用户体验。
    
     此方法的回调并非为原调用线程,您如果需要进行UI操作,请注意切换到主线程。
     */
    - (void)connectWithToken:(NSString *)token
                     success:(void (^)(NSString *userId))successBlock
                       error:(void (^)(RCConnectErrorCode status))errorBlock
              tokenIncorrect:(void (^)())tokenIncorrectBlock;
    

    用户信息

    设计原理

    融云认为,每一个设计良好且功能健全的 App 都应该能够在本地获取、缓存并在合适的时机更新 App 中的用户信息。所以,融云不维护和管理用户基本信息(用户ID、昵称、头像)的获取、缓存、变更和同步。

    此外,App 提供用户信息也避免了由于缓存导致的用户信息更新不及时,App 中不同界面上的用户信息不统一(比如:App 的一部分界面从 App 服务器上获取用户信息并显示,一部分界面从融云服务器获取用户信息并显示,容易造成信息不同步),能够获得最佳的用户体验。

    融云 IMKit 设计了用户信息提供者、群组信息提供者和群名片信息提供者,App 只需要实现该协议并提供正确的数据,IMKit 会在会话列表或会话页面需要展示用户头像和昵称的时候,去调用对应协议的代理函数,即可显示用户和群组的名称与头像。

    // RCIM Class
    
    /*!
     用户信息提供者
     @discussion SDK需要通过您实现的用户信息提供者,获取用户信息并显示。
     */
    @protocol RCIMUserInfoDataSource <NSObject>
    
    /*!
     获取用户信息
     @param userId                  用户ID
     @param completion              获取用户信息完成之后需要执行的Block
     @param userInfo(in completion) 该用户ID对应的用户信息
     @discussion SDK通过此方法获取用户信息并显示,请在completion中返回该用户ID对应的用户信息。
     在您设置了用户信息提供者之后,SDK在需要显示用户信息的时候,会调用此方法,向您请求用户信息用于显示。
     */
    - (void)getUserInfoWithUserId:(NSString *)userId
                       completion:(void (^)(RCUserInfo *userInfo))completion;
    
    @end
    
    /*!
     群组信息提供者
     @discussion SDK需要通过您实现的群组信息提供者,获取群组信息并显示。
     */
    @protocol RCIMGroupInfoDataSource <NSObject>
    /*!
     获取群组信息
    
     @param groupId                     群组ID
     @param completion                  获取群组信息完成之后需要执行的Block
     @param groupInfo(in completion)    该群组ID对应的群组信息
     @discussion SDK通过此方法获取用户信息并显示,请在completion的block中返回该用户ID对应的用户信息。
     在您设置了用户信息提供者之后,SDK在需要显示用户信息的时候,会调用此方法,向您请求用户信息用于显示。
     */
    - (void)getGroupInfoWithGroupId:(NSString *)groupId
                         completion:(void (^)(RCGroup *groupInfo))completion;
    
    @end
    
    /*!
     群名片信息提供者
    
     @discussion 如果您使用了群名片功能,SDK需要通过您实现的群名片信息提供者,获取用户在群组中的名片信息并显示。
     */
    @protocol RCIMGroupUserInfoDataSource <NSObject>
    
    /*!
     获取用户在群组中的群名片信息
     @param userId          用户ID
     @param groupId         群组ID
     @param completion      获取群名片信息完成之后需要执行的Block [userInfo:该用户ID在群组中对应的群名片信息]
     @discussion 如果您使用了群名片功能,SDK需要通过您实现的群名片信息提供者,获取用户在群组中的名片信息并显示。
     */
    - (void)getUserInfoWithUserId:(NSString *)userId
                          inGroup:(NSString *)groupId
                       completion:(void (^)(RCUserInfo *userInfo))completion;
    
    @end
    
    /*!
     用户信息提供者
     @discussion SDK需要通过您实现的用户信息提供者,获取用户信息并显示。
     */
    @property(nonatomic, weak) id<RCIMUserInfoDataSource> userInfoDataSource;
    /*!
     群组信息提供者
     @discussion SDK需要通过您实现的群组信息提供者,获取群组信息并显示。
     */
    @property(nonatomic, weak) id<RCIMGroupInfoDataSource> groupInfoDataSource;
    /*!
     群名片信息提供者
    
     @discussion 如果您使用了群名片功能,SDK需要通过您实现的群名片信息提供者,获取用户在群组中的名片信息并显示。
     */
    @property(nonatomic, weak) id<RCIMGroupUserInfoDataSource> groupUserInfoDataSource;
    

    实现流程

    用户信息提供者

    image

    群组信息提供者

    image

    群名片信息提供者

    image

    设置当前登录用户信息

    //RCIM Class
    
    /*!
     当前登录的用户的用户信息
    
     @discussion 与融云服务器建立连接之后,应该设置当前用户的用户信息,用于SDK显示和发送。
     */
    @property(nonatomic, strong) RCUserInfo *currentUserInfo;
    

    如果使用聊天室会话,建议使用在消息中携带用户信息,请参考知识库

    SDK 缓存操作

    1、刷新 SDK 缓存

    当用户信息或者群组信息发生变化之后,可以调用 RCIM 中的 -refreshUserInfoCache:withUserId:-refreshGroupInfoCache:withGroupId: 方法通知 IMKit 更新缓存

    //RCIM Class
    
    /*!
     更新SDK中的用户信息缓存
    
     @param userInfo     需要更新的用户信息
     @param userId       需要更新的用户ID
    
     @discussion 使用此方法,可以更新SDK缓存的用户信息。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新该用户的显示信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)refreshUserInfoCache:(RCUserInfo *)userInfo
                     withUserId:(NSString *)userId;
    
    /*!
     更新SDK中的群组信息缓存
    
     @param groupInfo   需要更新的群组信息
     @param groupId     需要更新的群组ID
    
     @discussion 使用此方法,可以更新SDK缓存的群组信息。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新该群组的显示信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)refreshGroupInfoCache:(RCGroup *)groupInfo
                     withGroupId:(NSString *)groupId;
    
    /*!
     更新SDK中的群名片信息缓存
    
     @param userInfo     需要更新的用户信息
     @param userId       需要更新的用户ID
     @param groupId      需要更新群名片信息的群组ID
    
     @discussion 使用此方法,可以更新SDK缓存的群名片信息。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新该群名片的显示信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)refreshGroupUserInfoCache:(RCUserInfo *)userInfo
                           withUserId:(NSString *)userId
                          withGroupId:(NSString *)groupId;
    

    2、获取信息

    当需要查询用户信息或者群组信息的时候,可以调用 RCIM 中的 -getUserInfoCache:-getGroupInfoCache: 方法从缓存中获取。

    //RCIM Class
    
    /*!
     获取SDK中缓存的用户信息
    
     @param userId  用户ID
     @return        SDK中缓存的用户信息
     */
    - (RCUserInfo *)getUserInfoCache:(NSString *)userId;
    
    /*!
     获取SDK中缓存的群组信息
    
     @param groupId     群组ID
     @return            SDK中缓存的群组信息
     */
    - (RCGroup *)getGroupInfoCache:(NSString *)groupId;
    
    /*!
     获取SDK中缓存的群名片信息
    
     @param userId      用户ID
     @param groupId     群组ID
     @return            群名片信息
     */
    - (RCUserInfo *)getGroupUserInfoCache:(NSString *)userId
                              withGroupId:(NSString *)groupId;
    

    3、清除缓存数据

    当需要清除用户信息或者群组信息的时候,可以调用 RCIM 中的 -clearUserInfoCache-clearGroupInfoCache 方法把用户信息从缓存中清除。

    //RCIM Class
    
    /*!
     清空SDK中所有的用户信息缓存
    
     @discussion 使用此方法,会清空SDK中所有的用户信息缓存。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新所显示的用户信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)clearUserInfoCache;
    
    /*!
     清空SDK中所有的群组信息缓存
    
     @discussion 使用此方法,会清空SDK中所有的群组信息缓存。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新所显示的群组信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)clearGroupInfoCache;
    
    /*!
     清空SDK中所有的群名片信息缓存
    
     @discussion 使用此方法,会清空SDK中所有的群名片信息缓存。
     但是处于性能和使用场景权衡,SDK不会在当前View立即自动刷新(会在切换到其他View的时候再刷新所显示的群名片信息)。
     如果您想立即刷新,您可以在会话列表或者会话页面reload强制刷新。
     */
    - (void)clearGroupUserInfoCache;
    

    4、持久化保存

    当需要将用户信息或者群组信息在本地持久化保存的时候,可以调用 RCIM 中的 -enablePersistentUserInfoCache 方法。

    //RCIM Class
    
    /*!
     是否将用户信息和群组信息在本地持久化存储,默认值为NO
    
     @discussion 如果设置为NO,则SDK在需要显示用户信息时,会调用用户信息提供者获取用户信息并缓存到Cache,此Cache在App生命周期结束时会被移除,下次启动时会再次通过用户信息提供者获取信息。
     如果设置为YES,则会将获取到的用户信息持久化存储在本地,App下次启动时Cache会仍然有效。
     */
    @property(nonatomic, assign) BOOL enablePersistentUserInfoCache;
    

    常见问题

    1. 如何在消息中携带用户信息。

      建议在使用聊天室会话的时候,在消息中携带用户信息,可以参考知识库实现

    2. 用户信息提供者、群组信息提供者和群名片信息提供者代理函数应该在哪里实现。

      建议:创建一个单例来实现这三个代理函数,可以参考参考 Demo 中 RCDRCIMDataSource 类。

      在 Appdelegate 中,把对应的代理设置给这个单例。

    //AppDelegate Class
    
    //设置用户信息源和群组信息源
      [RCIM sharedRCIM].userInfoDataSource = RCDDataSource;
      [RCIM sharedRCIM].groupInfoDataSource = RCDDataSource;
    

    基础功能

    SDK 默认的会话类型有单聊、讨论组、群聊、聊天室、系统会话、应用内订阅号服务、跨应用订阅号服务、推送服务等类型。

    /*!
     会话类型
     */
    typedef NS_ENUM(NSUInteger, RCConversationType) {
      /*!
       单聊
       */
      ConversationType_PRIVATE = 1,
    
      /*!
       讨论组
       */
      ConversationType_DISCUSSION = 2,
    
      /*!
       群组
       */
      ConversationType_GROUP = 3,
    
      /*!
       聊天室
       */
      ConversationType_CHATROOM = 4,
    
      /*!
       客服
       */
      ConversationType_CUSTOMERSERVICE = 5,
    
      /*!
       系统会话
       */
      ConversationType_SYSTEM = 6,
    
      /*!
       应用内公众服务会话
    
       @discussion
       客服2.0使用应用内公众服务会话(ConversationType_APPSERVICE)的方式实现。
       即客服2.0会话是其中一个应用内公众服务会话,这种方式我们目前不推荐,请尽快升级到新客服,升级方法请参考官网的客服文档。
       */
      ConversationType_APPSERVICE = 7,
    
      /*!
       跨应用公众服务会话
       */
      ConversationType_PUBLICSERVICE = 8,
    
      /*!
       推送服务会话
       */
      ConversationType_PUSHSERVICE = 9
    };
    

    单聊

    单聊是最基本的聊天界面,提供文字、表情、语音片段、图片、VoIP 等多种输入内容,解决 App 内用户的沟通瓶颈。单聊的 conversationType 是 ConversationType_PRIVATE,targetId 是单聊对象的 userId。

    讨论组

    指两个以上用户一起进行聊天,用户可以自行添加好友生成一个讨论组聊天,会话关系由融云负责建立并保持,退出聊天界面或者离线后可以收到推送通知,同一个用户最多可加入 500 个讨论组,讨论组不能解散。讨论组的 conversationType 是 ConversationType_DISCUSSION,targetId 是创建讨论组成功后融云生成返回的。

    创建讨论组

    // RCIMClient Class
    
    /*!
     创建讨论组
    
     @param name                        讨论组名称
     @param userIdList                  用户ID的列表
     @param successBlock                创建讨论组成功的回调
     @param discussion(in successBlock) 创建成功返回的讨论组对象
     @param errorBlock                  创建讨论组失败的回调
     @param status(in errorBlock)       创建失败的错误码
     */
    - (void)createDiscussion:(NSString *)name
                  userIdList:(NSArray *)userIdList
                     success:(void (^)(RCDiscussion *discussion))successBlock
                       error:(void (^)(RCErrorCode status))errorBlock;
    
    // RCIMClient Class
    
    /*!
     讨论组加人,将用户加入讨论组
    
     @param discussionId                讨论组ID
     @param userIdList                  需要加入的用户ID列表
     @param successBlock                讨论组加人成功的回调
     @param discussion(in successBlock) 讨论组加人成功返回的讨论组对象
     @param errorBlock                  讨论组加人失败的回调
     @param status(in errorBlock)       讨论组加人失败的错误码
    
     @discussion 设置的讨论组名称长度不能超过40个字符,否则将会截断为前40个字符。
     */
    - (void)addMemberToDiscussion:(NSString *)discussionId
                       userIdList:(NSArray *)userIdList
                          success:(void (^)(RCDiscussion *discussion))successBlock
                            error:(void (^)(RCErrorCode status))errorBlock;
    
    // RCIMClient Class
    
    /*!
     讨论组踢人,将用户移出讨论组
    
     @param discussionId                讨论组ID
     @param userId                      需要移出的用户ID
     @param successBlock                讨论组踢人成功的回调
     @param discussion(in successBlock) 讨论组踢人成功返回的讨论组对象
     @param errorBlock                  讨论组踢人失败的回调
     @param status(in errorBlock)       讨论组踢人失败的错误码
    
     @discussion 如果当前登陆用户不是此讨论组的创建者并且此讨论组没有开放加人权限,则会返回错误。
    
     @warning 不能使用此接口将自己移除,否则会返回错误。
     如果您需要退出该讨论组,可以使用-quitDiscussion:success:error:方法。
     */
    - (void)removeMemberFromDiscussion:(NSString *)discussionId
                                userId:(NSString *)userId
                               success:(void (^)(RCDiscussion *discussion))successBlock
                                 error:(void (^)(RCErrorCode status))errorBlock;
    

    退出讨论组

    // RCIMClient Class
    
    /*!
     退出当前讨论组
    
     @param discussionId                讨论组ID
     @param successBlock                退出成功的回调
     @param discussion(in successBlock) 退出成功返回的讨论组对象
     @param errorBlock                  退出失败的回调
     @param status(in errorBlock)       退出失败的错误码
     */
    - (void)quitDiscussion:(NSString *)discussionId
                   success:(void (^)(RCDiscussion *discussion))successBlock
                     error:(void (^)(RCErrorCode status))errorBlock;
    

    获取讨论组信息

    // RCIMClient Class
    
    /*!
     获取讨论组的信息
    
     @param targetId                    需要获取信息的讨论组ID
     @param successBlock                获取讨论组信息成功的回调
     @param discussion(in successBlock) 获取的讨论组信息
     @param errorBlock                  获取讨论组信息失败的回调
     @param status(in errorBlock)       获取讨论组信息失败的错误码
     */
    - (void)getDiscussion:(NSString *)discussionId
                  success:(void (^)(RCDiscussion *discussion))successBlock
                    error:(void (^)(RCErrorCode status))errorBlock;
    

    设置讨论组名称

    // RCIMClient Class
    
    /*!
     设置讨论组名称
    
     @param targetId                需要设置的讨论组ID
     @param discussionName          需要设置的讨论组名称,discussionName长度<=40
     @param successBlock            设置成功的回调
     @param errorBlock              设置失败的回调
     @param status(in errorBlock)   设置失败的错误码
    
     @discussion 设置的讨论组名称长度不能超过40个字符,否则将会截断为前40个字符。
     */
    - (void)setDiscussionName:(NSString *)targetId
                         name:(NSString *)discussionName
                      success:(void (^)())successBlock
                        error:(void (^)(RCErrorCode status))errorBlock;
    

    设置讨论组加人权限

    // RCIMClient Class
    
    /*!
     设置讨论组是否开放加人权限
    
     @param targetId                    讨论组ID
     @param isOpen                      是否开放加人权限
     @param discussion(in successBlock) 设置成功的回调
     @param errorBlock                  设置失败的回调
     @param status(in errorBlock)       设置失败的错误码
    
     @discussion 讨论组默认开放加人权限,即所有成员都可以加人。
     如果关闭加人权限之后,只有讨论组的创建者有加人权限。
     */
    - (void)setDiscussionInviteStatus:(NSString *)targetId
                               isOpen:(BOOL)isOpen
                              success:(void (^)())successBlock
                                error:(void (^)(RCErrorCode status))errorBlock;
    

    讨论组的成员关系由融云服务器维护,但是您需要实现用户信息提供者并返回正确的信息,以保证用户名称和头像能正常显示。

    群组

    群组关系和群组列表由您的 App 维护,客户端的所有群组操作都需要请求您的 App Server, 您的 App Server 可以根据自己的逻辑进行管理和控制,然后通过 Server API 接口进行群组操作,并将结果返回给客户端。群组的 conversationType 是 ConversationType_GROUP,targetId 是您在调用 Server API 创建群时自己指定的群组 Id。

    详请见 Server API 群组服务接口

    以下展示了客户端进行群组操作的流程:

    创建群组

    App -> App Server: App 向自己应用服务器发起创建群组请求。 App Server -> RongCloud Server: 授权成功后,在融云服务端同步创建群组。\n /group/create RongCloud Server -> App Server: 创建成功,返回状态。 App Server -> App: 创建成功,可以发送群组信息。

    加入群组

    App -> App Server: App 向自己应用服务器发起加入群组请求。 App Server -> RongCloud Server: 授权成功后,调用融云服务端加入群组接口。\n /group/join RongCloud Server -> App Server: 加入成功,返回状态。 App Server -> App: 加入成功,用户可在群组中发送信息。

    退出群组

    App -> App Server: App 向自己应用服务器发起退出群组请求。 App Server -> RongCloud Server: 授权成功后,调用融云服务端退出群组接口。\n /group/quit RongCloud Server -> App Server: 退出成功,返回状态。 App Server -> App: 退出群组,用户不会再收到此群组信息。

    解散群组

    App -> App Server: App 向自己应用服务器发起解散群组请求。 App Server -> RongCloud Server: 授权成功后,调用融云服务端解散群组接口。\n /group/dismiss RongCloud Server -> App Server: 解散成功,返回状态。 App Server -> App: 成功解散群组。

    设置群组信息

    App -> App Server: App 向自己应用服务器发起设置群组信息请求。 App Server -> RongCloud Server: 授权成功后,调用融云服务端设置群组信息接口。\n /group/refresh RongCloud Server -> App Server: 设置成功,返回状态。 App Server -> App: 群组信息设置成功。

    获取群组成员列表

    App -> App Server: App 向自己应用服务器发起查询群组成员请求。 App Server -> App: 成功,返回成员信息。

    获取群组列表

    App -> RongCloud Server: 连接融云服务器。 connectWithToken RongCloud Server -> App: 连接成功,返回状态信息。 App --> App Server: 请求获取群组列表 App Server -> RongCloud Server: 如有变更,需要向融云服务端同步群组信息。 RongCloud Server -> App Server: 成功,返回状态信息。 App Server --> App: 成功,返回群组列表。

    获取群组信息

    请参考用户信息中,群组信息提供者相关内容。

    聊天室

    聊天室业务的描述,请参见新手指南中的说明。聊天室的 conversationType 是 ConversationType_CHATROOM,targetId 是您在创建或者加入聊天室时自己指定的。

    聊天室的消息没有 Push 通知,没有人数限制,退出后不再接收消息并清空本地消息记录。IMKit 中的会话页面 RCConversationViewController 已经内置了加入和退出聊天室的接口调用,您直接启动即可。

    加入聊天室:

    IMKit 是在会话页面 viewDidLoad 的时候加入聊天室的, 加入聊天室的时候,默认会获取之前的 10 条聊天历史记录,您可以在 viewDidLoad 之前设置加入聊天室所需要获取的历史消息数量,RCConversationViewController 里面有下面属性可以设置:

    /*!
     如果当前会话类型为聊天室时,进入聊天室所获取的历史消息数量,默认值为10。
    
     @discussion 需要在viewDidLoad之前进行设置。
     -1表示不获取任何历史消息,0表示不特殊设置而使用SDK默认的设置(默认为获取10条),0<messageCount<=50为具体获取的消息数量,最大值为50。注:如果是7.x系统获取历史消息数量不要大于30
     */
    @property(nonatomic, assign) int defaultHistoryMessageCountOfChatRoom;
    

    退出聊天室:

    会话页面在 leftBarButtonItemPressed 时会默认退出聊天室。您需要在会话界面退出时,父类调用一下 RCConversationViewController 的 leftBarButtonItemPressed 方法。

    如果您是自定义聊天室页面的,IMLib 的 RCIMClient 里面有以下关于聊天室的操作方法:

    /*!
     加入聊天室(如果聊天室不存在则会创建)
    
     @param targetId                聊天室ID
     @param messageCount 进入聊天室时获取历史消息的数量,-1<=messageCount<=50
     @param successBlock            加入聊天室成功的回调
     @param errorBlock              加入聊天室失败的回调
     [status:加入聊天室失败的错误码]
    
     @discussion
     可以通过传入的messageCount设置加入聊天室成功之后,需要获取的历史消息数量。
     -1表示不获取任何历史消息,0表示不特殊设置而使用SDK默认的设置(默认为获取10条),0<messageCount<=50为具体获取的消息数量,最大值为50。注:如果是7.x系统获取历史消息数量不要大于30
     */
    - (void)joinChatRoom:(NSString *)targetId
            messageCount:(int)messageCount
                 success:(void (^)())successBlock
                   error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     加入已经存在的聊天室(如果不存在或超限会返回聊天室不存在错误23410 或 人数超限
     23411)
    
     @param targetId                聊天室ID
     @param messageCount 进入聊天室时获取历史消息的数量,-1<=messageCount<=50
     @param successBlock            加入聊天室成功的回调
     @param errorBlock              加入聊天室失败的回调
     [status:加入聊天室失败的错误码]
    
     @warning
     注意:使用Kit库的会话页面viewDidLoad会自动调用joinChatRoom加入聊天室(聊天室不存在会自动创建),
     如果您只想加入已存在的聊天室,需要在push到会话页面之前调用这个方法并且messageCount
     传-1,成功之后push到会话页面,失败需要您做相应提示处理
    
     @discussion
     可以通过传入的messageCount设置加入聊天室成功之后,需要获取的历史消息数量。
     -1表示不获取任何历史消息,0表示不特殊设置而使用SDK默认的设置(默认为获取10条),0<messageCount<=50为具体获取的消息数量,最大值为50。
     */
    - (void)joinExistChatRoom:(NSString *)targetId
                 messageCount:(int)messageCount
                      success:(void (^)())successBlock
                        error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     退出聊天室
    
     @param targetId                聊天室ID
     @param successBlock            退出聊天室成功的回调
     @param errorBlock              退出聊天室失败的回调
     [status:退出聊天室失败的错误码]
     */
    - (void)quitChatRoom:(NSString *)targetId
                 success:(void (^)())successBlock
                   error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     获取聊天室的信息(包含部分成员信息和当前聊天室中的成员总数)
    
     @param targetId     聊天室ID
     @param count 需要获取的成员信息的数量(目前获取到的聊天室信息中仅包含不多于20人的成员信息,即0 <= count <= 20,传入0获取到的聊天室信息将或仅包含成员总数,不包含具体的成员列表)
     @param order        需要获取的成员列表的顺序(最早加入或是最晚加入的部分成员)
     @param successBlock 获取成功的回调 [chatRoomInfo:聊天室信息]
     @param errorBlock   获取失败的回调 [status:获取失败的错误码]
    
     @discussion 因为聊天室一般成员数量巨大,权衡效率和用户体验,目前返回的聊天室信息仅包含不多于20人的成员信息和当前成员总数。如果您使用RC_ChatRoom_Member_Asc升序方式查询,将返回最早加入的成员信息列表,按加入时间从旧到新排列;如果您使用RC_ChatRoom_Member_Desc降序方式查询,将返回最晚加入的成员信息列表,按加入时间从新到旧排列。
     */
    - (void)getChatRoomInfo:(NSString *)targetId
                      count:(int)count
                      order:(RCChatRoomMemberOrder)order
                    success:(void (^)(RCChatRoomInfo *chatRoomInfo))successBlock
                      error:(void (^)(RCErrorCode status))errorBlock;
    

    1. 融云默认一个用户同时只能加入一个聊天室;

    2. 一个聊天室一个小时内没有人发送消息或者加入,聊天室会被自动销毁。

    系统会话

    不能从客户端发起系统会话,只能通过您的服务器来发起。系统会话的 conversationType 为 ConversationType_SYSTEM。

    客服

    IMKit 中提供封装好的客服功能和 UI 界面,轻松的几行代码就能完成集成,为应用中用户提供优质的客服服务。

    请参考融云 SealTalk 开源项目中,代码 RCDCustomerServiceViewController 来实现您的客服。 开发者后台客服使用说明,详细请参见客服使用文档

    启动客服

    拷贝 RCDCustomerServiceViewController 到您的工程中去,使用如下代码启动您的客服界面,请注意填写正确的客服 id ,客服的会话类型是 ConversationType_CUSTOMERSERVICE。

    RCDCustomerServiceViewController *chatService = [[RCDCustomerServiceViewController alloc] init];
    #define SERVICE_ID @"您在融云后台开通的客服ID"
    chatService.conversationType = ConversationType_CUSTOMERSERVICE;
    chatService.targetId = SERVICE_ID;
    chatService.title = @"客服";
    chatService.csInfo = csInfo; //用户的详细信息,此数据用于上传用户信息到客服后台,数据的nickName和portraitUrl必须填写。(目前该字段暂时没用到,客服后台显示的用户信息是你获取token时传的参数,之后会用到)
    [self.navigationController pushViewController :chatService animated:YES];
    

    挂断客服

    默认退出客服会话页面即挂断客服,如果不想在退出页面时挂断客服,在 IMLib 的 RCConfig.plist 配置文件中,修改下面字段为 false。

    <key>CustomerService</key>
        <dict>
            <key>SuspendWhenLeave</key>
            <true/>
        </dict>
    

    评价客服

    在客服页面停留一分钟后退出会自动弹出客服评价,若退出时是人工模式,默认评价弹出框样式(左图), 若退出时是机器人模式,默认评价弹出框样式(右图)

    image
    评价客服

    如果需要自定义评价界面,请根据 Demo 的 RCDCustomerServiceViewController 中的示例来自定义。

    公众号

    IMKit 组件中已经内置了获取已关注公众号列表及搜索、打开公众服务会话界面、订阅和取消订阅公众号的接口调用,您直接启动即可(申请上线通过后才能使用)。

    融云公众服务是为应用开发者和公众帐号运营者提供的连接服务产品,通过融云公众服务,App 可以具备为自己的用户提供公众帐号服务的能力和资源。

    公众服务包括:应用公众服务和公众服务平台。

    • 应用公众服务:是为应用开发者提供的 App 内建公众服务能力,通过在融云开发者站点创建 App 公众号,实现应用内的公众服务。

    • 公众服务平台:是在应用开发者和公众帐号运营者之间建立的对接平台,应用开发者可以通过平台引入公众服务资源,帮助 App 快速覆盖用户需求,公众帐号持有者通过平台可以有机会向所有集成融云 SDK 的 App 提供服务,进而获得更加精准更加丰富的受众渠道。

    开发者可在融云开发者平台的公众服务模块中,通过添加公众服务或应用公众服务中的公众号到自己的应用中(应用上线之后才能添加公众号)。

    IMKit 组件中已经内置了订阅和取消订阅公众号的接口调用,您直接启动即可:

    // 启动已关注的公众号列表功能
    RCPublicServiceListViewController *publicServiceVC = [[RCPublicServiceListViewController alloc] init];
    [self.navigationController pushViewController:publicServiceVC  animated:YES];
    
    // 启动搜索公众号功能
    RCPublicServiceSearchViewController *searchFirendVC = [[RCPublicServiceSearchViewController alloc] init];
    [self.navigationController pushViewController:searchFirendVC  animated:YES];
    

    启动公众服务会话界面

    如开发者想自己实现启动公众服务会话界面,需要在初始化公众服务会话时,正确初始化基类,比如服务号 Id 、会众服务类型、服务号名称和会话标题,例如:

    RCPublicServiceChatViewController *conversationVC = [[RCPublicServiceChatViewController alloc] init];
    conversationVC.conversationType = conversationType;
    conversationVC.targetId = targetId;
    conversationVC.userName = conversationTitle;
    conversationVC.title = conversationTitle;
    [self.navigationController pushViewController:conversationVC animated:YES];
    

    消息发送

    消息的远程推送:

    您在开发者后台配置好远程推送的证书,且在代码中申请好权限,并将 deviceToken 传给融云服务器,当接收者不在线的时候,融云服务器会自动通过远程推送将消息发过去。

    注: 推送的内容由发送消息接口的 pushContent 字段决定,内置消息发送的时候如果该字段没有值,将使用默认内容推送;自定义消息必须设置该字段,否则将不会推送。

    详情参考:iOS 远程推送文档

    消息的本地通知:

    SDK 在当 App 进入后台并且没被系统杀死的时候,收到消息会弹一个本地通知,并且有提示音,详情参考消息接收的文档

    说明:如果使用 IMKit,会话页面本身已经实现了除“图文消息”之外的其余常见的 SDK 内置消息的发送功能。

    文本消息

    /*!
     初始化文本消息
    
     @param content 文本消息的内容
     @return        文本消息对象
     */
    RCTextMessage *txtMsg = [RCTextMessage messageWithContent:content];
    

    发送该消息体的接口:

    //RCIM class
    /*!
     发送消息(除图片消息、文件消息外的所有消息),会自动更新UI
    
     @param conversationType    发送消息的会话类型
     @param targetId            发送消息的目标会话ID
     @param content             消息的内容
     @param pushContent         接收方离线时需要显示的远程推送内容
     @param pushData            接收方离线时需要在远程推送中携带的非显示数据
     @param successBlock        消息发送成功的回调 [messageId:消息的ID]
     @param errorBlock          消息发送失败的回调 [nErrorCode:发送失败的错误码, messageId:消息的ID]
     @return                    发送的消息实体
    
     @discussion 当接收方离线并允许远程推送时,会收到远程推送。
     远程推送中包含两部分内容,一是pushContent,用于显示;二是pushData,用于携带不显示的数据。
    
     SDK内置的消息类型,如果您将pushContent和pushData置为nil,会使用默认的推送格式进行远程推送。
     自定义类型的消息,需要您自己设置pushContent和pushData来定义推送内容,否则将不会进行远程推送。
    
     @warning 如果您使用IMKit,使用此方法发送消息SDK会自动更新UI;
     如果您使用IMLib,请使用RCIMClient中的同名方法发送消息,不会自动更新UI。
     */
    - (RCMessage *)sendMessage:(RCConversationType)conversationType
                      targetId:(NSString *)targetId
                       content:(RCMessageContent *)content
                   pushContent:(NSString *)pushContent
                      pushData:(NSString *)pushData
                       success:(void (^)(long messageId))successBlock
                         error:(void (^)(RCErrorCode nErrorCode, long messageId))errorBlock;
    
    在 IMKit 会话界面中,为保证 UI 体验的流畅度,单条文本消息的显示高度限制为 8000pt,当消息内容较多时将截取显示,不会将内容全部显示出来,如需要查看全部内容可在消息的双击事件中实现展示处理。详细查看

    位置消息

    /*!
     初始化地理位置消息
    
     @param image 地理位置的缩略图
     @param location 地理位置的二维坐标
     @param locationName 地理位置的名称
     @return 地理位置消息的对象
     */
    RCLocationMessage *locationMessage = [RCLocationMessage messageWithLocationImage:image location:location locationName:locationName];
    

    发送该消息体的接口,参照文本消息发送方法。

    语音消息

    /*!
     初始化语音消息
    
     @param audioData   wav格式的音频数据
     @param duration    语音消息的时长(单位:秒)
     @return            语音消息对象
    
     @discussion
     如果您不是使用IMKit中的录音功能,则在初始化语音消息的时候,需要确保以下几点。
     1. audioData必须是单声道的wav格式音频数据;
     2. audioData的采样率必须是8000Hz,采样位数(精度)必须为16位。
    
     您可以参考IMKit中的录音参数:
     NSDictionary *settings = @{AVFormatIDKey: @(kAudioFormatLinearPCM),
                                AVSampleRateKey: @8000.00f,
                                AVNumberOfChannelsKey: @1,
                                AVLinearPCMBitDepthKey: @16,
                                AVLinearPCMIsNonInterleaved: @NO,
                                AVLinearPCMIsFloatKey: @NO,
                                AVLinearPC'MIsBigEndianKey: @NO};
     */
     RCVoiceMessage *rcVoiceMessage = [RCVoiceMessage messageWithAudio:audioData duration:duration]
    

    发送该消息体的接口,参照文本消息发送方法。

    注:语音参数必须是 wav 格式的,RCIMClient 中有相关的方法来转换语音数据格式。

    图文(富文本)消息

    /*!
     初始化图文消息
    
     @param title       图文消息的标题
     @param digest      图文消息的内容摘要
     @param imageURL    图文消息的图片URL
     @param url         图文消息中包含的需要跳转到的URL
     @param extra       图文消息的扩展信息
     @return            图文消息对象
     */
    RCRichContentMessage *richMsg = [RCRichContentMessage messageWithTitle:title digest:digest imageURL:imageurl url:url extra:nil];
    

    发送该消息体的接口,参照文本消息发送方法。

    图片消息

    /*!
     初始化图片消息
    
     @param image   原始图片
     @return        图片消息对象
     */
    RCImageMessage *imageMessage = [RCImageMessage messageWithImage:image]
    
    /*!
     初始化图片消息
    
     @param imageURI    图片的本地路径
     @return            图片消息对象
     */
     + (instancetype)messageWithImageURI:(NSString *)imageURI
    
    注:图片消息发送方的 imageUrl 里面是图片的本地 url接收方的 imageUrl 是网络 url

    发送该消息体的接口,融云 SDK 中默认上传文件存储有效期为 6 个月,不支持文件迁移。

    //RCIM class
    /*!
     发送媒体文件消息,会自动更新UI
    
     @param conversationType    发送消息的会话类型
     @param targetId            发送消息的目标会话ID
     @param content             消息的内容
     @param pushContent         接收方离线时需要显示的远程推送内容
     @param pushData            接收方离线时需要在远程推送中携带的非显示数据
     @param progressBlock       消息发送进度更新的回调 [progress:当前的发送进度, 0 <= progress <= 100, messageId:消息的ID]
     @param successBlock        消息发送成功的回调 [messageId:消息的ID]
     @param errorBlock          消息发送失败的回调 [errorCode:发送失败的错误码, messageId:消息的ID]
     @param cancelBlock         用户取消了消息发送的回调 [messageId:消息的ID]
     @return                    发送的消息实体
    
     @discussion 当接收方离线并允许远程推送时,会收到远程推送。
     远程推送中包含两部分内容,一是pushContent,用于显示;二是pushData,用于携带不显示的数据。
    
     SDK内置的消息类型,如果您将pushContent和pushData置为nil,会使用默认的推送格式进行远程推送。
     自定义类型的消息,需要您自己设置pushContent和pushData来定义推送内容,否则将不会进行远程推送。
    
     @warning 如果您使用IMKit,使用此方法发送媒体文件消息SDK会自动更新UI;
     如果您使用IMLib,请使用RCIMClient中的同名方法发送媒体文件消息,不会自动更新UI。
     */
    - (RCMessage *)sendMediaMessage:(RCConversationType)conversationType
                           targetId:(NSString *)targetId
                            content:(RCMessageContent *)content
                        pushContent:(NSString *)pushContent
                           pushData:(NSString *)pushData
                           progress:(void (^)(int progress, long messageId))progressBlock
                            success:(void (^)(long messageId))successBlock
                              error:(void (^)(RCErrorCode errorCode, long messageId))errorBlock
                             cancel:(void (^)(long messageId))cancelBlock
    

    图片缩略图机制:

    缩略图尺寸为:240 x 240 像素,以宽度和高度中较长的边不超过 240 像素等比压缩。

    大图尺寸为:960 x 960 像素,以宽度和高度中较长的边不超过 960 像素等比压缩。

    发送该消息的接口(上传到指定的服务器):

    参考:如何将图片、文件发送到指定服务器

    文件消息

    融云 SDK 中默认上传文件存储有效期为 6 个月,不支持文件迁移。

    /*!
     初始化文件消息
    
     @param localPath 文件的本地路径
     @return          文件消息对象
     */
    + (instancetype)messageWithFile:(NSString *)localPath
    RCFileMessage *fileMessage = [RCFileMessage messageWithFile:filePath]
    

    发送该消息体的接口,参照图片消息发送方法。

    消息接收监听

    如果您使用 IMKit,请使用 RCIM 中的消息监听,而不要设置 RCIMClient 的监听,否则将屏蔽 IMKit 中的 UI 刷新(比如某些界面收发消息不能自动刷新)。

    // RCIM Class
    
    /*!
     IMKit消息接收的监听器
    
     @warning 如果您使用IMKit,可以设置并实现此Delegate监听消息接收;
     如果您使用IMLib,请使用RCIMClient中的RCIMClientReceiveMessageDelegate监听消息接收,而不要使用此方法。
     */
    @property(nonatomic, weak) id<RCIMReceiveMessageDelegate> receiveMessageDelegate;
    

    说明:

    该代理建议设置给 AppDelegate,这样保证 App 的生命周期内 SDK 可以正常的调用下面的代理方法。

    IMKit 监听消息接收的代理主要有三个接口:

    接收消息

    – onRCIMReceiveMessage:left: 在前台和后台活动状态时收到任何消息都会执行。

    如果需要在收到某些特殊消息进行相应处理(例如自定义消息有特殊的处理逻辑),可以在此方法中处理,但是这个时候消息已经被存入了 SDK 内置数据库中,在此处对消息体内容进行修改是不会保存到数据库中的
    image

    消息提醒设置

    关闭某个会话的消息提醒

    您可以通过 RCIMClient 的以下接口关闭某个会话的消息提醒。

    // RCIMClient Class
    
    /*!
     设置会话的消息提醒状态
    
     @param conversationType            会话类型
     @param targetId                    目标会话ID
     @param isBlocked                   是否屏蔽消息提醒
     @param successBlock                设置成功的回调 [nStatus:会话设置的消息提醒状态]
     @param errorBlock                  设置失败的回调 [status:设置失败的错误码]
    
     @discussion 如果您使用IMLib,此方法会屏蔽该会话的远程推送;如果您使用IMKit,此方法会屏蔽该会话的所有提醒(远程推送、本地通知、前台提示音)。
     */
    - (void)setConversationNotificationStatus:(RCConversationType)conversationType
                                     targetId:(NSString *)targetId
                                    isBlocked:(BOOL)isBlocked
                                      success:(void (^)(RCConversationNotificationStatus nStatus))successBlock
                                        error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     查询会话的消息提醒状态
    
     @param conversationType    会话类型
     @param targetId            目标会话ID
     @param successBlock        查询成功的回调 [nStatus:会话设置的消息提醒状态]
     @param errorBlock          查询失败的回调 [status:设置失败的错误码]
     */
    - (void)getConversationNotificationStatus:(RCConversationType)conversationType
                                     targetId:(NSString *)targetId
                                      success:(void (^)(RCConversationNotificationStatus nStatus))successBlock
                                        error:(void (^)(RCErrorCode status))errorBlock;
    

    按时间段关闭某个会话的消息提醒

    您可以通过 RCIMClient 的以下接口关闭某个会话在某个时间段内的消息提醒。

    // RCIMClient Class
    
    /*!
     屏蔽会话在某个时间段的消息提醒
    
     @param startTime       开始屏蔽消息提醒的时间,格式为HH:MM:SS s
     @param spanMins         需要屏蔽消息提醒的分钟数,0 < spanMins < 1440
     @param successBlock    屏蔽成功的回调
     @param errorBlock      屏蔽失败的回调 [status:屏蔽失败的错误码]
    
     @discussion 此方法设置的屏蔽时间会在每天该时间段时生效。
     如果您使用IMLib,此方法会屏蔽该会话在该时间段的远程推送;如果您使用IMKit,此方法会屏蔽该会话在该时间段的所有提醒(远程推送、本地通知、前台提示音)。
     */
    - (void)setConversationNotificationQuietHours:(NSString *)startTime
                                         spanMins:(int)spanMins
                                          success:(void (^)())successBlock
                                            error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     删除会话的时间段消息提醒的屏蔽设置
    
     @param successBlock    删除屏蔽成功的回调
     @param errorBlock      删除屏蔽失败的回调 [status:失败的错误码]
     */
    - (void)removeConversationNotificationQuietHours:(void (^)())successBlock
                                               error:(void (^)(RCErrorCode status))errorBlock;
    
    /*!
     查询会话消息提醒的屏蔽时间段设置
    
     @param successBlock    屏蔽成功的回调 [startTime:已设置的屏蔽开始时间, spansMin:已设置的屏蔽时间分钟数,0 < spansMin < 1440]
     @param errorBlock      查询失败的回调 [status:查询失败的错误码]
     */
    - (void)getNotificationQuietHours:(void (^)(NSString *startTime, int spansMin))successBlock
                                error:(void (^)(RCErrorCode status))errorBlock;
    

    前台提示音的开关

    您可以通过 RCIM 的以下接口关闭所有的前台提示音。

    // RCIM Class
    
    /*!
     是否关闭所有的前台消息提示音,默认值是NO
    
     @discussion 当App处于前台时,默认会播放消息提示音,您可以通过将此属性设置为YES,关闭所有的前台消息提示音。
     */
    @property(nonatomic, assign) BOOL disableMessageAlertSound;
    

    您可以通过 RCIMReceiveMessageDelegate 的以下接口,针对具体的会话或消息,定制或关闭前台提示音。

    // RCIM Class
    
    /*!
     IMKit消息接收的监听器
    
     @discussion 设置IMKit的消息接收监听器请参考RCIM的receiveMessageDelegate属性。
    
     @warning 如果您使用IMKit,可以设置并实现此Delegate监听消息接收;
     如果您使用IMLib,请使用RCIMClient中的RCIMClientReceiveMessageDelegate监听消息接收,而不要使用此监听器。
     */
    @protocol RCIMReceiveMessageDelegate <NSObject>
    @optional
    
    /*!
     当App处于前台时,接收到消息并播放提示音的回调方法
    
     @param message 接收到的消息
     @return        当返回值为NO时,SDK会播放默认的提示音;当返回值为YES时,SDK针对此消息不再播放提示音
    
     @discussion 到消息时播放提示音之前,会执行此方法。
     如果App没有实现此方法,SDK会播放默认的提示音。
     流程:
     SDK接收到消息 -> App处于前台状态 -> 回调此方法准备播放提示音 -> App实现并返回YES        -> SDK针对此消息不再播放提示音
                                                          -> App未实现此方法或者返回NO -> SDK会播放默认的提示音
    
     您可以通过RCIM的disableMessageAlertSound属性,关闭所有前台消息的提示音(此时不再回调此接口)。
     */
    -(BOOL)onRCIMCustomAlertSound:(RCMessage*)message;
    
    @end
    

    后台本地通知的开关

    您可以通过 RCIM 的以下接口关闭所有的本地通知提示。

    // RCIM Class
    
    /*!
     是否关闭所有的本地通知,默认值是NO
    
     @discussion 当App处于后台时,默认会弹出本地通知提示,您可以通过将此属性设置为YES,关闭所有的本地通知。
     */
    @property(nonatomic, assign) BOOL disableMessageNotificaiton;
    

    您可以通过 RCIMReceiveMessageDelegate 的以下接口,针对具体的会话或消息,定制或关闭本地通知提示。

    // RCIM Class
    
    /*!
     IMKit消息接收的监听器
    
     @discussion 设置IMKit的消息接收监听器请参考RCIM的receiveMessageDelegate属性。
    
     @warning 如果您使用IMKit,可以设置并实现此Delegate监听消息接收;
     如果您使用IMLib,请使用RCIMClient中的RCIMClientReceiveMessageDelegate监听消息接收,而不要使用此监听器。
     */
    @protocol RCIMReceiveMessageDelegate <NSObject>
    
    @optional
    
    /*!
     当App处于后台时,接收到消息并弹出本地通知的回调方法
    
     @param message     接收到的消息
     @param senderName  消息发送者的用户名称
     @return            当返回值为NO时,SDK会弹出默认的本地通知提示;当返回值为YES时,SDK针对此消息不再弹本地通知提示
    
     @discussion 如果您设置了IMKit消息监听之后,当App处于后台,收到消息时弹出本地通知之前,会执行此方法。
     如果App没有实现此方法,SDK会弹出默认的本地通知提示。
     流程:
     SDK接收到消息 -> App处于后台状态 -> 通过用户/群组/群名片信息提供者获取消息的用户/群组/群名片信息
     -> 用户/群组信息为空 -> 不弹出本地通知
     -> 用户/群组信息存在 -> 回调此方法准备弹出本地通知 -> App实现并返回YES        -> SDK不再弹出此消息的本地通知
                                                 -> App未实现此方法或者返回NO -> SDK弹出默认的本地通知提示
    
    
     您可以通过RCIM的disableMessageNotificaiton属性,关闭所有的本地通知(此时不再回调此接口)。
    
     @warning 如果App在后台想使用SDK默认的本地通知提醒,需要实现用户/群组/群名片信息提供者,并返回正确的用户信息或群组信息。
     参考RCIMUserInfoDataSource、RCIMGroupInfoDataSource与RCIMGroupUserInfoDataSource
     */
    -(BOOL)onRCIMCustomLocalNotification:(RCMessage*)message
                          withSenderName:(NSString *)senderName;
    
    @end
    

    常见问题

    1. 为什么收不到远程推送?

      参考收不到远程推送排查步骤

    2. 如何获取远程推送和本地通知的内容?

      参考获取远程推送和本地通知的内容

    3. 点击通知栏,如何跳到对应的会话?

      参考点击通知栏跳转相应会话页面

    4. 远程推送和本地通知有何区别?

      参考区分远程推送和本地通知

    5. 为什么 iOS 9 中收到重复的远程推送问题?

      参考 iOS 9 收到重复推送

    6. iOS SDK 本地通知的内容格式

      参考 SDK 本地通知的内容格式

    远程推送

    使用融云 IM 时,何时会收到远程推送、如何使用远程推送及获取远程推送的内容,请查看远程推送详解

    UI 自定义

    会话列表 UI 自定义

    会话列表自定义 cell

    参考如何自定义会话列表的 cell

    会话列表头像样式修改

    IMKit 的核心类 RCIM 中:

    /*!
     SDK会话列表界面中显示的头像形状,矩形或者圆形
    
     @discussion 默认值为矩形,即RC_USER_AVATAR_RECTANGLE
     */
    @property(nonatomic) RCUserAvatarStyle globalConversationAvatarStyle;
    
    /*!
     SDK会话列表界面中显示的头像大小,高度必须大于或者等于36
    
     @discussion 默认值为46*46
     */
    @property(nonatomic) CGSize globalConversationPortraitSize;
    

    会话列表自带 cell 样式修改:

    会话列表自带 cell 样式如字体颜色,字体大小的修改(不建议修改 cell 的布局,如果对 UI 比较高的定制需求,建议自定义会话列表 cell,参考如何自定义会话列表的 cell ?

    /*!
     即将显示Cell的回调
    
     @param cell        即将显示的Cell
     @param indexPath   该Cell对应的会话Cell数据模型在数据源中的索引值
    
     @discussion 您可以在此回调中修改Cell的一些显示属性。
     */
    - (void)willDisplayConversationTableCell:(RCConversationBaseCell *)cell
                                 atIndexPath:(NSIndexPath *)indexPath;
    

    如果需要判断会话类型做修改,RCConversationBaseCell 中可以直接取到数据 model:

    /*!
    会话Cell的数据模型
    */
    @property(nonatomic, strong) RCConversationModel *model;
    

    :RCConversationBaseCell 是会话列表 cell 的基类,如果您想对 cell 中某个控件的颜色,字体大小等做修改,需要在上述方法中把 RCConversationBaseCell 强转成子类 RCConversationCell ,再取相关控件做修改

    参考自定义 SDK 会话列表 Cell 的样式

    自定义会话列表为空时的视图:

    /*!
     列表为空时显示的View
     */
    @property(nonatomic, strong) UIView *emptyConversationView;
    

    参考会话列表为空,空视图的设置方法

    cell 背景色修改:

    /*!
     Cell的背景颜色
     */
    @property(nonatomic, strong) UIColor *cellBackgroundColor;
    
    /*!
     置顶会话的Cell背景颜色
     */
    @property(nonatomic, strong) UIColor *topCellBackgroundColor;
    

    会话列表加 header:

    可以直接给会话列表 conversationListTableView 的 tableHeaderView 添加视图

    会话列表隐藏网络状态:

    /*!
     当网络断开时,是否在Tabel View Header中显示网络连接不可用的提示。
    
     @discussion 默认值为YES。
     */
    @property(nonatomic, assign) BOOL isShowNetworkIndicatorView;
    

    会话列表未读消息提醒样式修改:

    会话列表的 RCConversationCell 中有下面属性

    /*!
     会话中有未读消息时,是否在头像右上角的bubbleTipView中显示数字
    
     @discussion 默认值为YES。
     您可以在RCConversationListViewController的willDisplayConversationTableCell:atIndexPath:回调中进行设置。
     */
    @property(nonatomic, assign) BOOL isShowNotificationNumber;
    

    会话页面

    RCConversationViewController 为会话页面视图控制器类,继承于 UICollectionViewController,您可以直接使用,也可以继承修改定制。

    会话页面中的 cell 为 RCMessageBaseCell,Model 为 RCMessageModel,数据的来源为本地存储的消息实体(RCMessage)。

    会话属性

    conversationType 和 targetId 唯一标识了当前会话页面所处的会话。

    // RCConversationViewController Class
    
    /*!
     初始化会话页面
    
     @param conversationType 会话类型
     @param targetId         目标会话ID
    
     @return 会话页面对象
     */
    - (id)initWithConversationType:(RCConversationType)conversationType
                          targetId:(NSString *)targetId;
    
    #pragma mark - 会话属性
    
    /*!
     当前会话的会话类型
     */
    @property(nonatomic) RCConversationType conversationType;
    
    /*!
     目标会话ID
     */
    @property(nonatomic, strong) NSString *targetId;
    

    点击事件

    会话页面提供了多种点击回调:点击 cell 内容、点击 cell 中的 URL 和电话号码、点击头像、长按头像、长按 cell 内容。

    // RCConversationViewController Class
    
    #pragma mark - 点击事件回调
    
    /*!
     点击Cell中的消息内容的回调
    
     @param model 消息Cell的数据模型
    
     @discussion SDK在此点击事件中,针对SDK中自带的图片、语音、位置等消息有默认的处理,如查看、播放等。
     您在重写此回调时,如果想保留SDK原有的功能,需要注意调用super。
     */
    - (void)didTapMessageCell:(RCMessageModel *)model;
    
    /*!
     长按Cell中的消息内容的回调
    
     @param model 消息Cell的数据模型
     @param view  长按区域的View
    
     @discussion SDK在此长按事件中,会默认展示菜单。
     您在重写此回调时,如果想保留SDK原有的功能,需要注意调用super。
     */
    - (void)didLongTouchMessageCell:(RCMessageModel *)model
                             inView:(UIView *)view;
    
    /*!
     点击Cell中URL的回调
    
     @param url   点击的URL
     @param model 消息Cell的数据模型
    */
    - (void)didTapUrlInMessageCell:(NSString *)url
                             model:(RCMessageModel *)model;
    
    /*!
     点击Cell中电话号码的回调
    
     @param phoneNumber 点击的电话号码
     @param model       消息Cell的数据模型
     */
    - (void)didTapPhoneNumberInMessageCell:(NSString *)phoneNumber
                                     model:(RCMessageModel *)model;
    
    /*!
     点击Cell中头像的回调
    
     @param userId  点击头像对应的用户ID
     */
    - (void)didTapCellPortrait:(NSString *)userId;
    
    /*!
     长按Cell中头像的回调
    
     @param userId  头像对应的用户ID
     */
    - (void)didLongPressCellPortrait:(NSString *)userId;
    

    发送、插入、删除消息

    在会话页面,用户可以通过 RCConversationViewController 内置的 UI 发送、插入和删除消息。您也可以通过代码接口完成这些操作。

    // RCConversationViewController Class
    
    #pragma mark 发送消息
    /*!
     发送消息
    
     @param messageContent 消息的内容
     @param pushContent    接收方离线时需要显示的远程推送内容
    
     @discussion 当接收方离线并允许远程推送时,会收到远程推送。
     远程推送中包含两部分内容,一是pushContent,用于显示;二是pushData,用于携带不显示的数据。
    
     SDK内置的消息类型,如果您将pushContent置为nil,会使用默认的推送格式进行远程推送。
     自定义类型的消息,需要您自己设置pushContent来定义推送内容,否则将不会进行远程推送。
    
     如果您需要设置发送的pushData,可以使用RCIM的发送消息接口。
     */
    - (void)sendMessage:(RCMessageContent *)messageContent
            pushContent:(NSString *)pushContent;
    
    /*!
     重新发送消息
    
     @param messageContent 消息的内容
    
     @discussion 发送消息失败,点击小红点时,会将本地存储的原消息实体删除,回调此接口将消息内容重新发送。
     如果您需要重写此接口,请注意调用super。
     */
    - (void)resendMessage:(RCMessageContent *)messageContent;
    
    #pragma mark 插入消息
    /*!
     在会话页面中插入一条消息并展示
    
     @param message 消息实体
    
     @discussion 通过此方法插入一条消息,会将消息实体对应的内容 Model 插入数据源中,并更新 UI。
     请注意,这条消息只会在 UI 上插入,并不会存入数据库。
     用户调用这个接口插入消息之后,如果退出会话页面再次进入的时候,这条消息将不再显示。
     */
    - (void)appendAndDisplayMessage:(RCMessage *)message;
    
    #pragma mark 删除消息
    /*!
     删除消息并更新UI
    
     @param model 消息Cell的数据模型
     */
    - (void)deleteMessage:(RCMessageModel *)model;
    

    其中,如果您想在当前会话中手动插入一条消息(如提醒消息等),根据您是否要将其保存到本地数据库,有不同的处理。如下示例:

    //是否保存到本地数据库,如果不保存,则下次进入聊天界面将不再显示。
    BOOL saveToDB = NO;
    
    RCMessage *insertMessage;
    RCInformationNotificationMessage *warningMessage = [RCInformationNotificationMessage notificationWithMessage:@"提醒消息" extra:nil];
    if (saveToDB) {
      // 如果保存到本地数据库,需要调用insertMessage生成消息实体并插入数据库。
      insertMessage = [[RCIMClient sharedRCIMClient] insertOutgoingMessage:self.conversationType
                        targetId:self.targetId
                      sentStatus:SentStatus_SENT
                         content:warningMessage];
    } else {
      // 如果不保存到本地数据库,需要初始化消息实体并将messageId要设置为-1。
      insertMessage =[[RCMessage alloc] initWithType:self.conversationType
                                            targetId:self.targetId
                                           direction:MessageDirection_SEND
                                           messageId:-1
                                             content:warningMessage];
    }
    
    // 在当前聊天界面插入该消息
    [self appendAndDisplayMessage:insertMessage];
    

    消息发送与显示的回调

    您可以在消息即将发送、即将插入数据源的时候监听回调(如果有需求,您可以在这时候修改其中的消息内容)。在消息发送完成和即将显示的时候,您也可以监听回调。

    // RCConversationViewController Class
    
    /*!
     准备发送消息的回调
    
     @param messageContent 消息内容
    
     @return 修改后的消息内容
    
     @discussion 此回调在消息准备向外发送时会回调,您可以在此回调中对消息内容进行过滤和修改等操作。
     如果此回调的返回值不为nil,SDK会对外发送返回的消息内容。
     */
    - (RCMessageContent *)willSendMessage:(RCMessageContent *)messageContent;
    
    /*!
     发送消息完成的回调
    
     @param status          发送状态,0表示成功,非0表示失败
     @param messageContent   消息内容
     */
    - (void)didSendMessage:(NSInteger)status
                   content:(RCMessageContent *)messageContent;
    
    /*!
     即将在会话页面插入消息的回调
    
     @param message 消息实体
     @return        修改后的消息实体
    
     @discussion 此回调在消息准备插入数据源的时候会回调,您可以在此回调中对消息进行过滤和修改操作。
     如果此回调的返回值不为nil,SDK会将返回消息实体对应的消息Cell数据模型插入数据源,并在聊天界面中显示。
     */
    - (RCMessage *)willAppendAndDisplayMessage:(RCMessage *)message;
    
    /*!
     即将显示消息Cell的回调
    
     @param cell        消息Cell
     @param indexPath   该Cell对应的消息Cell数据模型在数据源中的索引值
    
     @discussion 您可以在此回调中修改Cell的显示和某些属性。
     */
    - (void)willDisplayMessageCell:(RCMessageBaseCell *)cell
                       atIndexPath:(NSIndexPath *)indexPath;
    

    未读消息数

    会话页面提供了三种未读消息数的提示,您可以根据您的需求开启或关闭。

    1、 导航栏返回按钮中的未读消息数提示

    displayConversationTypeArray 表明在导航栏返回按钮中应该统计哪些会话类型的未读数,如果您需要关闭此功能,赋值为 nil 即可。

    // RCConversationViewController Class
    
    /*!
     需要统计未读数的会话类型数组(在导航栏的返回按钮中显示)
    
     @discussion 此属性表明在导航栏的返回按钮中需要统计显示哪部分的会话类型的未读数。
     (需要将RCConversationType转为NSNumber构建Array)
     */
    @property(nonatomic, strong) NSArray *displayConversationTypeArray;
    

    2、 右上角的未读消息数提示

    当一个会话收到大量消息时(超过一个屏幕能显示的内容),进入该会话后,会在右上角提示用户在当前可视区域上方存在的未读消息数,用户点击该提醒按钮,会滚动到最开始的未读消息处。

    // RCConversationViewController Class
    
    /*!
     当收到的消息超过一个屏幕时,进入会话之后,是否在右上角提示上方存在的未读消息数
    
     @discussion 默认值为NO。
     开启该提示功能之后,当一个会话收到大量消息时(超过一个屏幕能显示的内容),
     进入该会话后,会在右上角提示用户上方存在的未读消息数,用户点击该提醒按钮,会跳转到最开始的未读消息。
     */
    @property(nonatomic, assign) BOOL enableUnreadMessageIcon;
    

    3、 右下角的未读消息数提示

    当用户停留在会话页面非最下方区域阅读时,此会话收到消息,会在右下角显示未读消息提示,而不会自动滚动到最下方,当用户点击该提醒按钮,会滚动到最下方显示最新的消息。

    // RCConversationViewController Class
    
    /*!
     当前阅读区域的下方收到消息时,是否在会话页面的右下角提示下方存在未读消息
    
     @discussion 默认值为NO。
     开启该提示功能之后,当会话页面滑动到最下方时,此会话中收到消息会自动更新;
     当用户停留在上方某个区域阅读时,此会话收到消息,会在右下角显示未读消息提示,而不会自动滚动到最下方,
     用户点击该提醒按钮,会滚动到最下方。
     */
    @property(nonatomic, assign) BOOL enableNewComingMessageIcon;
    

    输入工具栏

    针对常用的 IM 聊天场景,聊天界面最下方提供了输入工具栏,集成了文本输入、语音录音、表情、扩展输入(图片、定位、VoIP等)、菜单项(在公众服务会话中使用),开箱即用。

    image

    会话页面输入框工具栏控件 RCChatSessionInputBarControl 作为页面的属性开放出来,可以通过以下属性。

    /*!
     聊天界面下方的输入工具栏
     */
    @property(nonatomic, strong) RCChatSessionInputBarControl *chatSessionInputBarControl;
    
    /*!
     输入框的默认输入模式
    
     @discussion 默认值为RCChatSessionInputBarInputText,即文本输入模式。
     */
    @property(nonatomic) RCChatSessionInputBarInputType defaultInputType;
    

    默认输入样式 RCChatSessionInputBarInputType 类型如下:

    /*!
     输入工具栏的输入模式
     */
    typedef NS_ENUM(NSInteger, RCChatSessionInputBarInputType) {
        /*!
         文本输入模式
         */
        RCChatSessionInputBarInputText = 0,
        /*!
         语音输入模式
         */
        RCChatSessionInputBarInputVoice = 1,
        /*!
         扩展输入模式
         */
        RCChatSessionInputBarInputExtention = 2
    };
    

    您也可以定制进入聊天界面时显示的默认输入模式。继承会话页面 RCConversationViewController 新建会话页面,然后在页面的 viewWillAppear 中修改页面的 defaultInputType 属性。

    示例代码:

    - (void)viewWillAppear:(BOOL)animated {
      [super viewWillAppear:animated];
      //默认输入类型为语音,这里修改为默认显示加号区域
      self.defaultInputType = RCChatSessionInputBarInputExtention;
    }
    

    输入工具栏默认提供了多种显示布局模式,您可以按需定制。您可以在聊天界面 RCConversationViewController 的 viewDidLoad 之后设置,改变输入工具栏的样式,示例代码:

    [self.chatSessionInputBarControl setInputBarType:RCChatSessionInputBarControlDefaultType style:RC_CHAT_INPUT_BAR_STYLE_CONTAINER];
    
    /*!
     输入工具栏的菜单类型
     */
    typedef NS_ENUM(NSInteger, RCChatSessionInputBarControlType) {
        /*!
         默认类型(非公众服务会话)
         */
        RCChatSessionInputBarControlDefaultType = 0,
        /*!
         公众服务(公众服务会话)
         */
        RCChatSessionInputBarControlPubType = 1,
    
        /*!
         客服机器人(客服会话)
         */
        RCChatSessionInputBarControlCSRobotType = 2,
    
        /*!
         客服机器人(客服会话)
         */
        RCChatSessionInputBarControlNoAvailableType = 3
    };
    
    注意:RCChatSessionInputBarControlType 是按照会话类型来设置的,不要随意设置。
    style 样式枚举 组合模式
    RC_CHAT_INPUT_BAR_STYLE_SWITCH_CONTAINER_EXTENTION 语音/文本切换功能+内容输入功能+扩展功能
    RC_CHAT_INPUT_BAR_STYLE_EXTENTION_CONTAINER_SWITCH 扩展功能+内容输入功能+语音/文本切换功能
    RC_CHAT_INPUT_BAR_STYLE_CONTAINER_SWITCH_EXTENTION 内容输入功能+语音/文本切换功能+扩展功能
    RC_CHAT_INPUT_BAR_STYLE_CONTAINER_EXTENTION_SWITCH 内容输入功能+扩展功能+语音/文本切换功能
    RC_CHAT_INPUT_BAR_STYLE_SWITCH_CONTAINER 语音/文本切换功能+内容输入功能
    RC_CHAT_INPUT_BAR_STYLE_CONTAINER_SWITCH 内容输入功能+语音/文本切换功能
    RC_CHAT_INPUT_BAR_STYLE_EXTENTION_CONTAINER 扩展功能+内容输入功能
    RC_CHAT_INPUT_BAR_STYLE_CONTAINER_EXTENTION 内容输入功能+扩展功能
    RC_CHAT_INPUT_BAR_STYLE_CONTAINER 内容输入功能

    表情区域

    表情区域分为默认的 Emoji 表情和扩展表情

    Emoji 表情区域

    输入框工具栏控件 RCChatSessionInputBarControl 里面继承了表情区域控件

    @property(nonatomic, strong) RCEmojiBoardView *emojiBoardView;
    

    表情您如果想定制表情的内容,可以直接修改 Emoji.plist 中的内容即可。

    image

    扩展表情区域

    image

    扩展区域可以开发者自己添加也可以集成我们合作商的表情(目前与表情云合作正在内测中)

    示例代码请参考添加自定义表情

    加号扩展区域

    您可以通过聊天界面的 pluginBoardView 属性,在 viewDidLoad 中自定义扩展输入中的内容,增加、移除、修改扩展的 Item(注意:SDK 默认的扩展项的唯一标示符为 1XXX,我们建议您在自定义扩展功能时不要选用 1XXX,以免与 SDK 预留的扩展项唯一标示符重复。)。

    pluginBoardView class
    
    /*!
     向扩展功能板中插入扩展项
    
     @param image 扩展项的展示图片
     @param title 扩展项的展示标题
     @param index 需要添加到的索引值
     @param tag   扩展项的唯一标示符
    
     @discussion 您以在RCConversationViewController的viewdidload后,添加自定义的扩展项。
     SDK默认的扩展项的唯一标示符为1XXX,我们建议您在自定义扩展功能时不要选用1XXX,以免与SDK预留的扩展项唯一标示符重复。
     */
    - (void)insertItemWithImage:(UIImage*)image title:(NSString*)title atIndex:(NSInteger)index tag:(NSInteger)tag;
    
    /*!
     添加扩展项到扩展功能板,并在显示为最后一项
    
     @param image 扩展项的展示图片
     @param title 扩展项的展示标题
     @param tag   扩展项的唯一标示符
    
     @discussion 您以在RCConversationViewController的viewdidload后,添加自定义的扩展项。
     SDK默认的扩展项的唯一标示符为1XXX,我们建议您在自定义扩展功能时不要选用1XXX,以免与SDK预留的扩展项唯一标示符重复。
     */
    -(void)insertItemWithImage:(UIImage*)image title:(NSString*)title tag:(NSInteger)tag;
    
    /*!
     更新指定扩展项
    
     @param index 扩展项的索引值
     @param image 扩展项的展示图片
     @param title 扩展项的展示标题
     */
    -(void)updateItemAtIndex:(NSInteger)index image:(UIImage*)image title:(NSString*)title;
    
    /*!
     更新指定扩展项
     @param tag   扩展项的唯一标示符
     @param image 扩展项的展示图片
     @param title 扩展项的展示标题
     */
    -(void)updateItemWithTag:(NSInteger)tag image:(UIImage*)image title:(NSString*)title;
    
    /*!
     删除扩展功能板中的指定扩展项
     @param index 指定扩展项的索引值
     */
    - (void)removeItemAtIndex:(NSInteger)index;
    
    /*!
     删除扩展功能板中的指定扩展项
     @param tag 指定扩展项的唯一标示符
     */
    - (void)removeItemWithTag:(NSInteger)tag;
    
    /*!
     删除扩展功能板中的所有扩展项
     */
    - (void)removeAllItems;
    @end
    

    SDK 默认的扩展输入 Item 的 Tag 定义如下:

    // RCConversationViewController class
    ///输入栏扩展输入的唯一标示
     #define PLUGIN_BOARD_ITEM_ALBUM_TAG    1001
     #define PLUGIN_BOARD_ITEM_CAMERA_TAG   1002
     #define PLUGIN_BOARD_ITEM_LOCATION_TAG 1003
     #if RC_VOIP_ENABLE
     #define PLUGIN_BOARD_ITEM_VOIP_TAG     1004
     #endif
    

    示例代码请参考输入扩展功能板的自定义,添加与移除

    从 2.8.2 开始加号区域 RCPluginBoradView 增加属性 extensionView,开发者可以控制显示或是隐藏,在点击语音头像等其他按钮切换输入方式时会自动隐藏 extensionView。

    pluginBoardView class
    /*!
     扩展view ,此视图会覆盖加号区域其他视图,默认隐藏
     */
    @property(nonatomic, strong) UIView *extensionView;
    

    消息自定义

    除了融云 SDK 中内置的消息类型,开发者也可以自定义消息。自定义消息的显示,需要完成以下三步:

    一、自定义消息并注册消息类型

    二、自定义消息 cell 并注册 cell

    三、自定义消息的发送

    版本说明:
    如果您使用的 SDK 是 2.7.1 (不包含 2.7.1) 之前版本,自定义消息参考融云 iOS SDK 自定义消息类型及展示
    如果您之前有自定义消息,升级 2.7.1 及以上版本后不想更改方法,仍然可按照之前的逻辑处理,SDK 兼容之前的版本。
    如果您使用的是 2.7.1 以后的版本,请按照具体步骤里的方法来做(注:融云的开源项目 SealTalk 中,有自定义消息的例子,RCDTestMessage 和对应的 cell,可以作为参考。)
    具体步骤:

    1、自定义消息并注册消息类型

    您需要继承 RCMessageContent 实现自定义消息类,并在 SDK 初始化之后,注册自定义消息。

    RCMessageContent 是消息内容类,是所有消息的基类。您可以继承此类,并实现其中的协议,来实现自定义消息。

    RCMessageContent 主要有三个协议:

    • 编解码协议 RCMessageCoding
    • 存储协议 RCMessagePersistentCompatible
    • 内容摘要协议 RCMessageContentView(可选)

    其中,RCMessageCoding 主要有三个功能:提供消息唯一标识符、消息发送时将消息中的所有信息编码为 JSON 数据传输、消息接收时将 JSON 数据解码还原为消息对象。

    RCMessagePersistentCompatible 用于确定消息内容的存储策略,指明此消息类型在本地是否存储、是否计入未读消息数,说明如下:

    枚举值 说明
    MessagePersistent_NONE 在本地不存储,不计入未读数。
    MessagePersistent_ISPERSISTED 表示客户端收到消息后,要进行未读消息计数(未读消息数增加 1),所有内容型消息都应该设置此值。非内容类消息暂不支持消息计数。
    MessagePersistent_ISCOUNTED 表示客户端收到消息后,要进行存储,并在之后可以通过接口查询。
    MessagePersistent_STATUS 在本地不存储,不计入未读数,并且如果对方不在线,服务器会直接丢弃该消息,对方如果之后再上线也不会再收到此消息(聊天室类型除外,此类消息聊天室会视为普通消息)。

    RCMessageContentView 用于在会话列表和本地通知中显示消息的摘要。 您可以通过继承 RCMessageContent 实现其中的协议,将您的消息内容编解码为 JSON 数据,即可实现存放任何数据的消息。

    您可以通过继承 RCMessageContent 实现其中的协议,将您的消息内容编解码为 JSON 数据,即可实现存放任何数据的消息。

    // RCMessageContent Class
    
    /*!
     消息内容的编解码协议
    
     @discussion 用于标示消息内容的类型,进行消息的编码和解码。
     所有自定义消息必须实现此协议,否则将无法正常传输和使用。
     */
    @protocol RCMessageCoding <NSObject>
    @required
    
    /*!
     将消息内容序列化,编码成为可传输的json数据
    
     @discussion 消息内容通过此方法,将消息中的所有数据,编码成为json数据,返回的json数据将用于网络传输。
     */
    - (NSData *)encode;
    
    /*!
     将json数据的内容反序列化,解码生成可用的消息内容
    
     @param data    消息中的原始json数据
     @discussion 网络传输的json数据,会通过此方法解码,获取消息内容中的所有数据,生成有效的消息内容。
     */
    - (void)decodeWithData:(NSData *)data;
    
    /*!
     返回消息的类型名
    
     @return 消息的类型名
     @discussion 您定义的消息类型名,需要在各个平台上保持一致,以保证消息互通。
     @warning 请勿使用@"RC:"开头的类型名,以免和SDK默认的消息名称冲突
     */
    + (NSString *)getObjectName;
    
    @end
    
    /*!
     消息内容的存储协议
    
     @discussion 用于确定消息内容的存储策略。
     所有自定义消息必须实现此协议,否则将无法正常存储和使用。
     */
    @protocol RCMessagePersistentCompatible <NSObject>
    @required
    
    /*!
     返回消息的存储策略
    
     @return 消息的存储策略
     @discussion 指明此消息类型在本地是否存储、是否计入未读消息数。
     */
    + (RCMessagePersistent)persistentFlag;
    @end
    
    /*!
     消息内容摘要的协议
    
     @discussion 用于在会话列表和本地通知中显示消息的摘要。
     */
    @protocol RCMessageContentView
    @optional
    
    /*!
     返回在会话列表和本地通知中显示的消息内容摘要
    
     @return 会话列表和本地通知中显示的消息内容摘要
     @discussion 如果您使用IMKit,当会话的最后一条消息为自定义消息时,需要通过此方法获取在会话列表展现的内容摘要;
     当App在后台收到消息时,需要通过此方法获取在本地通知中展现的内容摘要。
     */
    - (NSString *)conversationDigest;
    
    @end
    

    在实现了上面的自定义消息类后,您需要在 SDK 初始化之后,注册该自定义消息类。只有注册了该消息类型之后,SDK 才能识别和编码、解码该类型的消息。

    // RCIM Class
    
    /*!
    注册自定义的消息类型
    
    @param messageClass    自定义消息的类,该自定义消息需要继承于RCMessageContent
    @discussion 如果您需要自定义消息,必须调用此方法注册该自定义消息的消息类型,否则SDK将无法识别和解析该类型消息。
    @warning 如果您使用IMKit,请使用此方法注册自定义的消息类型;
    如果您使用IMLib,请使用RCIMClient中的同名方法注册自定义的消息类型,而不要使用此方法。
    */
    - (void)registerMessageType:(Class)messageClass;
    

    2、自定义消息 cell 并注册类型

    IMKit 消息 cell 都继承自 RCMessageBaseCell ,RCMessageCell 是在继承 RCMessageBaseCell 的基础上增加显示头像和昵称。 自定义消息 cell 可以根据业务场景选择继承于 RCMessageCell 或者 RCMessageBaseCell。

    RCMessageCell 结构图:

    image

    RCMessageBaseCell 结构图:

    image

    自定义消息 cell 继承于 RCMessageCell

    您的控件需要添加在 messageContentView 上,根据您自己的需求在画cell视图布局的时候调整 messageContentView 的 frame ,如果是接收方,您只需要修改 messageContentView 的 width 和 height,如果是发送方,您需要修改 messageContentView 的 x , width 和 height

    自定义消息 cell 继承于 RCMessageBaseCell

    您的控件需要添加在 baseContentView 上,建议在 baseContentView 上方预留 10

    请在初始化方法中实现 cell 的布局,并重写下面方法来返回 cell 的 Size:

    /*!
     自定义消息 Cell 的 Size
    
     @param model               要显示的消息model
     @param collectionViewWidth cell所在的collectionView的宽度
     @param extraHeight         cell内容区域之外的高度
    
     @return 自定义消息Cell的Size
    
     @discussion 当应用自定义消息时,必须实现该方法来返回cell的Size。
     其中,extraHeight是Cell根据界面上下文,需要额外显示的高度(比如时间、用户名的高度等)。
     一般而言,Cell的高度应该是内容显示的高度再加上extraHeight的高度。
     */
    + (CGSize)sizeForMessageModel:(RCMessageModel *)model
          withCollectionViewWidth:(CGFloat)collectionViewWidth
             referenceExtraHeight:(CGFloat)extraHeight;
    
    注:extraHeight 就是上面 cell 结构图中红色箭头的总高度, 返回的 cell 的 size 的高 等于 图中标注的 height + extraHeight ,size 的宽就是 collectionViewWidth

    并在会话界面注册消息 cell 和消息:

    //RCConversationViewController.h
    /*!
     注册自定义消息的 cell
    
     @param cellClass     自定义消息的类,该自定义消息需要继承于RCMessageContent
     @param messageClass  自定义消息Cell对应的自定义消息
    
     @discussion 你需要在cell中重写RCMessageBaseCell基类的sizeForMessageModel:withCollectionViewWidth:referenceExtraHeight:来计算cell的高度。
     */
    - (void)registerClass:(Class)cellClass forMessageClass:(Class)messageClass;
    

    请注意,如果使用了上述注册消息 cell 的方法,那么在接收该自定义消息时,是不会走聊天界面的如下方法的。这两个方法适用于 2.7.1 之前的版本,请参考版本说明里的内容。

    /*!
     自定义消息Cell显示的回调
    
     @param collectionView  当前CollectionView
     @param indexPath       该Cell对应的消息Cell数据模型在数据源中的索引值
     @return                自定义消息需要显示的Cell
    
     @discussion 自定义消息如果需要显示,则必须先通过RCIM的registerMessageType:注册该自定义消息类型,
     并在聊天界面中通过registerClass:forCellWithReuseIdentifier:注册该自定义消息的Cell,否则将此回调将不会被调用。
     */
    - (RCMessageBaseCell *)rcConversationCollectionView:(UICollectionView *)collectionView
                                 cellForItemAtIndexPath:(NSIndexPath *)indexPath;
    
    /*!
     自定义消息Cell显示的回调
    
     @param collectionView          当前CollectionView
     @param collectionViewLayout    当前CollectionView Layout
     @param indexPath               该Cell对应的消息Cell数据模型在数据源中的索引值
     @return                        自定义消息Cell需要显示的高度
    
     @discussion 自定义消息如果需要显示,则必须先通过RCIM的registerMessageType:注册该自定义消息类型,
     并在聊天界面中通过registerClass:forCellWithReuseIdentifier:注册该自定义消息的Cell,否则将此回调将不会被调用。
     */
    - (CGSize)rcConversationCollectionView:(UICollectionView *)collectionView
                                    layout:(UICollectionViewLayout *)collectionViewLayout
                    sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
    

    3、发送消息

    完成了自定义消息和 cell 的定义与注册后,调用 RCIM 的 sendMessage 方法,来发送自定义消息。如果是 server 端来发送,须定义同样的消息类型。请注意,自定义的消息,需要您自己设置 pushContent 来定义推送内容,否则将不会进行远程推送。

    功能进阶

    红包

    注意事项:融云 SDK 的 IM 红包功能是由一路魔方科技提供的服务,若您的 App 之前已集成过一路魔方的红包 SDK,为了保证两个平台数据兼容和一致性,请提交工单咨询帮助,感谢您的配合。
    点击查看红包使用说明书红包业务常见问题
    您可以在开发者后台-数据统计-红包统计中查看红包的数据统计情况。

    只要按照下面的步骤正常的集成了红包 SDK,那么就可以在 App 中正常使用红包功能,不需要再申请开通。

    红包 SDK 从 2.8.5 版本开始支持 HTTPS,如果您使用的是 2.8.2 至 2.8.4 版本的红包 SDK,那么请参考红包 SDK 的 ATS 设置

    开始集成扩展模块需要以下几步:

    一、把红包 SDK 文件夹拷贝到项目文件夹下,并导入到项目工程中。

    二、Build Settings 中 Other Linker Flags 添加 -ObjC 。

    三、添加系统依赖库:

    • CoreMotion.framework
    • CoreText.framework

    四、从红包 SDK 2.8.5 版本开始支持 HTTPS,如果需要使用 HTTP 协议,需要在 App 项目的 plist 手动添加以下 key 和 value 来支持。

    <key>NSAppTransportSecurity</key>
      <dict>
    <key>NSAllowsArbitraryLoads</key>
       <true/>
      </dict>
    

    五、需要在 AppDelegate.m 中加入如下代码处理:

    1. Application:didFinishLaunchingWithOptions: 函数中初始化融云之后,为红包扩展设置 URL scheme。

      //初始化融云SDK
      [[RCIM sharedRCIM] initWithAppKey:RONGCLOUD_IM_APPKEY];
      //设置红包扩展的Url Scheme。
      [[RCIM sharedRCIM] setScheme:@"rongcloudRedPacket" forExtensionModule:@"JrmfPacketManager"];
      

      说明:

      scheme: 支付宝支付成功后,回调您的应用时使用,如果有另一个 App 与您的 scheme 相同,则红包发送失败,建议用应用名+功能名组合模式定义您的 scheme;如果您在使用红包之前已经使用了支付宝,那么红包的 scheme 要和之前支付宝的 scheme 分开,保证红包的 scheme 仅用于红包。

      extensionModule:红包 SDK 默认值为 JrmfPacketManager 不能修改。

    2. 在 Appdelegate.m 中添加 openUrl 处理函数。

      - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options {
      if ([[RCIM sharedRCIM] openExtensionModuleUrl:url]) {
        return YES;
      }
      return YES;
      }
      
      - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
      if ([[RCIM sharedRCIM] openExtensionModuleUrl:url]) {
        return YES;
      }
      return YES;
      }
      
    3. 最后在 Info.plist 添加对应的 scheme(Scheme 为下述支付宝回调参数)。

    image

    六、支持支付宝支付,需将 AlipaySDK 文件夹如上导入,并在 Build Phases 选项卡的 Link Binary With Libraries 中,增加以下依赖:

    image

    七、在 App 合适位置添加“我的钱包”入口,在调用钱包时,需要引入头文件:

    低于 2.8.5 版本

    #import <JrmfPacketKit/JrmfPacketManager.h>
    

    调用方法:

    [JrmfPacketManager getEventOpenWallet];
    

    2.8.5 及更高版本

    #import <JrmfWalletKit/JrmfWalletKit.h>
    

    调用方法:

    [JrmfWalletSDK openWallet];
    

    获取红包版本的方法

    #import <JrmfPacketKit/JrmfPacketManager.h>
    

    调用方法:

    [JrmfPacketManager getCurrentVersion];
    

    八、如想在群组中实现红包功能,需要实现以下代理,提供群组成员数据给融云 SDK:

    //RCIM Class
    
    /*!
    群组成员列表提供者
    */
    @protocol RCIMGroupMemberDataSource <NSObject>
    @optional
    
    /*!
    获取当前群组成员列表的回调
    
    @param groupId     群ID
    @param resultBlock 获取成功 [userIdList:群成员ID列表]
    */
    - (void)getAllMembersOfGroup:(NSString *)groupId
    result:(void (^)(NSArray<NSString *> *userIdList))resultBlock;
    @end
    

    动态表情

    默认表情

    融云默认支持表情 MM 的动态表情下载,开发者只需要在表情云官网下载 IM 版表情 SDK,导入即可。

    关于表情自定义

    如果您想自定可以其它表情,详细查看输入区域自定义

    群组、讨论组 @ 功能

    从 2.6.8 版本开始,群组、讨论组中支持 @ 功能,满足您 @ 指定用户或 @ 所有人的需求,在 RCIM.h 中通过设置 enableMessageMentioned 开启 @ 消息功能,默认为关闭状态。

    //RCIM Class
    
    /*!
    是否开启消息@提醒功能(只支持群组和讨论组),默认值是 NO。
    */
    @property(nonatomic, assign) BOOL enableMessageMentioned;
    

    RCIM.h 中定义了提供 @ 选择用户展示的群组成员数据的代理,需要实现如下代理,提供数据给选择用户的列表。

    //RCIM Class
    
    /*!
    群组成员列表提供者
    */
    @protocol RCIMGroupMemberDataSource <NSObject>
    @optional
    
    /*!
    获取当前群组成员列表的回调
    
    @param groupId     群ID
    @param resultBlock 获取成功 [userIdList:群成员ID列表]
    */
    - (void)getAllMembersOfGroup:(NSString *)groupId
    result:(void (^)(NSArray<NSString *> *userIdList))resultBlock;
    @end
    
    @ 所有人

    通过设置 RCMentionInfo 中的 RCMentionedType 实现 @ 所有人的功能,RCMentionInfo 定义如下:(注 sendmessage )

    //RCMentionedInfo Class
    
    /*!
    消息中的 @ 提醒信息
    */
    @interface RCMentionedInfo : NSObject
    
    /*!
    @提醒的类型,设置 @ 指定用户,还是 @ 所有人
    */
    @property(nonatomic, assign) RCMentionedType type;
    
    /*!
    @的用户ID列表
    
    @discussion 如果 type 是 @所有人,则可以传 nil
    */
    @property(nonatomic, strong) NSArray *userIdList;
    
    /*!
    包含 @ 提醒的消息,本地通知和远程推送显示的内容
    */
    @property(nonatomic, strong) NSString *mentionedContent;
    
    /*!
    初始化 @ 提醒信息
    
    @param type       @提醒的类型
    @param userIdList @的用户 ID 列表
    
    @return @提醒信息的对象
    */
    - (instancetype)initWithMentionedType:(RCMentionedType)type
    userIdList:(NSArray *)userIdList
    mentionedContent:(NSString *)mentionedContent;
    

    RCMentionedType 定义如下:

    //RCStatusDefine Class
    
    #pragma mark RCMentionedType - 消息中@提醒的类型
    /*!
     @提醒的类型
     */
    typedef NS_ENUM(NSUInteger, RCMentionedType) {
       /*!
       @所有人
       */
       RC_Mentioned_All = 1,
    
       /*!
       @部分指定用户
       */
       RC_Mentioned_Users = 2,
    };
    
    @ 消息推送会越过所有免打扰逻辑,给用户推送 Push 通知。

    如果您使用的是 IMLib SDK 集成,可参考 IMLib @ 功能文档

    消息撤回

    从 2.6.8 版本开始,支持消息撤回功能。在 RCIM.h 文件中,通过设置 enableMessageRecall 来开启该功能,默认为关闭状态。

    //RCIM Class
    
    /*!
     是否开启消息撤回功能,默认值是 NO。
     */
    @property(nonatomic, assign) BOOL enableMessageRecall;
    

    在 RCIM.h 文件中,通过 maxRecallDuration 设置消息可撤回的时间值,默认为 120 秒,表示消息发送后 120 秒内可以选择撤回该消息。

    //RCIM Class
    
    /*!
    消息可撤回的最大时间,单位是秒,默认值是 120s。
    */
    @property(nonatomic, assign) NSUInteger maxRecallDuration;
    

    调用以下接口,您也可以自己来实现撤回消息的功能:

    //RCIMClient Class
    
    /*!
     撤回消息并更新UI
    
     @param messageId 被撤回的消息Id
     @discussion 只有存储并发送成功的消息才可以撤回。
     */
    - (void)recallMessage:(long)messageId;
    

    消息阅读回执

    阅读回执功能设置

    IMKit SDK 中已实现阅读回执功能,默认为关闭状态,您可以通过 RCIM 的以下接口开启消息的阅读回执功能,目前仅支持单聊、群聊、讨论组。

    //RCIM Class
    
    /*!
     开启已读回执功能的会话类型,默认为空
    
     @discussion 这些会话类型的消息在会话页面显示了之后会发送已读回执。目前仅支持单聊、群聊和讨论组。
     */
    @property(nonatomic, copy) NSArray* enabledReadReceiptConversationTypeList;
    

    点击查看群组消息阅读功能产品介绍

    多端阅读消息数同步

    通过设置 enableSyncReadStatus 开启多端阅读消息数同步功能。默认为 NO 关闭状态。

    //RCIM Class
    
    /*!
     是否开启多端同步未读状态的功能,默认值是 NO
    
     @discussion 开启之后,用户在其他端上阅读过的消息,当前客户端会清掉该消息的未读数。目前仅支持单聊、群聊、讨论组。
     */
    @property(nonatomic, assign) BOOL enableSyncReadStatus;
    

    正在输入的状态提示

    如果聊天的用户正在输入文字或者正在录制语音消息,SDK 可以在会话页面的 NavigationBar 的 Title 中显示对方正在输入和对方正在讲话的提示。

    您可以通过 RCIM 的以下接口开启输入状态提醒的功能,目前仅支持单聊。

    // RCIM Class
    
    /*!
     是否开启发送输入状态,默认值是NO,开启之后在输入消息的时候对方可以看到正在输入的提示(目前只支持单聊)
     */
    @property(nonatomic, assign) BOOL enableTypingStatus;
    

    位置实时共享功能

    融云 IMKit 中提供位置实时共享功能,功能支持在单聊、群组、讨论组中使用。

    在群组、讨论组中加入位置实时共享的人数不能超过 5 个人,超过后加入不成功。

    下面以 Demo 中的代码为例,说明集成步骤。以下代码,可以在 SealTalk 的 RCDChatViewController.m 中找到。SealTalk 源码下载地址

    引入头文件

    //RCDChatViewController Class
    
    #import "RealTimeLocationEndCell.h"
    #import "RealTimeLocationStartCell.h"
    #import "RealTimeLocationStatusView.h"
    #import "RealTimeLocationViewController.h"
    

    设置协议和代理

    //RCDChatViewController Class
    
    @interface RCDChatViewController () < RCRealTimeLocationObserver,
        RealTimeLocationStatusViewDelegate>
    @property(nonatomic, weak) id<RCRealTimeLocationProxy> realTimeLocation;
    @property(nonatomic, strong)
        RealTimeLocationStatusView *realTimeLocationStatusView;
    

    注册自定义消息 cell

    //RCDChatViewController Class
    
      [self registerClass:[RealTimeLocationStartCell class]
          forMessageClass:[RCRealTimeLocationStartMessage class]];
      [self registerClass:[RealTimeLocationEndCell class]
          forMessageClass:[RCRealTimeLocationEndMessage class]];
    

    获取实时位置共享服务,并配置给当前类。

    //RCDChatViewController Class
    
    __weak typeof(&*self) weakSelf = self;
      [[RCRealTimeLocationManager sharedManager]
          getRealTimeLocationProxy:self.conversationType
          targetId:self.targetId
          success:^(id<RCRealTimeLocationProxy> realTimeLocation) {
            weakSelf.realTimeLocation = realTimeLocation;
            [weakSelf.realTimeLocation addRealTimeLocationObserver:self];
            [weakSelf updateRealTimeLocationStatus];
          }
          error:^(RCRealTimeLocationErrorCode status) {
            NSLog(@"get location share failure with code %d", (int)status);
          }];
    

    在点击位置时弹出位置实时共享

    //RCDChatViewController Class
    
    - (void)pluginBoardView:(RCPluginBoardView *)pluginBoardView
         clickedItemWithTag:(NSInteger)tag {
      switch (tag) {
      case PLUGIN_BOARD_ITEM_LOCATION_TAG: {
        if (self.realTimeLocation) {
          UIActionSheet *actionSheet = [[UIActionSheet alloc]
                       initWithTitle:nil
                            delegate:self
                   cancelButtonTitle:@"取消"
              destructiveButtonTitle:nil
                   otherButtonTitles:@"发送位置", @"位置实时共享", nil];
          [actionSheet showInView:self.view];
        } else {
          [super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
        }
    
      } break;
    

    点击 actionSheet 执行位置实时共享

    //RCDChatViewController Class
    
    - (void)actionSheet:(UIActionSheet *)actionSheet
        clickedButtonAtIndex:(NSInteger)buttonIndex {
      switch (buttonIndex) {
      case 0: {
        [super pluginBoardView:self.pluginBoardView
            clickedItemWithTag:PLUGIN_BOARD_ITEM_LOCATION_TAG];
      } break;
      case 1: {
        [self showRealTimeLocationViewController];
      } break;
      }
    }
    

    将上面所有方法以及方法中相关的的代码都拷贝到您的工程中,就可以正常使用位置实时共享功能了。

    ATS 集成与设置说明

    一、如果您使用的是不带红包的 2.8.2 及以上版本的 SDK 请参考以下设置:

    App 中的 Info.plist 文件可参考以下设置(开发者可根据自身业务情况调整设置):

    image

    如果使用 2.8.2 版本之前的 SDK,请参考 2.8.2 版本之前 ATS 设置

    其中:

    1. rongcloud-image.ronghub.com(可选)

      rongcloud-image.ronghub.com 是图片下载的 HTTP 域名。

      1.1、 如果您的 App 没有收发图片的功能,可以不增加这个白名单。

      1.2、 在开发者后台 App Key 页面开启 HTTPS,您可以选择以 HTTPS 的方式上传下载图片。需要注意的是,SDK 2.6.0 之前的版本不支持下载 HTTPS 图片。您可以在 2.6.0 以上版本存量到达一定程度之后,在开发者后台迁移 HTTPS,之后就不再需要增加这个白名单。

    2. rongcloud-file.ronghub.com(可选)

      rongcloud-file.ronghub.com 是文件下载的 HTTP 域名。

      2.1、 如果您的 App 没有收发文件的功能,可以不增加这个白名单。

      2.2、 在开发者后台 App Key 页面开启 HTTPS,您可以选择以 HTTPS 的方式上传下载文件。需要注意的是,SDK 2.6.0 之前的版本不支持下载 HTTPS 文件。您可以在 2.6.0 以上版本存量到达一定程度之后,在开发者后台迁移 HTTPS,之后就不再需要增加这个白名单。

    3. nav.cn.ronghub.com(可选)

      nav.cn.ronghub.com 是连接服务的 HTTP 域名。

      nav.cn.ronghub.com 连接服务支持并优先使用 HTTPS,但是考虑到国内网络环境的复杂性和某些特殊情况,加上了这个白名单,在 HTTPS 失败的情况下 SDK 会回落 HTTP 以保证连接,增强连接的稳定性。

      但是 NSExceptionDomains 在提交审核的时候,会触发苹果的审核 review,App 可以权衡 App 的使用场景和自己的 ATS 策略,自行决定是否增加这个白名单。

    4. public.rongcloud.cn(可选)

      public.rongcloud.cn 是公众账号服务的 HTTP 域名。

      public.rongcloud.cn 目前只支持 HTTP,如果开发中需要使用公众账号服务,那么必须添加白名单,如果没有公众账号服务那么可以忽略。

    5. NSAllowsArbitraryLoadsInWebContent 和 NSAllowsArbitraryLoads(可选)

      SDK 默认优先使用 SFSafariViewController 打开 URL 链接,App 审核的时候不需要额外说明。

      您也可以通过设置 RCIM 的 embeddedWebViewPreferred 属性,设置默认使用 WebView 打开 URL 链接,但是您需要在 Info.plist 中添加 NSAllowsArbitraryLoadsInWebContent 和 NSAllowsArbitraryLoads,并在 App 审核的时候提交额外的说明。

    NSAllowsArbitraryLoadsInWebContent 是为了用 WebView 可以打开任意 HTTP 链接,该参数只在 iOS 10 以上的系统生效。
    NSAllowsArbitraryLoads 是为了在 iOS 9 上用 WebView 可以打开任意 HTTP 链接,在 iOS 10 上,如果设置了 NSAllowsArbitraryLoadsInWebContent,会默认忽略 NSAllowsArbitraryLoads。
    // RCIM Class
    
    #pragma mark - 网页展示模式
    /*!
     点击Cell中的URL时,优先使用WebView还是SFSafariViewController打开。
    
     @discussion 默认为NO。
     如果设置为YES,将使用WebView打开URL链接,则您需要在App的Info.plist的NSAppTransportSecurity中增加NSAllowsArbitraryLoadsInWebContent和NSAllowsArbitraryLoads字段,并在苹果审核的时候提供额外的说明。
     如果设置为NO,将优先使用SFSafariViewController,在iOS 8及之前的系统中使用WebView,在审核的时候不需要提供额外说明。
     更多内容可以参考:https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW55
     */
    @property(nonatomic, assign) BOOL embeddedWebViewPreferred;
    

    二、如果您使用的是带红包功能的 SDK:

    红包 SDK 从 2.8.5 版本开始支持 HTTPS,如果您使用的是 2.8.2 至 2.8.4 版本的红包 SDK,那么请参考红包 SDK 的 ATS 设置

    Bitcode

    iOS 9 中,Apple 引入了新特性 Bitcode,用于改进版本编译提交。 因为针对 Bitcode,静态库的编译需要调整,所以 SDK 的版本有所区别。 如果您使用 Xcode6.X 版本,您可以使用 2.3.0 或 2.3.0 以下版本的 SDK;如果您使用 Xcode7.X 版本,您可以使用 SDK 2.3.1 或者 2.3.1 以上版本的 SDK。

    常见错误码及处理

    集成融云 SDK 过程中,如遇到问题可查看常见状态码及处理表