Files
peko-ios/docs/NIMSDK_Integration_Documentation.md

16 KiB
Raw Blame History

NIMSDK 集成说明文档

目录

1. 项目概述

1.1 项目简介

YuMi是一个基于iOS平台的社交应用使用Objective-C开发采用MVP架构模式。项目集成了网易云信SDKNIMSDK用于实现即时通讯功能。

1.2 技术栈

  • 开发语言: Objective-C
  • 架构模式: MVP (Model-View-Presenter)
  • 即时通讯: 网易云信 NIMSDK_LITE
  • 依赖管理: CocoaPods
  • 最低支持版本: iOS 11.0

1.3 主要功能模块

  • 用户登录注册
  • 即时消息通讯
  • 聊天室功能
  • 动态发布
  • 个人中心
  • 房间直播

2. NIMSDK导入配置

2.1 Podfile配置

在项目的Podfile中添加NIMSDK依赖

# 云信SDK
pod 'NIMSDK_LITE'

2.2 头文件导入

在需要使用NIMSDK的文件中导入头文件

#import <NIMSDK/NIMSDK.h>

2.3 项目结构

YuMi/
├── Appdelegate/
│   ├── AppDelegate.m                    # 主应用代理
│   └── AppDelegate+ThirdConfig.m        # 第三方SDK配置
├── Modules/
│   ├── YMMessage/                       # 消息模块
│   │   └── Tool/
│   │       └── CustomAttachmentDecoder  # 自定义消息解码器
│   ├── YMTabbar/                        # 主界面模块
│   └── YMRoom/                          # 房间模块
└── Global/
    └── YUMIConstant.h                   # 常量定义

3. NIMSDK初始化流程

3.1 初始化时序图

sequenceDiagram
    participant App as AppDelegate
    participant ThirdConfig as AppDelegate+ThirdConfig
    participant NIMSDK as NIMSDK
    participant Config as NIMSDKConfig
    participant Decoder as CustomAttachmentDecoder
    
    App->>ThirdConfig: initThirdConfig()
    ThirdConfig->>ThirdConfig: configNIMSDK()
    ThirdConfig->>NIMSDK: registerWithOption(option)
    ThirdConfig->>Decoder: registerCustomDecoder()
    ThirdConfig->>Config: shouldConsiderRevokedMessageUnreadCount = YES
    ThirdConfig->>Config: setShouldSyncStickTopSessionInfos(YES)
    ThirdConfig->>Config: enabledHttpsForInfo = NO (DEBUG)
    ThirdConfig->>Config: enabledHttpsForMessage = NO (DEBUG)
    
    Note over App: 应用启动完成
    App->>App: loadMainPage()
    App->>App: 检查登录状态
    App->>NIMSDK: 自动登录或手动登录

3.2 初始化代码实现

3.2.1 主应用代理初始化

// AppDelegate.m
- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 初始化第三方SDK配置
    [self initThirdConfig];
    
    // 其他初始化代码...
    
    return YES;
}

3.2.2 NIMSDK配置方法

// AppDelegate+ThirdConfig.m
- (void)configNIMSDK {
    // 1. 获取云信AppKey
    NSString *appKey = KeyWithType(KeyType_NetEase);
    NIMSDKOption *option = [NIMSDKOption optionWithAppKey:appKey];
    
    // 2. 配置APNS证书名称
    #ifdef DEBUG
        option.apnsCername = @"pikoDevelopPush";
    #else
        option.apnsCername = @"newPiko";
    #endif
    
    // 3. 注册SDK
    [[NIMSDK sharedSDK] registerWithOption:option];
    
    // 4. 注册自定义消息解码器
    [NIMCustomObject registerCustomDecoder:[[CustomAttachmentDecoder alloc] init]];
    
    // 5. 配置SDK参数
    [NIMSDKConfig sharedConfig].shouldConsiderRevokedMessageUnreadCount = YES;
    [[NIMSDKConfig sharedConfig] setShouldSyncStickTopSessionInfos:YES];
    
    // 6. DEBUG模式下禁用HTTPS
    #ifdef DEBUG
        [NIMSDKConfig sharedConfig].enabledHttpsForInfo = NO;
        [NIMSDKConfig sharedConfig].enabledHttpsForMessage = NO;
    #endif
}

4. 关键配置参数说明

4.1 AppKey配置

项目支持多环境配置,通过YUMIConstant.m中的KeyWithType方法获取:

// 测试环境
@(KeyType_NetEase): @"79bc37000f4018a2a24ea9dc6ca08d32"

// 生产环境  
@(KeyType_NetEase): @"7371d729710cd6ce3a50163b956b5eb6"

4.2 APNS推送配置

// 开发环境
option.apnsCername = @"pikoDevelopPush";

// 生产环境
option.apnsCername = @"newPiko";

4.3 SDK配置参数详解

