本文介绍了在一个新项目中实现跨APP即时通讯的过程,作者选择了容云(而非环信)作为第三方框架。文章详细说明了从注册获取App Key、下载SDK、SDK集成、到IMLib和IMKit使用的全过程。其中,涉及了SDK的手动集成、初始化与连接服务器、会话列表和会话页面的快速启动与定制、用户信息的获取与显示、消息缓存与推送设置等关键环节。特别提到了在Xcode和iOS新版本中的注意事项,如系统库依赖变更,以及如何通过容云SDK处理用户信息、消息缓存和推送等核心功能。
以前做过聊天的项目,群聊和单聊都是在一个项目中进行的,这次的项目需求是在不同的APP中实现即时通讯,想着很复杂的样子,没想到实现起来那么容易。
一.选择第三方框架(环信?,容云?)
上次做聊天功能时用的容云,这次本想着用下环信,看环信API时,真心感到累!不能浪费太长时间,于是最后的决定还是容云吧!
二.注册获得App Key和下载SDK
注册登录后,在容云的控制台获得App Key。
2.选择自己需要的功能去官网下载下载SDK
三.SDK 集成(手动集成)
1.把下载IMLib和IMKit 手动拖到自己的项目中。
2.添加系统库依赖
注:苹果在 XCode10 和 iOS12 中移除了 libstdc++ 这个库,由 libc++ 这个库取而代之。
在 Xcode 项目 Build Settings -> Other Linker Flags 中,增加 "-ObjC"。
在 Xcode 项目 Build Settings->Search Paths 中SDK路径是否正确。
四.IMLib和IMKit 的使用
初始化
在AppDelegate.m文件中引入头文件#import <RongIMKit/RongIMKit.h>
//初始化融云SDK appKey即是在容云控制台获得的 [[RCIM sharedRCIM] initWithAppKey:@“3argexb6**ve”];
2.连接融云服务器
[[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错误"); }];
注:
(1).如何获取token?
必须在服务器端请求 Token,因为获取 Token 时需要提供 App Key 和 App Secret。如果在客户端请求 Token,假如 App 代码一旦被反编译,则会导致 App Key和App Secret 泄露。作为前端的我们需要Token来做测试,容云已经想到这一问题,为我们提供API调试,获取token。
(2).登录完成后,记得回到主线程
// 在主线程更新UIdispatch_sync(dispatch_get_main_queue(), ^(){ // 就是这里进行页面跳转啊,刷新UI });
会话列表
直接用RCConversationListViewController 或者继承它创建一个控制器,即可快速启动和使用会话列表界面。
- (void)viewDidLoad { [super viewDidLoad]; //设置需要显示哪些类型的会话 [self setDisplayConversationTypes:@[@(ConversationType_PRIVATE), @(ConversationType_GROUP)]]; //设置需要将哪些类型的会话在会话列表中聚合显示 [self setCollectionConversationType:@[@(ConversationType_PRIVATE), @(ConversationType_GROUP)]]; }
注:
您需要设置在会话列表界面显示哪些类型的会话,以及将哪些类型的会话在会话列表中聚合显示。
聚合显示指的是此类型所有会话,在会话列表中聚合显示成一条会话,点击进去会显示此类型的具体会话列表。
/*! 点击Cell头像的回调 @param model 会话Cell的数据模型 */- (void)didTapCellPortrait:(RCConversationModel *)model;/*! 长按Cell头像的回调 @param model 会话Cell的数据模型 */- (void)didLongPressCellPortrait:(RCConversationModel *)model;
4.会话页面
融云 IMKit 中已经实现了完整的会话页面,包含发送、接收、更新等 UI,并覆盖常用的 IM 交互场景,您直接使用或继承 RCConversationViewController,即可快速启动和使用会话页面。创建一个 ChatViewController控制器 继承 RCConversationViewController,点击会话列表时进入会话页面方法
/** *重写RCConversationListViewController的onSelectedTableRow事件 * * @param conversationModelType 数据模型类型 * @param model 数据模型 * @param indexPath 索引 */-(void)onSelectedTableRow:(RCConversationModelType)conversationModelType conversationModel:(RCConversationModel *)model atIndexPath:(NSIndexPath *)indexPath { ChatViewController *conversationVC = [[ChatViewController alloc]init]; conversationVC.conversationType =model.conversationType; conversationVC.targetId = model.targetId; conversationVC.title = model.conversationTitle; conversationVC.hidesBottomBarWhenPushed = YES; [self.navigationController pushViewController:conversationVC animated:YES]; }
注:
(1).用户信息如何传给容云?会话列表上的用户头像和昵称如何显示?
userInfoDataSource 和 groupMemberDataSource两个代理会为我们完美的解决显示用户信息的问题。
设置代理:
[RCIM sharedRCIM].userInfoDataSource =self; [RCIM sharedRCIM].groupMemberDataSource =self;
实现代理的方法:
/** *此方法中要提供给融云用户的信息,建议缓存到本地,然后改方法每次从您的缓存返回 */- (void)getUserInfoWithUserId:(NSString *)userId completion:(void(^)(RCUserInfo* userInfo))completion { //此处为了演示写了一个用户信息 if ([@"123456" isEqual:userId]) { RCUserInfo *user = [[RCUserInfo alloc]init]; user.userId = @"123456"; user.extra = @"全国总店"; user.name = @"dddd"; user.portraitUri = @"https://kxlj.oss-cn-hangzhou.aliyuncs.com/diary-pic/15547206992190"; return completion(user); }else if([@"222222" isEqual:userId]) { RCUserInfo *user = [[RCUserInfo alloc]init]; user.userId = @"222222"; user.name = @"用户3"; user.portraitUri = @"https://kxlj.oss-cn-hangzhou.aliyuncs.com/diary-pic/15547206992190"; return completion(user); } }
当需要显示用户信息的时候,IMKit自己会走这个方法去查询用户信息,然后显示在适当的位置。
(2).点击头像的方法?
/*! 点击Cell中头像的回调 @param userId 点击头像对应的用户ID */- (void)didTapCellPortrait:(NSString *)userId;/*! 长按Cell中头像的回调 @param userId 头像对应的用户ID */- (void)didLongPressCellPortrait:(NSString *)userId;
消息缓存
IMKit是如何缓存的呢?怎么才能在断网的时候显示聊天记录呢?我们所担心的这些,云容都已经想到方法,在此不得不佩服容云开发者的强大。
[RCIM sharedRCIM].enablePersistentUserInfoCache = YES;
在AppDelegate里面如果设置enablePersistentUserInfoCache设置为NO,则SDK在需要显示用户信息时,会调用用户信息提供者获取用户信息并缓存到Cache,此Cache在App生命周期结束时会被移除,下次启动时会再次通过用户信息提供者获取信息。
如果设置为YES,则会将获取到的用户信息持久化存储在本地,App下次启动时Cache会仍然有效。
/*! 获取SDK中缓存的用户信息 @param userId 用户ID @return SDK中缓存的用户信息 */- (RCUserInfo *)getUserInfoCache:(NSString *)userId;
在我们服务端获取token成功之后我们会去连接融云的服务器,断网的时候当然我们不能获取到token了。
我们需要在获取token成功之后缓存一下这个token,
[NSUserDefaults standardUserDefaults] setObject:token forKey:KRongCloudToken];
再次登录的时候,如果获取token失败了,那在登录失败的方法中用缓存的token连接融云服务器,根据UserId拿到用户信息。
消息推送
根据官网推送说明在容云控制台上传自己生成的推送证书。
在代码中请求远程推送权限
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; /** * 推送处理1 */ if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) { //注册推送, iOS 8 UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil]; [application registerUserNotificationSettings:settings]; } else { UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound; [application registerForRemoteNotificationTypes:myTypes]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMessageNotification:) name:RCKitDispatchMessageNotification object:nil];
需要将 didRegisterForRemoteNotificationsWithDeviceToken: 获取到的 deviceToken ,转为 NSString 类型,并去掉其中的空格和尖括号,传给 SDK。
SDK 会将此 deviceToken 保存,在 Connect 时会自动上传到融云服务器,并与用户 ID 绑定,用于远程推送。
/** * 将得到的devicetoken 传给融云用于离线状态接收push ,您的app后台要上传推送证书 * * @param application <#application description#> * @param deviceToken <#deviceToken description#> */- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""]; [[RCIMClient sharedRCIMClient] setDeviceToken:token]; }
五.功能拓展
两个APP端之间如何聊天?
我们只需要申请一套 App Key / App Secrect,提供给两个 App 使用即可。上线前,您需要在开发者平台上填写这两个应用的包名(Bundle Identifier)即可。
在测试中我们只需要在API调试中,申请两个Token,一个是目标的userId,一个是自己。也同样在以下方法中实现用户信息的刷新。
设置 [[RCIM sharedRCIM] setUserInfoDataSource:self]; - (void)getUserInfoWithUserId:(NSString *)userId completion:(void(^)(RCUserInfo* userInfo))completion{}
聊天列表头布局显示,设计如下图
在聊天列表上面显示
因为我们自己创建的控制器是集成RCConversationListViewController的,所以我们可以当成tableView的tableHeaderView。
self.conversationListTableView.tableHeaderView = self.tableHeaderView; //设置tableView头部 self.conversationListTableView.tableFooterView = [UIView new]; //这样就不会显示多余的分割线啦 self.conversationListTableView.backgroundColor = [UIColor whiteColor]; self.conversationListTableView.layoutMargins = UIEdgeInsetsZero; self.conversationListTableView.separatorInset = UIEdgeInsetsZero; self.topCellBackgroundColor = [UIColor blueColor]; // 置顶的消息背景颜色,
3.会话列表页,cell的左滑显示 置顶,表为未读,删除
直接用tableView的代理方法
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { RCConversationModel *model = self.conversationListDataSource[indexPath.row]; UITableViewRowAction *deleteRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[RCIMClient sharedRCIMClient] removeConversation:ConversationType_PRIVATE targetId:model.targetId]; [self refreshConversationTableViewIfNeeded]; }]; UITableViewRowAction *readRoWAction; if (model.unreadMessageCount > 0) { readRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"标为已读" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[RCIMClient sharedRCIMClient] clearMessagesUnreadStatus:ConversationType_PRIVATE targetId:model.targetId]; [self refreshConversationTableViewIfNeeded]; }]; } else { readRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"标为未读" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[RCIMClient sharedRCIMClient] setMessageReceivedStatus:model.lastestMessageId receivedStatus:ReceivedStatus_UNREAD]; [self refreshConversationTableViewIfNeeded]; // 刷新tableView }]; } UITableViewRowAction *topRoWAction; if (model.isTop) { topRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"取消置顶" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[RCIMClient sharedRCIMClient] setConversationToTop:1 targetId:model.targetId isTop:NO]; [self refreshConversationTableViewIfNeeded]; }]; topRoWAction.backgroundColor = [UIColor redColor]; } else { topRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"置顶" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[RCIMClient sharedRCIMClient] setConversationToTop:1 targetId:model.targetId isTop:YES]; [self refreshConversationTableViewIfNeeded]; }]; topRoWAction.backgroundColor = [UIColor redColor]; } return @[deleteRoWAction, readRoWAction, topRoWAction]; }
设置聊天的头像为圆形
[RCIMsharedRCIM].globalConversationAvatarStyle =RC_USER_AVATAR_CYCLE; //设置头像是圆形还是方形,默认是方形,聊天列表 [RCIMsharedRCIM].globalConversationPortraitSize =CGSizeMake(50,50); //设置头像大小,默认是46*46,设置时必须大于36*36,否则无效
5.获取对话列表
NSArray *array = [NSArray arrayWithObjects:[NSNumber numberWithInteger:ConversationType_PRIVATE] ,[NSNumber numberWithInteger:ConversationType_DISCUSSION],nil]; NSArray *listArray = [[RCIMClient sharedRCIMClient] getConversationList:array];
六.遇到的问题
在聊天列表页,总是自己的信息不能显示出来,但在另一个APP上信息显示正常,并且没有发现两者什么不同,表示很郁闷,于是自己用了一个简单粗暴的方法,更新SDK中的用户信息缓存。
//刷新融云对应用户信息 RCUserInfo *user = [[RCUserInfo alloc]init]; user.userId = @"666666"; user.name = @"用户2"; user.portraitUri = @"https://ss0.baidu.com/73t1bjeh1BF3odCf/it/u=1756054607,4047938258&fm=96&s=94D712D20AA1875519EB37BE0300C008"; [[RCIM sharedRCIM]refreshUserInfoCache:user withUserId:userId];
因为项目目前需求为单聊,以后会有群聊,聊天室,后续加上此功能。
本篇独发金蝶云社区
推荐阅读