XMPP即时聊天步骤
2016-03-22 20:04
323 查看
XMPP即时聊天步骤
字数2232 阅读204 评论0 喜欢7XMPP核心的几个类 :XMPPStream(通讯管道)、XMPPJID(用户标示)、XMPPPresence(出席通知)、XMPPRoster(花名册)
一.登录注册
1.创建单例(XMPPManager)
static XMPPManager *manager = nil; + (XMPPManager *)sharedManager{ // GCD 创建单例对象 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[XMPPManager alloc] init]; }); return manager; }
2.配置通讯管道
- (instancetype)init{ self = [super init]; if (self) { //-----------------配置通信管道--------------- self.stream = [[XMPPStream alloc] init]; // 设置通信管道的目标服务器地址 _stream.hostName = kHostName; // 设置通信管道的xmpp server端口 _stream.hostPort = kHostPort; // 设置代理 [_stream addDelegate:self delegateQueue:dispatch_get_main_queue()]; } return self; }
3.构造XMPPJID
// 根据一个用户名构造一个xmppjid XMPPJID *myjid = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource]; // 设置通信管道的jid _stream.myJID = myjid;
4.连接服务器
if ([_stream isConnected]) { NSLog(@"已经连接"); [_stream disconnect]; // 断开连接 } BOOL result = [_stream connectWithTimeout:30 error:nil]; if (result){ NSLog(@"服务器链接成功"); }else{ NSLog(@"服务器链接失败"); }
5.连接服务器常用代理方法
// 服务器连接超时 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender{ NSLog(@"服务器连接超时"); } // 断开连接 - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{ if (error) { NSLog(@"%@",error); } // 离线消息(下线通知) XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"]; // 发送下线通知 [_stream sendElement:presence]; }
6.登陆 先连接服务器,再登陆
// 连接成功之后发起登陆事件 [_stream authenticateWithPassword:self.loginPassword error:nil];
7.登陆常用代理方法
// 登陆成功 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{ NSLog(@"登陆成功"); // 出席消息(上线通知) XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"]; // 发送上线通知 [_stream sendElement:presence]; } // 登陆失败 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{ NSLog(@"登陆失败:%@",error); }
8.注册 先连接服务器,再注册
// 连接成功之后发起注册事件 [_stream registerWithPassword:self.regPassword error:nil];
9.注册常用的代理方法
// 注册成功 - (void)xmppStreamDidRegister:(XMPPStream *)sender{ NSLog(@"注册成功"); } // 注册失败 - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{ NSLog(@"注册失败, error:%@",error); }
二. 好友列表、添加好友、发送消息、接收消息
1.单例中创建花名册的属性 (XMPPRoster 可以处理和好友相关的事:获取好友列表,添加好友,接收好友请求,同意添加好友,拒绝添加好友)
/** * 好友花名册,用来处理和好友相关的事件 */ @property(nonatomic, strong) XMPPRoster * roster;
2.初始化一个花名册,并且在通讯管道中激活花名册
//------------------用户花名册---------------- // xmpp为我们提供了一个CoreData存储器 XMPPRosterCoreDataStorage *xrcds = [XMPPRosterCoreDataStorage sharedInstance]; // 创建roster 花名册时,需要给花名册指定一个数据存储的地方(就是XMPPRosterCoreDataStorage) self.roster = [[XMPPRoster alloc] initWithRosterStorage:xrcds dispatchQueue:dispatch_get_main_queue()]; // 在通讯管道中激活花名册 // 这时就可以通过通讯管道去给服务器发送请求了。 // 然后roster的消息都通过stream间接的发给服务器 [self.roster activate:self.stream];
3.设置花名册代理,在好友列表控制器里面设置,列表控制器遵循代理 XMPPRosterDelegate// 设置代理
[[XMPPManager sharedManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
4.声明一个数组存放好友
@property (nonatomic, strong) NSMutableArray *rosters;
5.花名册常用代理方法
// 开始接收好友列表 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{ NSLog(@"开始接收好友列表"); } // 接收完毕 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{ NSLog(@"结束接收好友列表"); } // 每次接收到一个好友就会走一次这个方法 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{ NSString *jid = [[item attributeForName:@"jid"] stringValue]; XMPPJID *xmppjid = [XMPPJID jidWithString:jid resource:kResource]; [self.rosters addObject:xmppjid]; NSIndexPath *indexpath = nil; if (self.rosters.count == 0) return; indexpath = [NSIndexPath indexPathForRow:self.rosters.count - 1 inSection:0]; [self.tableView insertRowsAtIndexPaths:@[indexpath] withRowAnimation:(UITableViewRowAnimationLeft)]; } // 收到添加好友请求 :(同意:[roster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];) //(拒绝:[roster rejectPresenceSubscriptionRequestFrom:presence.from];) - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{ XMPPRoster *roster = [XMPPManager sharedManager].roster; NSString *message = [NSString stringWithFormat:@"%@请求加你为好友", presence.from.user]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"好友请求" message:message preferredStyle:(UIAlertControllerStyleAlert)]; UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) { // 同意请求 [roster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES]; }]; UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"拒绝" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) { // 拒绝请求 [roster rejectPresenceSubscriptionRequestFrom:presence.from]; }]; [alert addAction:action1]; [alert addAction:action2]; [self presentViewController:alert animated:YES completion:nil]; }
6.添加好友(添加好友界面)
- (IBAction)actionAdd:(UIButton *)sender { // 拿到花名册管理类 XMPPRoster *roster = [XMPPManager sharedManager].roster; // 拼装要添加的好友 XMPPJID *userjid = [XMPPJID jidWithUser:self.txtUserName.text domain:kDomin resource:kResource]; // 添加好友请求 [roster subscribePresenceToUser:userjid]; }
7.建立聊天
7.1 声明属性 (声明一个XMPPJID类型的属性,记录要聊天的对象)
// 当前聊天对象 @property(nonatomic, strong) XMPPJID * jidChatTo;
7.2 选择聊天对象(可视化编程中)
点击好友列表页面中的一个好友,将这个好友的XMPPJID传给聊天页面。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([sender isKindOfClass:[UITableViewCell class]]) { NSIndexPath *index = [self.tableView indexPathForCell:(UITableViewCell *)sender]; XMPPJID *jid = self.rosters[index.row]; // 属性传值 ChatViewController *chatVC = [segue destinationViewController]; // 获取目标控制器 chatVC.jidChatTo = jid; }
7.3 声明聊天界面属性
声明聊天相关的控件属性以及其他属性
@property (weak, nonatomic) IBOutlet UITableView *tableChat; // 展示聊天记录 @property (weak, nonatomic) IBOutlet UITextField *txtMessage; // 输入消息内容 @property (weak, nonatomic) IBOutlet UIView *viewInput; // 底部的输入控件 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *constraintBottomFromSuper; // 底部frame约束 @property (nonatomic, strong) NSMutableArray *messages; // 存放聊天信息
7.4 获取聊天记录
单例(XMPPManager)里面声明CoreData的上下文对象属性,以及聊天消息的归档处理类的对象属性
/** * 消息归档处理类 * 程序关闭后,下次打开还可以再次查看以前的聊天记录 */ @property(nonatomic, strong) XMPPMessageArchiving * messageArchiving; /** * coredata 上下文,用来获取通过messageArchiving归档后存储起来的消息 */ @property(nonatomic, strong) NSManagedObjectContext * context;单例对象初始化(init方法)里面对上下文,和归档处理对象的一些操作
//------------------初始化 XMPPMessageArchiving------ // xmppMessageArchiving的主要功能:1、通过通讯管道获取到服务器发送过来的消息。2、将消息存储到指定的XMPPMessageArchivingCoreStorage // xmpp为我们提供的一个存储聊天消息的coredata仓库 XMPPMessageArchivingCoreDataStorage *xmacds = [XMPPMessageArchivingCoreDataStorage sharedInstance]; // 初始化时,需要给这个归档类指定一个存储仓库 self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:xmacds dispatchQueue:dispatch_get_main_queue()]; // 在通讯管道中激活 [self.messageArchiving activate:self.stream]; // 获取消息归档类提供的上下文信息 self.context = xmacds.mainThreadManagedObjectContext;
聊天页面里面通过单例中获得的上下文对象,从CoreData中取到聊天记录数据
// 加载所有信息(通过单例类的上下文获取) - (void)reloadAllMessage{ // 获取上下文信息 NSManagedObjectContext *context = [XMPPManager sharedManager].context; // xmppMessageArchving : 把接收到得消息归档,归档后的数据类型是:XMPPMessageArchiving_Message_CoreDataObject NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"]; // 设置断言 // 查找所有的和当前聊天对象的聊天记录 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr = %@",self.jidChatTo.bare]; // 让断言生效 [fetchRequest setPredicate:predicate]; // 获取数据 NSArray *array = [context executeFetchRequest:fetchRequest error:nil]; if (array) { // 将原来的数据清空 [self.messages removeAllObjects]; } // 把获取的数据添加到当前数据源中 [self.messages addObjectsFromArray:array]; // 刷新列表 [self.tableChat reloadData]; // 将视图定位到最新的一条消息。 if (array.count > 0) { NSIndexPath *index = [NSIndexPath indexPathForItem:array.count - 1 inSection:0]; [self.tableChat scrollToRowAtIndexPath:index atScrollPosition:(UITableViewScrollPositionBottom) animated:YES]; } }
展示聊天内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; // 获取对应的消息 XMPPMessageArchiving_Message_CoreDataObject *message = self.messages[indexPath.row]; // 判断是不是自己发出去的 if ([message isOutgoing]) { cell.textLabel.text = [NSString stringWithFormat:@"我:%@",message.body]; }else{ cell.textLabel.text = [NSString stringWithFormat:@"%@:%@",message.bareJidStr, message.body]; } return cell; }
7.5 发送和接收消息
设置通讯管道代理
XMPPStream *stream = [XMPPManager sharedManager].stream; [stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
通讯管道代理中和发送以及接收消息相关的代理方法
// 收到信息 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{ NSLog(@"接收到一条消息:%@",message); [self reloadAllMessage]; // 加载一遍所有的聊天数据 } // 发送信息 - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{ NSLog(@"消息【%@】发送成功",message); [self reloadAllMessage]; // 加载一遍所有聊天数据 }
发送消息
- (IBAction)actionSendMsg:(UIButton *)sender { XMPPStream *stream = [XMPPManager sharedManager].stream; // 实例化一个消息类 XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.jidChatTo]; // 设置消息内容 [message addBody:self.txtMessage.text]; // 通过通讯管道发送 [stream sendElement:message]; }
其他:处理键盘弹出相关:
注册键盘frame改变的通知:
// 通过通知中心来观察键盘的frame的变化,当键盘frame发生变化后触发keyboardFrameChange事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChange:) name:UIKeyboardDidChangeFrameNotification object:nil];
处理方法及键盘回收代理事件:
// 键盘frame改变后触发事件 - (void)keyboardFrameChange:(NSNotification *)sender{ // 键盘改变后的frame CGRect rect = [[sender.userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; // 计算出聊天窗口的底部偏移量 CGFloat height = self.view.frame.size.height - rect.origin.y; self.constraintBottomFromSuper.constant = height; } #pragma mark -UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField{ [textField resignFirstResponder]; return YES; }
相关文章推荐
- 将博客搬至CSDN
- cocos2dx遇到的坑2
- ANDROID系统启动流程分析
- QR code API
- Windows系统下gvim字体和主题设置,显示行号
- 面试感悟:3年工作经验程序员应有的技能
- ubuntu 14.04 无线网连接一会儿后, wifi 没掉, 但不能上网的解决方案
- Robot Motion
- 启动apache Fail
- 笔记1handler/广播/布局/AIDL
- 第四周【项目3-随机数函数应用于游戏】
- libcurl使用演示样例
- 实验一、命令解释程序的编写
- cocos2dx遇到的坑1
- 前端路由
- 随笔1
- D2RQ平台之本体操作
- Trick (八)——list 模拟 stack(Python)
- Java POI 读取Office excel (2003,2007)及相关jar包
- 新学到list的常用方法,做下记录