参数 说明
shouldConsiderRevokedMessageUnreadCount YES 撤回消息计入未读数
shouldSyncStickTopSessionInfos YES 同步置顶会话信息
enabledHttpsForInfo NO (DEBUG) DEBUG模式禁用HTTPS信息传输
enabledHttpsForMessage NO (DEBUG) DEBUG模式禁用HTTPS消息传输

5. 自定义消息处理

5.1 自定义附件解码器

5.1.1 解码器接口定义

// CustomAttachmentDecoder.h
@interface CustomAttachmentDecoder : NSObject<NIMCustomAttachmentCoding>
@end

5.1.2 解码器实现

// CustomAttachmentDecoder.m
- (id<NIMCustomAttachment>)decodeAttachment:(NSString *)content {
    id<NIMCustomAttachment> attachment;
    NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
    
    if (data) {
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
                                                             options:0
                                                               error:nil];
        if ([dict isKindOfClass:[NSDictionary class]]) {
            int first = [dict[@"first"] intValue];
            int second = [dict[@"second"] intValue];
            id originalData = dict[@"data"];

            AttachmentModel *model = [[AttachmentModel alloc] init];
            model.first = (short)first;
            model.second = (short)second;
            model.data = originalData;
            
            attachment = model;
        }
    }
    return attachment;
}

5.2 消息类型定义

自定义消息通过AttachmentModel定义结构:

@interface AttachmentModel : NSObject<NIMCustomAttachment>

@property (nonatomic, assign) short first;   // 消息类型标识
@property (nonatomic, assign) short second;  // 消息子类型标识
@property (nonatomic, strong) id data;       // 消息数据内容

@end

5.3 消息类型示例

// 打招呼消息
if (attachment.first == CustomMessageType_FindNew && 
    attachment.second == Custom_Message_Find_New_Greet_New_User) {
    // 处理新用户打招呼消息
    FindNewGreetMessageModel *greetInfo = [FindNewGreetMessageModel modelWithDictionary:attachment.data];
    // 显示打招呼弹窗
}

6. 登录管理

6.1 登录状态检查

// 检查是否已登录
if ([NIMSDK sharedSDK].loginManager.isLogined) {
    // 已登录,执行相关操作
} else {
    // 未登录,跳转登录页面
}

6.2 自动登录处理

// NIMLoginManagerDelegate
- (void)onAutoLoginFailed:(NSError *)error {
    // 如果非上次登录设备 autoLogin 会返回 417
    if (error.code == 417) {
        @weakify(self);
        AccountModel* accountModel = [AccountInfoStorage instance].getCurrentAccountInfo;
        [[NIMSDK sharedSDK].loginManager login:accountModel.uid 
                                         token:accountModel.netEaseToken 
                                    completion:^(NSError * _Nullable error) {
            if (error) {
                @strongify(self);
                [self.presenter logout];
            }
        }];
        return;
    }
    [self.presenter logout];
}

6.3 踢出处理

// NIMLoginManagerDelegate
- (void)onKickout:(NIMLoginKickoutResult *)result {
    // 显示踢出提示
    [XNDJTDDLoadingTool showErrorWithMessage:YMLocalizedString(@"TabbarViewController0")];
    
    // 清理房间状态
    if ([XPRoomMiniManager shareManager].getRoomInfo) {
        [[RtcManager instance] exitRoom];
        [[NIMSDK sharedSDK].chatroomManager exitChatroom:roomId completion:nil];
        [self.roomMineView hiddenRoomMiniView];
    }
    
    // 执行登出
    [self.presenter logout];
}

6.4 手动登录

// 执行登录
[[NIMSDK sharedSDK].loginManager login:accountModel.uid 
                                 token:accountModel.netEaseToken 
                            completion:^(NSError * _Nullable error) {
    if (error) {
        // 登录失败处理
        NSLog(@"登录失败: %@", error);
    } else {
        // 登录成功处理
        NSLog(@"登录成功");
    }
}];

7. 消息接收处理

7.1 消息接收代理

// NIMChatManagerDelegate
- (void)onRecvMessages:(NSArray<NIMMessage *> *)messages {
    if ([AccountInfoStorage instance].getTicket.length == 0) {
        return;
    }
    
    for (NIMMessage *message in messages) {
        if (message.session.sessionType == NIMSessionTypeP2P) {
            if (message.messageType == NIMMessageTypeCustom) {
                NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
                if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
                    AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
                    // 处理自定义消息
                    [self handleCustomMessage:attachment];
                }
            }
        }
    }
}

7.2 自定义消息处理

- (void)handleCustomMessage:(AttachmentModel *)attachment {
    switch (attachment.first) {
        case CustomMessageType_FindNew:
            [self handleFindNewMessage:attachment];
            break;
        case CustomMessageType_Gift:
            [self handleGiftMessage:attachment];
            break;
        default:
            break;
    }
}

- (void)handleFindNewMessage:(AttachmentModel *)attachment {
    if (attachment.second == Custom_Message_Find_New_Greet_New_User) {
        FindNewGreetMessageModel *greetInfo = [FindNewGreetMessageModel modelWithDictionary:attachment.data];
        if (greetInfo.uid.integerValue != [AccountInfoStorage instance].getUid.integerValue) {
            // 显示打招呼弹窗
            [self showGreetAlert:greetInfo];
        }
    }
}

