融云即时通讯开发

写在开头:本片文章记录融云开发的流程以及开发过程中遇到的问题,由于项目中没有好友的概念,所以没有添加好友删除好友的功能,做的功能罗列如下:
1、集成sdk
2、注册登录
3、会话列表
4、会话页面
5、位置实时共享
6、头像昵称
7、未读角标
8、搜索历史记录

本人遇到的问题,相信大多数人都会遇到的问题在下面一一记录

一、集成sdk的时候遇到的问题
1、现在大多数iOS开发集成第三方都使用cocopods,很少用手动下载集成,一方面比较快捷方便,另一方面减少不比较的麻烦冲突。一般开发者都是想快速开发使用SDK自带的页面,那就
target 'MyApp' do
    pod 'RongCloudIM/IMKit', '~> 2.9.0'  
end
否则
target 'MyApp' do
    pod 'RongCloudIM/IMLib', '~> 2.9.0'
end
注意一点:后面的版本号最好用最新的,官网文档中写的不是最新的(2.8.3),不要直接复制粘贴过去就pod install,因为iOS(2.8.0之前老版本)和安卓版本差别大的话,可能会造成苹果和安卓互通音视频的时候出问题
2、还有可能出现冲突的问题,那就只能根据自己的情况去除重复文件了
二、连接服务器(登录融云)

//因为获取 Token 时需要提供 App Key 和 App Secret。如果在客户端请求 Token,假如您的 App 代码一旦被反编译,则会导致您的 App Key 和 App Secret 泄露。所以,务必在您的服务器端获取 Token,一句话:让后台登录接口返回融云的token,然后在登录成功后,调用下面的方法登录融云。
//登录融云
    [[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错误");
}];

注意:如果怕APP每次杀死在进入的时候会断开连接,所以在APPdelegate.m里面再调一次
三、会话列表
//融云 IMKit 已经实现了一个默认的会话列表视图控制器,您直接使用或继承 RCConversationListViewController,即可快速启动和使用会话列表界面。

- (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)]];
}

1、这个页面如果断开连接有个红色的提示栏,网络断开如果想去掉,在本类中写:
self.isShowNetworkIndicatorView = NO;
2、如果想在头部加东西
self.conversationListTableView.tableHeaderView = self.header;
3、点击cell处理事件
//重写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];
}
四、会话页面(单聊)
1、修改扩展内容中的图片和名字
//更新扩展部分的图片
[self.chatSessionInputBarControl.pluginBoardView updateItemWithTag:PLUGIN_BOARD_ITEM_ALBUM_TAG image:[UIImage imageNamed:@"actionbar_picture_icon"] title:@"照片"];
2、语音视频
//导入callkit
pod 'RongCloudRTC/RongCallKit', '~> 2.9.0'
注意:开通功能后自动显示出来
3、小视频(相册中可选视频)
从 SDK 2.8.29 版本开始支持小视频功能
下载小视频 SDK 将 RongSight.framework 编译连接到自己的项目里面就可以使用小视频功能,不需要写额外的代码。

/*!
 选择媒体资源时,是否包含视频文件,默认值是NO

 @discussion 默认是不包含
 */
@property(nonatomic, assign) BOOL isMediaSelectorContainVideo;


在APPdelegate.m中设置:
[[RCIM sharedRCIM] setIsMediaSelectorContainVideo:YES];
五、头像昵称
1、形状问题
    // 设置头像为圆形
    [RCIM sharedRCIM].globalMessageAvatarStyle = RC_USER_AVATAR_CYCLE;
    [RCIM sharedRCIM].globalConversationAvatarStyle = RC_USER_AVATAR_CYCLE;
在APPdelegate.m中设置,会话列表和会话页面中的头像变成圆形了、但是音视频的页面不会被修改的,如果你想修改那里的头像形状,就不能用cocopods来导入
pod 'RongCloudRTC/RongCallKit', '~> 2.9.0'
因为这样的话,页面是没有暴露出来的,就不能去修改。真的想修改,只能下载开源的callkit代码导入项目中,进行修改,只不过手动导入的话,可能还是会有一些不必要的麻烦。
2、头像
有两种情况,第一种是像demo中的一样头像是用背景色加上昵称的第一位字来生成一张image,存储到本地,进行展示。第二种是用自己的在线图片url。
第一种的做法:去demo中把图1这些类拉过来进行修改,保证不报错,能实现自己的功能,该删的删,该改的改~
第二种的做法:就不用这么麻烦了~
展示刷新头像的方法也有两种,最好不要都用,二选其一即可

*************第一种开始*****************
首先遵守协议:RCIMUserInfoDataSource,RCIMGroupInfoDataSource

在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中 设置代理:
[[RCIM sharedRCIM] setUserInfoDataSource:self];
[[RCIM sharedRCIM] setGroupInfoDataSource:self];

实现代理方法,以个人信息为例:(本地头像的情况,通过demo中的两个类来生成的图片)
- (void)getUserInfoWithUserId:(NSString *)userId
                   completion:(void (^)(RCUserInfo *userInfo))completion
{
    if ([userId isEqualToString:[RDAppDataManager instance].ucUserId]) {
        RCUserInfo *info = [[RCUserInfo alloc] init];
        info.userId = userId;
        info.name = [RDAppDataManager instance].nickName;
        FTAddressBookUserModel *model = [[FTAddressBookUserModel alloc] init];
        model.name = [RDAppDataManager instance].nickName;
        model.userId = [RDAppDataManager instance].ucUserId;
        model.color = [RDAppDataManager instance].color;
        info.portraitUri = [RCDUtilities defaultUserPortrait:model];
        [[RCIM sharedRCIM] refreshUserInfoCache:info withUserId:info.userId];
        return completion(info);
    }else{
        FTAddressBookUserModel *model = [[StorageManager sharedInstance] getUserWithUserID:userId];
        RCUserInfo *info = [[RCUserInfo alloc] init];
        info.userId = userId;
        info.name = model.name;
        info.portraitUri = [RCDUtilities defaultUserPortrait:model];
        [[RCIM sharedRCIM] refreshUserInfoCache:info withUserId:info.userId];
        return completion(info);
    }
}

(链接头像的情况)
- (void)getUserInfoWithUserId:(NSString *)userId
                   completion:(void (^)(RCUserInfo *userInfo))completion
{
    if ([userId isEqualToString:@"当前登录用户的融云id"]) {
        return completion([[RCUserInfo alloc] initWithUserId:userId name:@"当前登录用户的用户名" portrait:@"当前登录用户头像的url"]);
    }else
    {
        根据存储联系人信息的模型,通过 userId 来取得对应的name和头像url,进行以下设置(此处因为项目接口尚未实现,所以就只能这样给大家说说,请见谅)
        return completion([[RCUserInfo alloc] initWithUserId:userId name:@"name" portrait:@"http://pic32.nipic.com/20130827/12906030_123121414000_2.png"]);
    }
}

