美文网首页
腾讯云IM实现短信注册登录好友添加单聊会话

腾讯云IM实现短信注册登录好友添加单聊会话

作者: uli | 来源:发表于2018-05-18 15:46 被阅读0次

    以后项目会用到即时通信,写了一个demo,这里整理下集成腾讯云IM过程。

    云通信架构

    提供单聊、群聊、资料托管、关系链托管、帐号托管全方位解决方案,并提供完善的 App 接入、后台管理接口。

    image

    sdk下载

    demo使用 SDK v2.x版本,ImSDK开发包内含:ImSDK.framework、IMCore.framework、TLSSDK.framework、QALSDK.framework。

    image

    使用说明

    1.直接拖入

    image

    2.选中IMDemo的Target,在General面板中的Linked Frameworks and Libraries添加依赖库。

    image

    3.在Build Setting中Other Linker Flags添加-ObjC。

    image

    4.工程Info.plist文件增加

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

    5.添加工程pch文件

    #import <ImSDK/ImSDK.h>
    #import <QALSDK/QalSDKProxy.h>
    #import <QALSDK/QalSDKCallbackProtocol.h>
    #import "TLSSDK/TLSAccountHelper.h"
    #import "TLSSDK/TLSLoginHelper.h"
    #import "TLSSDK/TLSRefreshTicketListener.h"
    #import "TLSSDK/TLSOpenLoginListener.h"
    #import "TLSSDK/TLSHelper.h"
    

    6.编译成功,运行以下代码,打印"hello world"表示ImSDK集成成功

      // 初始化TIM
      [[TIMManager sharedInstance]initSdk:kSdkAppId accountType:kAccountType];
    
      [[TIMManager sharedInstance]log:TIM_LOG_DEBUG tag:@"imsdk" msg:@"hello world"];
    
    image.png

    帐号登录集成

    这里采用托管模式,集成(Tencent Login Service,TLS)SDK实现短信登录验证服务。

    托管模式

    托管模式是指,由 TLS 为开发者提供 App 帐号的密码注册、存储和密码验证。帐号验证成功后,派发私钥加密生成的签名到客户端,App 业务服务器可以通过 下载的公钥 解密签名进行验证。

    使用说明

    直接使用 TLS SDK 即可快速完成注册和登录能力集成。

    image

    搭建登录界面

    image.png

    注册登录逻辑

    image.png image.png

    注册功能开发
    SDK接口采用了异步的方式来实现,所有需要异步返回结果的接口都提供了listener回调参数,回调命名为Listener.
    1.TLS初始化

    # AppDelegate.m
    // kSdkAppId和kAccountType是在腾讯云平台申请的业务id和帐号类型
    [[QalSDKProxy sharedInstance]initWithAppid:kSdkAppId andSDKAppid:kSdkAppId andAccType:kAccountType];
    
    TLSLoginHelper *helper = [[TLSLoginHelper getInstance]init:kSdkAppId andAccountType:kAccountType andAppVer:@"1.0"];
    
    [[TLSAccountHelper getInstance]init:kSdkAppId andAccountType:kAccountType andAppVer:@"1.0"];
    #强烈注意:TLSSDK依赖QALSDK,因此需要先初始化QALSDK。
    

    2.输入手机号码,请求短信验证码

    # UIMRegisterViewController.m 遵守<TLSSmsLoginListener>协议
    #pragma mark - 获取验证码按钮的点击
    - (void)getCodeBtnClick
    { 
        // phoneNumStr 用户输入的手机号码,格式是 国家码-手机号码,比如 86-186xxx
        // regListener 注册回调,需要实现TLSSmsRegListener协议,不能为nil
        NSString *phoneNoStr = [NSString stringWithFormat:@"86-%@",self.phoneTextField.text];
        
        [[TLSHelper getInstance]TLSSmsRegAskCode:phoneNoStr andTLSSmsRegListener:self];
    }
    
    #pragma mark - 注册按钮的点击
    - (void)registerBtnClick
    { 
        [[TLSHelper getInstance]TLSSmsRegVerifyCode:self.pwdTextField.text andTLSSmsRegListener:self]; 
    }
    
    /**
     *  刷新短信验证码请求成功
     *
     *  @param reaskDuration 下次请求间隔
     *  @param expireDuration 验证码有效期
     */
    -(void)    OnSmsRegReaskCodeSuccess:(int)reaskDuration andExpireDuration:(int)expireDuration{
    // 重新请求短信验证码的回调
    //[[TLSHelper getInstance]TLSSmsRegReaskCode:nil];//等倒计时结束,用户手动点击重新发送之后,触发此逻辑
    }
    
    /**
     *  验证短信验证码成功
     */
    -(void)    OnSmsRegVerifyCodeSuccess{
        NSLog(@"验证短信验证码成功");
        [SVProgressHUD setMinimumDismissTimeInterval:1];
        [SVProgressHUD showSuccessWithStatus:@"验证短信验证码成功"];
       // 完成注册
        [[TLSHelper getInstance]TLSSmsRegCommit:self];
    }
    
    /**
     *  提交注册成功
     *
     *  @param userInfo 用户信息
     */
    -(void)    OnSmsRegCommitSuccess:(TLSUserInfo *)userInfo{
        NSLog(@"注册成功");
        [SVProgressHUD setMinimumDismissTimeInterval:1];
        [SVProgressHUD showSuccessWithStatus:@"注册成功"];
        [self dismissViewControllerAnimated:YES completion:^{
            NSLog(@"移除");  // 注册页是modal出来的
        }];
    }
    
    /**
     *  短信注册失败
     *
     *  @param errInfo 错误信息
     */
    -(void)    OnSmsRegFail:(TLSErrInfo *) errInfo{
        NSString *errMsg = errInfo.sErrorMsg;
        NSLog(@"验证码注册失败%@",errMsg);
        //时间
        [SVProgressHUD setMinimumDismissTimeInterval:1];
        [SVProgressHUD showSuccessWithStatus:errMsg];
        [self timeFailBeginFrom:1];
    }
    
    /**
     *  短信注册超时
     *
     *  @param errInfo 错误信息
     */
    -(void)    OnSmsRegTimeout:(TLSErrInfo *) errInfo{
        NSLog(@"验证码注册失败%@",errInfo);
        [self timeFailBeginFrom:1];  
    }
    
    /**
     *  请求短信验证码成功
     *
     *  @param reaskDuration 下次请求间隔
     *  @param expireDuration 验证码有效期
     */
    -(void)    OnSmsRegAskCodeSuccess:(int)reaskDuration andExpireDuration:(int) expireDuration{
        NSLog(@"提交手机号成功");
    }
    

    短信登录功能开发
    用户通过短信验证码的方式进行登录。
    1.输入手机号码,请求短信验证码

    #pragma mark - 登录按钮的点击
    - (void)loginBtnClick
    {
        // smsCodeText.text 用户输入的短信验证码
        // listener可以传nil,此时listener使用上一步设置的值
        NSString *phoneNoStr = [NSString stringWithFormat:@"86-%@", self.phoneTextField.text];
        [[TLSHelper getInstance] TLSSmsVerifyCode: phoneNoStr andCode: self.smsCodeText.text andTLSSmsLoginListener:self];
    }
    
    #pragma mark - 获取验证码的点击
    - (void)getLoginCodeBtnClick
    {
        // 调用短信验证码接口
        //phoneNumStr 用户输入的手机号,格式是 国家码-手机号码,比如 86-186xxx
        //self短消登录回调listener,需要实现TLSSmsLoginListener协议,不能为nil
        NSString *phoneNoStr = [NSString stringWithFormat:@"86-%@",self.phoneTextField.text];
        [[TLSHelper getInstance] TLSSmsAskCode:phoneNoStr andTLSSmsLoginListener:self];
    }
    

    2.在回调中提交登录腾讯IM

    /**
     *  请求短信验证码成功
     *
     *  @param reaskDuration 下次请求间隔
     *  @param expireDuration 验证码有效期
     */
    - (void)OnSmsLoginAskCodeSuccess:(int)reaskDuration andExpireDuration:(int)expireDuration{
        NSLog(@"提交手机号成功");
    }
    
    /**
     *  验证短信验证码成功
     */
    - (void)OnSmsLoginVerifyCodeSuccess{
        NSLog(@"验证短信验证码成功");
        
        //self短消登录回调listener,不能为nil
        NSString *phoneNoStr = [NSString stringWithFormat:@"86-%@",self.phoneTextField.text];
        [[TLSHelper getInstance] TLSSmsLogin:phoneNoStr andTLSSmsLoginListener:self];
    }
    
    /**
     *  提交登录请求成功
     *
     *  @param userInfo 用户信息
     */
    - (void)OnSmsLoginSuccess:(TLSUserInfo *)userInfo{
        
        /* 登录成功了,在这里可以获取用户票据*/
        
        NSString *userSig = [[TLSHelper getInstance] getTLSUserSig:userInfo.identifier];
        
        //  登录腾讯云IM
        [[IMDataManager shareManager]loginTIMWithUserInfo:[IMUserInfo sharedIMUserInfo] withToken:userSig];  
    }
     
    - (void)OnSmsLoginFail:(TLSErrInfo *)errInfo{
        
        /* 短信登录过程中任意一步都可以到达这里,可以根据tlsErrInfo 中ErrCode, Title, Msg 给用户弹提示语,引导相关操作*/
        NSString *errMsg = errInfo.sErrorMsg;
        NSLog(@"登录失败%@",errMsg);
        //时间
        [SVProgressHUD setMinimumDismissTimeInterval:1];
        [SVProgressHUD showSuccessWithStatus:errMsg];
        
    }
    
    - (void)OnSmsLoginTimeout:(TLSErrInfo *)errInfo{
        
        /* 短信登录过程中任意一步都可以到达这里,顾名思义,网络超时,可能是用户网络环境不稳定,一般让用户重试即可*/
        NSString *errMsg = errInfo.sErrorMsg;
        NSLog(@"登录超时%@",errMsg);
        //时间
        [SVProgressHUD setMinimumDismissTimeInterval:1];
        [SVProgressHUD showSuccessWithStatus:errMsg];
        
    }
    
    # IMDataManager.m
    /**
     *  登录TIM服务器(connect,用token去连接)
     *
     *  @param userInfo 用户信息
     *  @param token    token令牌
     */
    -(void)loginTIMWithUserInfo:(IMUserInfo *)userInfo withToken:(NSString *)token{
        TIMLoginParam *param = [[TIMLoginParam alloc]init];
        [param setIdentifier:userInfo.userId];
        [param setSdkAppId:kSdkAppId];
        [param setAccountType:kAccountType];
        [param setAppidAt3rd:kAppIdAt3rd];
        [param setUserSig:token];
        
        [[TIMManager sharedInstance] login:param succ:^{
            
            NSLog(@"tom2登录成功啦啦啦啦啦啦啦啦啦啦了");
     
          // 成功后跳转主界面
          UITabBarController *tabBarVC = [[UIMTabBarController alloc] init];
          [UIApplication sharedApplication] .keyWindow.rootViewController = tabBarVC;
        
        } fail:^(int code, NSString *msg) {
            NSLog(@"登录失败,呼叫总部,登录失败┏(- 0 -)┛");
        }];
    
    }
    

    简易搭建会话列表界面

    image.png
    1. 初始化会话刷新监听
      通过会话刷新监听器 TIMRefreshListener 中的 onRefresh 回调通知更新界面,用户得到这个消息时,可以刷新界面,比如会话列表的未读等。
    # UIMConversationListViewController.m
    @property(nonatomic, strong)NSArray<TIMConversation *> *conversationList;
    
    [[TIMManager sharedInstance] setRefreshListener:self];
    

    通过TIMManager获取会话列表list后,遍历会话列表,通过getType,判断你会话类型,单聊TIM_C2C,系统消息TIM_SYSTEM


    image.png
    #pragma 刷新用户会话列表
    - (void)onRefresh
    {
        // 获取所有会话
        NSArray *list = [[TIMManager sharedInstance] getConversationList];
     
        _conversationList = list;
        
        for (TIMConversation *sess in list) {
            
            // 加好友消息
            if ([sess getType] == TIM_SYSTEM) {
                
                NSString *rec = [sess getReceiver];
                /*  获取会话消息
                *
                *  @param count 获取数量
                *  @param last  上次最后一条消息
                *  @param succ  成功时回调
                *  @param fail  失败时回调
                *
                *  @return 0 本次操作成功
                */
                __block TIMMessage *msg;
                [sess getMessage:1 last:msg succ:^(NSArray *msgs) {
                    msg = msgs[0];
                    for (int i = 0; i < [msg elemCount]; i++) {
                        TIMElem * elem = [msg getElem:i];
                        if ([elem isKindOfClass:[TIMSNSSystemElem class]]) {
                            TIMSNSSystemElem * system_elem = (TIMSNSSystemElem * )elem;
                            switch ([system_elem type]) {
                                case TIM_SNS_SYSTEM_ADD_FRIEND:
                                    for (TIMSNSChangeInfo * info in [system_elem users]) {
                                        NSLog(@"user %@ become friends", [info identifier]);
                                    }
                                    break;
                                case TIM_SNS_SYSTEM_DEL_FRIEND:
                                    for (TIMSNSChangeInfo * info in [system_elem users]) {
                                        NSLog(@"user %@ delete friends", [info identifier]);
                                    }
                                    break;
                                case TIM_SNS_SYSTEM_ADD_FRIEND_REQ:
                                    for (TIMSNSChangeInfo * info in [system_elem users]) {
                                        NSLog(@"user %@ request friends: reason=%@", [info identifier], [info wording]);
                                    }
                                    break;
                                default:
                                    NSLog(@"ignore type");
                                    break;
                            }
                        }
                    }
                } fail:^(int code, NSString *msg) {
                    NSLog(@"出错了");
                }];
            }
    
            // 1对1会话消息
            if (sess.getType == TIM_C2C) {
                NSString *rec = [sess getReceiver];
            }
        }
        [self.conversationListTabelView reloadData];
    }
    

    2.设置cell数据源为 _conversationList

    #pragma mark - UITableViewDataSource
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
       
        ..........省略..........
        TIMConversation *sess = _conversationList[indexPath.row];
        cell.nicknameLabel.text = [sess getReceiver];  
        ..........省略..........
        return cell;
    
    }
    

    ** 简易搭建单聊会话界面**


    Simulator Screen Shot - iPhone 7 - 2018-05-10 at 15.39.25.png

    1.注册新消息通知回调 TIMMessageListener

    // 监听新消息
    [[TIMManager sharedInstance] setMessageListener:self];
    

    2.保存聊天数据的数组

    // 保存所有聊天数据
    @property (nonatomic, strong) NSMutableArray *chatsData;
    

    3.消息模型

    #import <Foundation/Foundation.h>
    
    typedef enum : NSUInteger {
        UIMChatModelMessageTypeOther,
        UIMChatModelMessageTypeMe,
    } UIMChatModelMessageType; // 消息类型
    
    
    @interface UIMChatModel : NSObject 
    
    /// 消息内容
    @property (nonatomic, copy) NSString *text;
    /// 消息时间
    @property (nonatomic, copy) NSString *time;
    /// 消息类型
    @property (nonatomic, assign) UIMChatModelMessageType type;
    
    @end
    

    4.发送消息/接收消息
    回调消息内容通过参数TIMMessage传递,通过TIMMessage可以获取消息和相关会话的详细信息,如消息文本,语音数据,图片等等(这里只做了文字对话)

    注意: 需要在登录之前注册新消息通知,ImSDK 会拉取离线消息,通过 onNewMessage 抛出

    /**
     *  新消息通知
     *
     *  @param msgs 新消息列表,TIMMessage 类型数组
     */
    - (void)onNewMessage:(NSArray*) msgs{
        
        for (TIMMessage *msg in msgs)
        {
            int cnt = [msg elemCount];
            for (int i = 0; i < cnt; I++)
            {
                TIMElem * elem = [msg getElem:i];
                if ([elem isKindOfClass:[TIMTextElem class]])
                {
                    TIMTextElem * text_elem = (TIMTextElem * )elem;
                    if ([msg.sender isEqualToString: _friendInfo.userId])
                    {
                        NSString *text = text_elem.text;
                        [self sendMessageWithText:text andMessageType:UIMChatModelMessageTypeOther];
                        
                        [_tableView reloadData];
                        
                        //  滚动到最后一行
                        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_chatsData.count - 1 inSection:0];
                        [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
                    }
                }
            }
        }
    }
    
    /**
     发送消息/接收消息
     
     @param text 消息内容
     @param type 消息类型
     UIMChatModelMessageTypeMe 自己
     UIMChatModelMessageTypeOther 好友
     */
    - (void)sendMessageWithText:(NSString *)text andMessageType:(UIMChatModelMessageType)type {
        
        if (type == UIMChatModelMessageTypeMe) {
            
            [self sendMessageToTIM:text];
        }
        
        // 1.创建一个新模型
        UIMChatModel *newChatModel = [[UIMChatModel alloc] init];
        
        NSDate *date = [NSDate date]; // 获取当前系统时间
        // 把时间转换成字符串
        NSDateFormatter *df = [[NSDateFormatter alloc] init];
        // 指定时间格式
        //    df.dateFormat = @"yyyy-MM-dd hh:mm:ss";
        df.dateFormat = @"HH:mm";
        
        newChatModel.time = [df stringFromDate:date];
        
        if ([newChatModel.time isEqualToString:_previousTime]) {
            newChatModel.time = nil;
        } else {
            _previousTime = newChatModel.time;
        }
        
        newChatModel.text = text;
        newChatModel.type = type;
        // 2.添加模型数组中
        [_chatsData addObject:newChatModel];
    }
    
    /**
     Me发送消息
     
     @param textMsg 消息内容 
     */
    - (void)sendMessageToTIM: (NSString *)textMsg{
        
        TIMMessage *msg = [[TIMMessage alloc] init];
        
        TIMTextElem *elem = [[TIMTextElem alloc] init];
        [elem setText:textMsg];
        [msg addElem:elem];
        
        TIMConversation *sess = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:_friendInfo.userId];
        [sess sendMessage:msg succ:^{
            NSLog(@"发送消息成功");
        } fail:^(int code, NSString *msg) {
            NSLog(@"发送消息失败");
        }];
        
    }
    
    
    /**
     接收Other消息 
     */
    - (NSString *)receiveMessageFromTIM{
        
        __block NSString *otherMsgText;
        
        TIMConversation *sess = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:_friendInfo.userId];
        [sess getMessage:2 last:nil succ:^(NSArray *msgs) {
            NSLog(@"接收消息成功 %@",msgs);
            for (TIMMessage *msg in msgs)
            {
                int cnt = [msg elemCount];
                for (int i = 0; i < cnt; I++)
                {
                    TIMElem * elem = [msg getElem:i];
                    if ([elem isKindOfClass:[TIMTextElem class]])
                    {
                        TIMTextElem * text_elem = (TIMTextElem * )elem;
                        otherMsgText = text_elem.text;
                    }
                }
            }
        } fail:^(int code, NSString *msg) {
            NSLog(@"接收消息失败");
     
        }];
        
        return otherMsgText;
    }
    
    // 当点击键盘的发送时会调用此方法
    - (BOOL)textFieldShouldReturn:(UITextField *)textField {
    
        // 自己发一条消息
        [self sendMessageWithText:textField.text andMessageType:UIMChatModelMessageTypeMe];
     
        [_tableView reloadData];
        
        // 滚动到最后一行
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_chatsData.count - 1 inSection:0];
        [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
        
        // 清空文本输入框中的文字
        textField.text = nil;
        return YES;
    }
    

    3.显示对话
    UIMChatMeCell.m和UIMChatOtherCell.m

    // 重写模型属性的set方法给子控件设置数据
    - (void)setChatModel:(UIMChatModel *)chatModel {
        _chatModel = chatModel;
        
        _timeLabel.text = chatModel.time;
        _textContentLabel.text = chatModel.text;
    }
    

    资料关系链托管
    实现好友列表,使用用户 id查找好友,添加好友功能(https://cloud.tencent.com/doc/product/269/%E8%B5%84%E6%96%99%E7%B3%BB%E7%BB%9F)、关系链系统 文档。

    设置好友验证方式
    可通过 TIMFriendshipManager 的 SetAllowType 方法设置好友验证方式,用户可根据需要设置其中一种,目前没有方法设置默认的好友验证方式,默认都是任何人可加好友。(demo也是默认,没改)
    有以下几种验证方式:
    同意任何用户加好友
    拒绝任何人加好友
    需要验证

    添加好友界面

    image.png
    /**
     *  添加好友点击事件
     *
     * users 要添加的用户列表 TIMAddFriendRequest* 列表
     */
    - (void)actionAddFriend:(id)sender {
         
            NSMutableArray * users = [[NSMutableArray alloc] init];
            TIMAddFriendRequest* req = [[TIMAddFriendRequest alloc] init];
            // 添加好友  
            req.identifier = self.friendUserInfo.userId;
            // 添加备注 002Remark
            req.remark = [NSString stringWithUTF8String:"002Remark"];
            // 添加理由
            req.addWording = [NSString stringWithUTF8String:"i am X"];
            [users addObject:req];
            [[TIMFriendshipManager sharedInstance] AddFriend:users succ:^(NSArray * arr) {
                for (TIMFriendResult * res in arr) {
                    if (res.status != TIM_FRIEND_STATUS_SUCC) {
                        NSLog(@"AddFriend failed: user=%@ status=%d", res.identifier, res.status);
                       
                        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil
                                                                            message:@"已发送好友邀请"
                                                                           delegate:nil
                                                                  cancelButtonTitle:@"确定"
                                                                  otherButtonTitles:nil, nil];
                        [alertView show];
                        
                    }
                    else {
                        NSLog(@"AddFriend succ: user=%@ status=%d", res.identifier, res.status);
                    }
                }
            } fail:^(int code, NSString * err) {
                NSLog(@"add friend fail: code=%d err=%@", code, err);
            }];
        }
    }
    

    好友列表

    image.png
    #pragma mark - 加载数据
    - (void)loadTIMFriendData {
        [[TIMFriendshipManager sharedInstance] GetFriendList:^(NSArray * arr) {
           
            NSMutableArray *ArrM = [[NSMutableArray alloc]initWithCapacity:arr.count];
            for (TIMUserProfile *friend in arr) {
                
                [ArrM addObject:friend];
            }
            
            _friendListTIMData = ArrM.copy;
            [self.friendsTabelView reloadData];
    
     
        }fail:^(int code, NSString * err) {
            NSLog(@"GetFriendList fail: code=%d err=%@", code, err);;
        }];
        
    }
    

    相关文章

      网友评论

          本文标题:腾讯云IM实现短信注册登录好友添加单聊会话

          本文链接:https://www.haomeiwen.com/subject/qlyirftx.html