IOS XMPP(即时通讯的框架)
#import "AppDelegate.h"
#import "XMPPFramework.h"
/*
* 在AppDelegate实现登录
1. 初始化XMPPStream
2. 连接到服务器[传一个JID]
3. 连接到服务成功后,再发送密码授权
4. 授权成功后,发送"在线" 消息
*/
@interface AppDelegate ()<XMPPStreamDelegate>{
XMPPStream *_xmppStream;
}
// 1. 初始化XMPPStream
-(void)setupXMPPStream;
// 2.连接到服务器
-(void)connectToHost;
// 3.连接到服务成功后,再发送密码授权
-(void)sendPwdToHost;
// 4.授权成功后,发送"在线" 消息
-(void)sendOnlineToHost;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 程序一启动就连接到主机
[self connectToHost];
return YES;
}
#pragma mark -私有方法
#pragma mark 初始化XMPPStream
-(void)setupXMPPStream{
_xmppStream = [[XMPPStream alloc] init];
// 设置代理
[_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
}
#pragma mark 连接到服务器
-(void)connectToHost{
NSLog(@"开始连接到服务器");
if (!_xmppStream) {
[self setupXMPPStream];
}
// 设置登录用户JID
//resource 标识用户登录的客户端 iphone android
XMPPJID *myJID = [XMPPJID jidWithUser:@"wangwu" domain:@"teacher.local" resource:@"iphone" ];
_xmppStream.myJID = myJID;
// 设置服务器域名
_xmppStream.hostName = @"teacher.local";//不仅可以是域名,还可是IP地址
// 设置端口 如果服务器端口是5222,可以省略
_xmppStream.hostPort = 5222;
// 连接
NSError *err = nil;
if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){
NSLog(@"%@",err);
}
}
#pragma mark 连接到服务成功后,再发送密码授权
-(void)sendPwdToHost{
NSLog(@"再发送密码授权");
NSError *err = nil;
[_xmppStream authenticateWithPassword:@"123456" error:&err];
if (err) {
NSLog(@"%@",err);
}
}
#pragma mark 授权成功后,发送"在线" 消息
-(void)sendOnlineToHost{
NSLog(@"发送 在线 消息");
XMPPPresence *presence = [XMPPPresence presence];
NSLog(@"%@",presence);
[_xmppStream sendElement:presence];
}
#pragma mark -XMPPStream的代理
#pragma mark 与主机连接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
NSLog(@"与主机连接成功");
// 主机连接成功后,发送密码进行授权
[self sendPwdToHost];
}
#pragma mark 与主机断开连接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
// 如果有错误,代表连接失败
NSLog(@"与主机断开连接 %@",error);
}
#pragma mark 授权成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
NSLog(@"授权成功");
[self sendOnlineToHost];
}
#pragma mark 授权失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
NSLog(@"授权失败 %@",error);
}
#pragma mark -公共方法
-(void)logout{
// 1." 发送 "离线" 消息"
XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppStream sendElement:offline];
// 2. 与服务器断开连接
[_xmppStream disconnect];
}
@end
封装好的XMPP工具(登录,注册,注销)
WCXMPPTool.h
#import <Foundation/Foundation.h> #import "Singleton.h" #import "XMPPFramework.h" typedef enum { XMPPResultTypeLoginSuccess,//登录成功 XMPPResultTypeLoginFailure,//登录失败 XMPPResultTypeNetErr,//网络不给力 XMPPResultTypeRegisterSuccess,//注册成功 XMPPResultTypeRegisterFailure//注册失败 }XMPPResultType; typedef void (^XMPPResultBlock)(XMPPResultType type);// XMPP请求结果的block @interface WCXMPPTool : NSObject singleton_interface(WCXMPPTool); @property (nonatomic, strong)XMPPvCardTempModule *vCard;//电子名片 /** * 注册标识 YES 注册 / NO 登录 */ @property (nonatomic, assign,getter=isRegisterOperation) BOOL registerOperation;//注册操作 /** * 用户注销 */ -(void)xmppUserlogout; /** * 用户登录 */ -(void)xmppUserLogin:(XMPPResultBlock)resultBlock; /** * 用户注册 */ -(void)xmppUserRegister:(XMPPResultBlock)resultBlock; @end
WCXMPPTool.m
#import "WCXMPPTool.h" /* * 在AppDelegate实现登录 1. 初始化XMPPStream 2. 连接到服务器[传一个JID] 3. 连接到服务成功后,再发送密码授权 4. 授权成功后,发送"在线" 消息 */ @interface WCXMPPTool ()<XMPPStreamDelegate>{ XMPPStream *_xmppStream; XMPPResultBlock _resultBlock; XMPPvCardCoreDataStorage *_vCardStorage;//电子名片的数据存储 XMPPvCardAvatarModule *_avatar;//头像模块 } // 1. 初始化XMPPStream -(void)setupXMPPStream; // 2.连接到服务器 -(void)connectToHost; // 3.连接到服务成功后,再发送密码授权 -(void)sendPwdToHost; // 4.授权成功后,发送"在线" 消息 -(void)sendOnlineToHost; @end @implementation WCXMPPTool singleton_implementation(WCXMPPTool) #pragma mark -私有方法 #pragma mark 初始化XMPPStream -(void)setupXMPPStream{ _xmppStream = [[XMPPStream alloc] init]; #warning 每一个模块添加后都要激活 //添加电子名片模块 _vCardStorage = [XMPPvCardCoreDataStorage sharedInstance]; _vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage]; //激活 [_vCard activate:_xmppStream]; //添加头像模块 _avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard]; [_avatar activate:_xmppStream]; // 设置代理 [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; } #pragma mark 连接到服务器 -(void)connectToHost{ WCLog(@"开始连接到服务器"); if (!_xmppStream) { [self setupXMPPStream]; } // 设置登录用户JID //resource 标识用户登录的客户端 iphone android // 从单例获取用户名 NSString *user = nil; if (self.isRegisterOperation) { user = [WCUserInfo sharedWCUserInfo].registerUser; }else{ user = [WCUserInfo sharedWCUserInfo].user; } XMPPJID *myJID = [XMPPJID jidWithUser:user domain:@"teacher.local" resource:@"iphone" ]; _xmppStream.myJID = myJID; // 设置服务器域名 _xmppStream.hostName = @"127.0.0.1";//不仅可以是域名,还可是IP地址 // 设置端口 如果服务器端口是5222,可以省略 _xmppStream.hostPort = 5222; // 连接 NSError *err = nil; if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){ WCLog(@"%@",err); } } #pragma mark 连接到服务成功后,再发送密码授权 -(void)sendPwdToHost{ WCLog(@"再发送密码授权"); NSError *err = nil; // 从单例里获取密码 NSString *pwd = [WCUserInfo sharedWCUserInfo].pwd; [_xmppStream authenticateWithPassword:pwd error:&err]; if (err) { WCLog(@"%@",err); } } #pragma mark 授权成功后,发送"在线" 消息 -(void)sendOnlineToHost{ WCLog(@"发送 在线 消息"); XMPPPresence *presence = [XMPPPresence presence]; WCLog(@"%@",presence); [_xmppStream sendElement:presence]; } #pragma mark -XMPPStream的代理 #pragma mark 与主机连接成功 -(void)xmppStreamDidConnect:(XMPPStream *)sender{ WCLog(@"与主机连接成功"); if (self.isRegisterOperation) {//注册操作,发送注册的密码 NSString *pwd = [WCUserInfo sharedWCUserInfo].registerPwd; [_xmppStream registerWithPassword:pwd error:nil]; }else{//登录操作 // 主机连接成功后,发送密码进行授权 [self sendPwdToHost]; } } #pragma mark 与主机断开连接 -(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{ // 如果有错误,代表连接失败 // 如果没有错误,表示正常的断开连接(人为断开连接) if(error && _resultBlock){ _resultBlock(XMPPResultTypeNetErr); } WCLog(@"与主机断开连接 %@",error); } #pragma mark 授权成功 -(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{ WCLog(@"授权成功"); [self sendOnlineToHost]; // 回调控制器登录成功 if(_resultBlock){ _resultBlock(XMPPResultTypeLoginSuccess); } } #pragma mark 授权失败 -(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{ WCLog(@"授权失败 %@",error); // 判断block有无值,再回调给登录控制器 if (_resultBlock) { _resultBlock(XMPPResultTypeLoginFailure); } } #pragma mark 注册成功 -(void)xmppStreamDidRegister:(XMPPStream *)sender{ WCLog(@"注册成功"); if(_resultBlock){ _resultBlock(XMPPResultTypeRegisterSuccess); } } #pragma mark 注册失败 -(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{ WCLog(@"注册失败 %@",error); if(_resultBlock){ _resultBlock(XMPPResultTypeRegisterFailure); } } #pragma mark -公共方法 -(void)xmppUserlogout{ // 1." 发送 "离线" 消息" XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"]; [_xmppStream sendElement:offline]; // 2. 与服务器断开连接 [_xmppStream disconnect]; // 3. 回到登录界面 // UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Login" bundle:nil]; // // self.window.rootViewController = storyboard.instantiateInitialViewController; [UIStoryboard showInitialVCWithName:@"Login"]; //4.更新用户的登录状态 [WCUserInfo sharedWCUserInfo].loginStatus = NO; [[WCUserInfo sharedWCUserInfo] saveUserInfoToSanbox]; } -(void)xmppUserLogin:(XMPPResultBlock)resultBlock{ // 先把block存起来 _resultBlock = resultBlock; // Domain=XMPPStreamErrorDomain Code=1 "Attempting to connect while already connected or connecting." UserInfo=0x7fd86bf06700 {NSLocalizedDescription=Attempting to connect while already connected or connecting.} // 如果以前连接过服务,要断开 [_xmppStream disconnect]; // 连接主机 成功后发送登录密码 [self connectToHost]; } -(void)xmppUserRegister:(XMPPResultBlock)resultBlock{ // 先把block存起来 _resultBlock = resultBlock; // 如果以前连接过服务,要断开 [_xmppStream disconnect]; // 连接主机 成功后发送注册密码 [self connectToHost]; } @end
自动登录
AppDelegate.m
#import "AppDelegate.h" #import "XMPPFramework.h" #import "WCNavigationController.h" #import "DDLog.h" #import "DDTTYLogger.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //从沙盒的路径 NSString *path=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; //打开XMPP的日志 [DDLog addLogger:[DDTTYLogger sharedInstance]]; //设置导航栏背景 // [WCNavigationController setupNavTheme]; //从沙盒里加载用户的数据单例 [[WCUserInfo sharedWCUserInfo] loadUserInfoFromSanbox]; //判断用户的登录状态,YES直接来到主界面 if([WCUserInfo sharedWCUserInfo].loginStatus){ UIStoryboard *storayobard=[UIStoryboard storyboardWithName:@"Main" bundle:nil]; self.window.rootViewController=storayobard.instantiateInitialViewController; //自动登录服务 [[WCXMPPTool sharedWCXMPPTool]xmppUserLogin:nil]; } return YES; } @end
登录方法
#import "WCBaseLoginViewController.h" #import "AppDelegate.h" @implementation WCBaseLoginViewController - (void)login{ // 登录 /* * 官方的登录实现 * 1.把用户名和密码放在WCUserInfo的单例 * 2.调用 AppDelegate的一个login 连接服务并登录 */ //隐藏键盘 [self.view endEditing:YES]; // 登录之前给个提示 [MBProgressHUD showMessage:@"正在登录中..." toView:self.view]; [WCXMPPTool sharedWCXMPPTool].registerOperation = NO; __weak typeof(self) selfVc = self; [[WCXMPPTool sharedWCXMPPTool] xmppUserLogin:^(XMPPResultType type) { [selfVc handleResultType:type]; }]; } -(void)handleResultType:(XMPPResultType)type{ // 主线程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ [MBProgressHUD hideHUDForView:self.view ]; switch (type) { case XMPPResultTypeLoginSuccess: NSLog(@"登录成功"); [self enterMainPage]; break; case XMPPResultTypeLoginFailure: NSLog(@"登录失败"); [MBProgressHUD showError:@"用户名或者密码不正确" toView:self.view]; break; case XMPPResultTypeNetErr: [MBProgressHUD showError:@"网络不给力" toView:self.view]; default: break; } }); } -(void)enterMainPage{ // 更改用户的登录状态为YES [WCUserInfo sharedWCUserInfo].loginStatus = YES; // 把用户登录成功的数据,保存到沙盒 [[WCUserInfo sharedWCUserInfo] saveUserInfoToSanbox]; // 隐藏模态窗口 [self dismissViewControllerAnimated:NO completion:nil]; // 登录成功来到主界面 // 此方法是在子线程补调用,所以在主线程刷新UI UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; self.view.window.rootViewController = storyboard.instantiateInitialViewController; }