7.3 广播消息处理

// NIMBroadcastManagerDelegate
- (void)onReceiveBroadcastMessage:(NIMBroadcastMessage *)broadcastMessage {
    if ([AccountInfoStorage instance].getUid.length == 0) {
        return;
    }
    
    // 处理广播消息
    NSString *content = broadcastMessage.content;
    // 解析并处理广播内容
}

8. 推送通知集成

8.1 推送权限申请

- (void)registerNot {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | 
                                                 UNAuthorizationOptionBadge | 
                                                 UNAuthorizationOptionSound) 
                              completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (granted) {
                [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
                    if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [[UIApplication sharedApplication] registerForRemoteNotifications];
                        });
                    }
                }];
            }
        }];
    }
}

8.2 设备Token更新

- (void)application:(UIApplication *)app 
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // 上传devicetoken至云信服务器
    [[NIMSDK sharedSDK] updateApnsToken:deviceToken];
}

8.3 推送消息处理

- (void)application:(UIApplication *)application 
didReceiveRemoteNotification:(NSDictionary *)userInfo 
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    NSString *data = userInfo[@"data"];
    if (data) {
        NSDictionary *dataDic = [data mj_JSONObject];
        NSString *userId = dataDic[@"uid"];
        if (userId) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), 
                          dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:kOpenRoomNotification 
                                                                    object:nil 
                                                                  userInfo:@{@"type": @"kOpenChat", 
                                                                             @"uid": userId, 
                                                                             @"isNoAttention": @(YES)}];
                ClientConfig *config = [ClientConfig shareConfig];
                config.pushChatId = userId;
            });
            return;
        }
    }
    
    completionHandler(UIBackgroundFetchResultNewData);
}

8.4 应用状态处理

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // 设置应用角标为未读消息数
    NSInteger count = [NIMSDK sharedSDK].conversationManager.allUnreadCount;
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // 应用激活时清除角标
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"kAppDidBecomeActive" object:nil];
}

9. 最佳实践

9.1 初始化最佳实践

  1. 在应用启动时初始化: 在AppDelegatedidFinishLaunchingWithOptions中调用
  2. 配置环境参数: 区分开发和生产环境的配置
  3. 注册自定义解码器: 在SDK注册后立即注册自定义解码器
  4. 设置代理: 在适当的时机添加和移除代理

9.2 登录管理最佳实践

  1. 自动登录: 优先使用自动登录,减少用户等待时间
  2. 错误处理: 对登录失败进行适当的错误处理和重试
  3. 状态同步: 保持本地登录状态与服务器状态同步
  4. 踢出处理: 妥善处理被踢出的情况,清理相关状态

9.3 消息处理最佳实践

  1. 消息过滤: 根据业务需求过滤不需要的消息
  2. 自定义消息: 合理设计自定义消息结构
  3. 性能优化: 避免在消息处理中进行耗时操作
  4. 内存管理: 及时释放不需要的消息对象

9.4 推送通知最佳实践

  1. 权限申请: 在合适的时机申请推送权限
  2. Token更新: 及时更新设备Token
  3. 消息解析: 正确解析推送消息内容
  4. 状态处理: 根据应用状态处理推送消息

10. 常见问题

10.1 初始化问题

Q: SDK初始化失败怎么办 A: 检查AppKey是否正确网络连接是否正常证书配置是否正确。

Q: 自定义解码器注册失败? A: 确保在SDK注册后注册解码器解码器类实现了正确的协议。

10.2 登录问题

Q: 自动登录失败错误码417 A: 这是正常情况,表示非上次登录设备,需要重新输入账号密码登录。

Q: 登录后立即被踢出? A: 检查账号是否在其他设备登录或者Token是否过期。

10.3 消息问题

Q: 自定义消息无法解析? A: 检查消息格式是否正确,解码器是否正确注册。

Q: 消息发送失败? A: 检查网络连接,登录状态,以及消息内容格式。

10.4 推送问题

Q: 推送通知无法接收? A: 检查推送权限是否开启证书配置是否正确设备Token是否正确上传。

Q: 推送消息解析错误? A: 检查推送消息格式,确保解析逻辑正确。

11. 总结

本项目对NIMSDK的集成非常完整包括

  1. 完整的初始化流程: 从SDK注册到配置参数设置
  2. 自定义消息处理: 实现了自定义附件解码器
  3. 登录状态管理: 包含自动登录、踢出处理等
  4. 推送通知集成: 完整的APNS推送处理
  5. 消息接收处理: 支持自定义消息类型的处理
  6. 多环境配置: 区分开发和生产环境的配置

整个集成方案遵循了NIMSDK的最佳实践代码结构清晰功能完整为项目的即时通讯功能提供了坚实的基础。


文档版本: 1.0
最后更新: 2024年12月
维护人员: 开发团队