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

2018-05-18  本文已影响0人  uli

以后项目会用到即时通信,写了一个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);;
    }];
    
}
上一篇下一篇

猜你喜欢

热点阅读