// 从 2.6.0 开始 IMKit 可以缓存用户信息到数据库里,开发者可以开启 RCIM.h 里的enablePersistentUserInfoCache (当然也可以不用写这个,个人感觉没有什么显著的用处)
[RCIM sharedRCIM].enablePersistentUserInfoCache = YES;

 /*!
     获取群组信息

     @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
{
// 此处做相应的群组设置即可,获取群组信息的流程与获取用户信息的流程一致
}
************第一种结束******************
************第二种开始******************


//APPdelegate.m 设置消息体内是否携带用户信息
[RCIM sharedRCIM].enableMessageAttachUserInfo = YES;

// 设置当前用户信息
链接融云成功后在成功回调里面设置一下currentUserInfo就行了
[[RCIM sharedRCIM] connectWithToken:@"YourTestUserToken"     success:^(NSString *userId) {
    NSLog(@"登陆成功。当前登录的用户ID:%@", userId);
[RCIM sharedRCIM].currentUserInfo = [[RCUserInfo alloc] initWithUserId:@"当前登录用户的融云id" name:@"当前登录用户的用户名" portrait:@"用户头像的url"];
} error:^(RCConnectErrorCode status) {
    NSLog(@"登陆的错误码为:%d", status);
} tokenIncorrect:^{
    //token过期或者不正确。
    //如果设置了token有效期并且token过期,请重新请求您的服务器获取新的token
    //如果没有设置token有效期却提示token错误,请检查您客户端和服务器的appkey是否匹配,还有检查您获取token的流程。
    NSLog(@"token错误");
}];
************第二种结束******************
图1
六、位置实时共享
1、首先将下面的文件(融云demo里面关于位置的文件)拉进项目里
2、导入头文件
#import "RealTimeLocationEndCell.h"
#import "RealTimeLocationStartCell.h"
#import "RealTimeLocationStatusView.h"
#import "RealTimeLocationViewController.h"
3、遵守协议 < RCRealTimeLocationObserver, RealTimeLocationStatusViewDelegate,UIActionSheetDelegate>
4、属性
@property(nonatomic, weak) id<RCRealTimeLocationProxy> realTimeLocation;
@property(nonatomic, strong) RealTimeLocationStatusView *realTimeLocationStatusView;
5、在viewDidLoad中添加代码如下:
/*******************实时地理位置共享***************/
    [self registerClass:[RealTimeLocationStartCell class] forMessageClass:[RCRealTimeLocationStartMessage class]];
    [self registerClass:[RealTimeLocationEndCell class] forMessageClass:[RCRealTimeLocationEndMessage class]];
    
    WeakSelf;
    [[RCRealTimeLocationManager sharedManager] getRealTimeLocationProxy:self.conversationType
                                                               targetId:self.targetId
                                                                success:^(id<RCRealTimeLocationProxy> realTimeLocation) {
                                                                    weakSelf.realTimeLocation = realTimeLocation;
                                                                    [weakSelf.realTimeLocation addRealTimeLocationObserver:weakSelf];
                                                                    [weakSelf updateRealTimeLocationStatus];
                                                                }
                                                                  error:^(RCRealTimeLocationErrorCode status) {
                                                                      NSLog(@"get location share failure with code %d", (int)status);
                                                                  }];
    
    /******************实时地理位置共享**************/

6、代理中的几个方法
//选择位置按钮时弹出两个选项(位置实时共享,发送位置)
- (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;
        default:
        {
            [super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
        }
            break;
    }
    
}
//结束定位
- (void)willMoveToParentViewController:(UIViewController*)parent{
    [super willMoveToParentViewController:parent];
    if (!parent) {
        [self.realTimeLocation quitRealTimeLocation];
    }
}
//发送位置和位置实时共享选择
#pragma mark - UIActionSheet Delegate
- (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;
    }
}
//点击cell时,如果是地理位置,进行位置共享
#pragma mark override
- (void)didTapMessageCell:(RCMessageModel *)model {
    [super didTapMessageCell:model];
    if ([model.content isKindOfClass:[RCRealTimeLocationStartMessage class]]) {
        [self showRealTimeLocationViewController];
    }
}

以下代理方法 原封不动放下面
/*******************实时地理位置共享***************/
- (void)showRealTimeLocationViewController {
    RealTimeLocationViewController *lsvc = [[RealTimeLocationViewController alloc] init];
    lsvc.realTimeLocationProxy = self.realTimeLocation;
    if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_INCOMING) {
        [self.realTimeLocation joinRealTimeLocation];
    } else if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_IDLE) {
        [self.realTimeLocation startRealTimeLocation];
    }
    [self.navigationController presentViewController:lsvc
                                            animated:YES
                                          completion:^{
                                              
                                          }];
}
- (void)updateRealTimeLocationStatus {
    if (self.realTimeLocation) {
        [self.realTimeLocationStatusView updateRealTimeLocationStatus];
        WeakSelf;
        NSArray *participants = nil;
        switch ([self.realTimeLocation getStatus]) {
            case RC_REAL_TIME_LOCATION_STATUS_OUTGOING:
                [self.realTimeLocationStatusView updateText:@"你正在共享位置"];
                break;
            case RC_REAL_TIME_LOCATION_STATUS_CONNECTED:
            case RC_REAL_TIME_LOCATION_STATUS_INCOMING:
                participants = [self.realTimeLocation getParticipants];
                if (participants.count == 1) {
                    NSString *userId = participants[0];
                    [weakSelf.realTimeLocationStatusView
                     updateText:[NSString stringWithFormat:@"user<%@>正在共享位置", userId]];
                    [[RCIM sharedRCIM].userInfoDataSource
                     getUserInfoWithUserId:userId
                     completion:^(RCUserInfo *userInfo) {
                         if (userInfo.name.length) {
                             dispatch_async(dispatch_get_main_queue(), ^{
                                 [weakSelf.realTimeLocationStatusView
                                  updateText:[NSString stringWithFormat:@"%@正在共享位置", userInfo.name]];
                             });
                         }
                     }];
                } else {
                    if (participants.count < 1)
                        [self.realTimeLocationStatusView removeFromSuperview];
                    else
                        [self.realTimeLocationStatusView
                         updateText:[NSString stringWithFormat:@"%d人正在共享地理位置", (int)participants.count]];
                }
                break;
            default:
                break;
        }
    }
}

- (RealTimeLocationStatusView *)realTimeLocationStatusView {
    if (!_realTimeLocationStatusView) {
        _realTimeLocationStatusView =
        [[RealTimeLocationStatusView alloc] initWithFrame:CGRectMake(0, STATUS_BAR_HEIGHT + 44, self.view.frame.size.width, 0)];
        _realTimeLocationStatusView.delegate = self;
        [self.view addSubview:_realTimeLocationStatusView];
    }
    return _realTimeLocationStatusView;
}

#pragma mark - RealTimeLocationStatusViewDelegate
- (void)onJoin {
    [self showRealTimeLocationViewController];
}
- (RCRealTimeLocationStatus)getStatus {
    return [self.realTimeLocation getStatus];
}

- (void)onShowRealTimeLocationView {
    [self showRealTimeLocationViewController];
}

#pragma mark - RCRealTimeLocationObserver
- (void)onRealTimeLocationStatusChange:(RCRealTimeLocationStatus)status {
    WeakSelf;
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf updateRealTimeLocationStatus];
    });
}

- (void)onReceiveLocation:(CLLocation *)location type:(RCRealTimeLocationType)type fromUserId:(NSString *)userId {
    WeakSelf;
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf updateRealTimeLocationStatus];
    });
}

- (void)onParticipantsJoin:(NSString *)userId {
    WeakSelf;
    if ([userId isEqualToString:[RCIMClient sharedRCIMClient].currentUserInfo.userId]) {
        [self notifyParticipantChange:@"你加入了地理位置共享"];
    } else {
        [[RCIM sharedRCIM].userInfoDataSource
         getUserInfoWithUserId:userId
         completion:^(RCUserInfo *userInfo) {
             if (userInfo.name.length) {
                 [weakSelf notifyParticipantChange:[NSString stringWithFormat:@"%@加入地理位置共享",
                                                    userInfo.name]];
             } else {
                 [weakSelf notifyParticipantChange:[NSString stringWithFormat:@"user<%@>加入地理位置共享",
                                                    userId]];
             }
         }];
    }
}

- (void)onParticipantsQuit:(NSString *)userId {
    WeakSelf;
    if ([userId isEqualToString:[RCIMClient sharedRCIMClient].currentUserInfo.userId]) {
        [self notifyParticipantChange:@"你退出地理位置共享"];
    } else {
        [[RCIM sharedRCIM].userInfoDataSource
         getUserInfoWithUserId:userId
         completion:^(RCUserInfo *userInfo) {
             if (userInfo.name.length) {
                 [weakSelf notifyParticipantChange:[NSString stringWithFormat:@"%@退出地理位置共享",
                                                    userInfo.name]];
             } else {
                 [weakSelf notifyParticipantChange:[NSString stringWithFormat:@"user<%@>退出地理位置共享",
                                                    userId]];
             }
         }];
    }
}

- (void)onRealTimeLocationStartFailed:(long)messageId {
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i < self.conversationDataRepository.count; i++) {
            RCMessageModel *model = [self.conversationDataRepository objectAtIndex:i];
            if (model.messageId == messageId) {
                model.sentStatus = SentStatus_FAILED;
            }
        }
        NSArray *visibleItem = [self.conversationMessageCollectionView indexPathsForVisibleItems];
        for (int i = 0; i < visibleItem.count; i++) {
            NSIndexPath *indexPath = visibleItem[i];
            RCMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
            if (model.messageId == messageId) {
                [self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
            }
        }
    });
}

- (void)notifyParticipantChange:(NSString *)text {
    WeakSelf;
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.realTimeLocationStatusView updateText:text];
        [weakSelf performSelector:@selector(updateRealTimeLocationStatus) withObject:nil afterDelay:0.5];
    });
}

- (void)onFailUpdateLocation:(NSString *)description {
}
位置实时共享
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355