您的位置:首页 > 其它

XMPP即时聊天步骤

2016-03-22 20:04 323 查看


XMPP即时聊天步骤

字数2232 阅读204 评论0 喜欢7
XMPP核心的几个类  :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;
}


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: