前言

这篇教程主要简略介绍 NIM demo,方便第三方开发者熟悉 demo 代码结构,迅速找到各个功能点入口。但需要注意的是 demo 仅展现相应功能的主要实现流程,但并不一定是最佳实践,在代码规范,业务逻辑实现方面 demo 有自己的缺陷,请忽略。

工程介绍

工程结构

代码结构

NIM demo 代码顶级结构如下:

内分为五个功能模块:联系人 (Contact),名片 (Card),最近会话 (Session List),会话 (Session) 登录 (Login) 和 设置 (Setting)。每个功能模块按照 MVC 模式划分,部分模块还有一些 Util 类。由于 NIM SDK 已经提供比较完善的数据管理功能,所以上层模块基本不需要做管理模类。

自定义 View

通用 Category

通用辅助类

功能点指引

SDK 初始化

SDK 的初始化主要在 AppDelegate 的 application:didFinishLaunchingWithOptions: 中进行

如图,推荐在启动后立刻进行 SDK 的注册,如需 APNS 推送则同时进行 APNS 的注册,在正确获得 deviceToken 后设置给 SDK。

通常初次接入 SDK 时最头疼的问题就是 APNS 并没有起效,建议开发检查如下事项

登录

NIM SDK 有两种登录方式:手动登录和自动登录。demo 展示了两种登录的应用场景:

自动登录的意义在于提供一种用户友好的登录形式: APP 在启动时发现本地有用户账户名和密码的缓存,调用自动登录且打开主界面,用户不再需要进行一次登录的确认,并且能在无网的情况下获得会话列表信息。

如图

需要注意的是这这个时候需要有对象监听自动登录的回调,并在某些情况下给出相应操作(如密码错误等)。在 demo 中我们使用 AppDelegate 作为监听者。

手动登录的流程可以参考 NTESLoginViewController ,它接受用户输入并调用登录接口。

会话

demo 中会话相关的代码在 Classes/Sections/Session 下,几个关键的类是

会话流程

当选择一个用户发起会话后,demo 向 NavigationController 中推入NTESSessionViewController,而后主要做了如下一些事情

发起消息

使用 SDK 发起消息的流程非常简单,一般分为三步:

以发送图片为例,NTESSessionViewControllerUIImagePickerController 获取 UIImage,使用 NTESSessionMsgConverter 的方法打包成一个可以发送的 NIMMessage,并调用

[[[NIMSDK sharedSDK] chatManager] sendMessage:message toSession:session error:nil]

进行发送。而后进行回调处理即可

如图所见,NTESSessionViewController 主要处理三个消息发送的回调

接收消息

demo 中处理接受消息主要在 NIMKitNIMSessionViewController 中,收到消息会通过 chatManageronRecvMessages: 方法进行回调。一般而言 NIMSessionViewController 只需要处理当前session的消息即可,对于非当前 session 的消息直接丢弃即可。

对于带有附件的消息类型(图片,音频,视频),SDK 在第一次收到消息时会自动获取对应的附件(图片和视频的缩略图和音频原文件),上层只需要监听获取附件的回调即可。

如果发生错误上层 APP 也可以主动调用 fetchMessageAttach:error 进行重试。

最近消息

在NIM SDK 中管理最近消息的类是 NIMConversationManager,而 demo 中和它互动的 ViewController 是 NTESSessionListViewController , 继承自 NIMSessionListViewController ,他们的关系非常简单:NTESSessionListViewController 初始化时获取 NIMConversationManager 中所有的会话信息,并进行显示,且在最近消息有变化时进行处理即可。

最近消息在如下情况下会发生变化

但上层应用只需要管理最近消息变化即可,不需要关心引起它变化的源头,换言之消息和最近消息对于上层而言完全是两个不同的概念,尽量不要混淆。 在 NIMConversationManager 中可以看到demo对最近消息发生变化的回调处理

聊天室

在NIM SDK 中管理聊天室的类是 NIMChatroomManager , demo 中将聊天室包装成了直播间的模式。 直播间列表页为 NTESChatroomListViewController。页面将向应用服务器做聊天室房间列表请求,具体的代码在 NTESDemoService.m 中:

- (void)runTask:(id<NTESDemoServiceTask>)task
{
    if ([[NIMSDK sharedSDK] isUsingDemoAppKey])
    {
        NSURLRequest *request = [task taskRequest];
        [NSURLConnection sendAsynchronousRequest:request
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
                                   id jsonObject = nil;
                                   NSError *error = connectionError;
                                   if (connectionError == nil &&
                                       [response isKindOfClass:[NSHTTPURLResponse class]] &&
                                       [(NSHTTPURLResponse *)response statusCode] == 200)
                                   {
                                       if (data)
                                       {
                                           jsonObject = [NSJSONSerialization JSONObjectWithData:data
                                                                                        options:0
                                                                                          error:&error];
                                       }
                                   }
                                   [task onGetResponse:jsonObject
                                                 error:error];

                               }];
    }
    else
    {
        //Demo Service中我们模拟了APP服务器所应该实现的部分功能,上层开发需要构建相应的APP服务器,而不是直接使用我们的DEMO服务器
        [task onGetResponse:nil
                      error:[NSError errorWithDomain:@"ntes domain"
                                                code:-1
                                            userInfo:@{@"description" : @"use your own app server"}]];
    }
}

直播间的视图控制器为 NTESLiveViewController , NTESLiveViewController 是一个父视图控制器容器 , 包含了三个子视图控制器:

视图控制器 NTESChatroomViewController 继承自 NIMSessionViewController , 是一个特殊的会话页。这个会话页自己定义了部分会话设定和新的气泡排版样式。新的会话设定在 NTESChatroomConfig 中定义;新的气泡排版样式在 NTESChatroomCellLayoutConfig 中定义。目前会话页只处理文字和猜拳(自定义消息)两种消息类型。

视图控制器 NTESChatroomMemberListViewController 是一个聊天室成员列表,每次点击到这个列表的时候都会进行聊天室成员刷新。demo中,聊天室成员的排序为创建者,管理员,普通成员,受限成员,游客。由于 SDK 的接口无法直接给出所需要列表,demo 中做了多次请求合并以及排序的操作。请求合并的摘录代码为:

- (void)requestTeamMembers:(NIMChatroomMember *)lastMember handler:(NIMChatroomMembersHandler)handler{
    NIMChatroomMemberRequest *request = [[NIMChatroomMemberRequest alloc] init];
    request.roomId = self.chatrooom.roomId;
    request.lastMember = lastMember;
    request.type   = lastMember.type == NIMChatroomMemberTypeGuest ? NIMChatroomFetchMemberTypeTemp : NIMChatroomFetchMemberTypeRegularOnline;
    request.limit  = self.limit;
    __weak typeof(self) wself = self;
    [[NIMSDK sharedSDK].chatroomManager fetchChatroomMembers:request completion:^(NSError *error, NSArray *members) {
        if (!error)
        {
            if (members.count < wself.limit && request.type == NIMChatroomFetchMemberTypeRegularOnline) {
                //固定的没抓够,再抓点临时的充数
                NIMChatroomMemberRequest *req = [[NIMChatroomMemberRequest alloc] init];
                req.roomId = wself.chatrooom.roomId;
                req.lastMember = nil;
                req.type   = NIMChatroomFetchMemberTypeTemp;
                req.limit  = wself.limit;
                [[NIMSDK sharedSDK].chatroomManager fetchChatroomMembers:req completion:^(NSError *error, NSArray *tempMembers) {
                    NSArray *result;
                    if (!error) {
                        result = [members arrayByAddingObjectsFromArray:tempMembers];
                        if (result.count > wself.limit) {
                            result = [result subarrayWithRange:NSMakeRange(0, wself.limit)];
                        }
                    }
                    handler(error,result);
                }];
            }
            else
            {
                handler(error,members);
            }
        }
        else
        {
            handler(error,members);
        }
    }];
}

视图控制器 NTESLiveInfoViewController 是一个聊天室信息页面,每次点击到这个页面时回去抓取一遍聊天室信息和主播信息。相关的摘录代码为:

- (void)requestChatroom
{
    __weak typeof(self) wself = self;
    [[NIMSDK sharedSDK].chatroomManager fetchChatroomInfo:self.chatroom.roomId completion:^(NSError *error, NIMChatroom *chatroom) {
        if (!error)
        {
            wself.chatroom = chatroom;
            [wself refresh];
        }
    }];
}

- (void)requestMaster
{
    NIMChatroomMembersByIdsRequest *request = [[NIMChatroomMembersByIdsRequest alloc] init];
    request.roomId  = self.chatroom.roomId;
    request.userIds = @[self.chatroom.creator];
    __weak typeof(self) wself = self;
    [[NIMSDK sharedSDK].chatroomManager fetchChatroomMembersByIds:request completion:^(NSError *error, NSArray *members) {
        if (!error) {
            wself.master = members.firstObject;
            [wself refresh];
        }
    }];
}