// ======================= 用户 登陆注册 注销 ======================
/**
* ==== xmpp 登陆 ====
1. 初始化 XMPPStream
注意:
在连接服务器时初始化,
步骤:
1> 创建 XMPPStream对象
2> 设置代理(注意设置方式不同)
3> 添加一些需要的xmpp模块,并激活(电子名片,头像,花名册等等)
2. 建立连接到服务器(要传一个jid)
步骤:
1. 设置jid
1> 根据登陆标识判断是登陆 还是注册
2> 从单例中获取用户名
登陆: 获取账户对象的登陆用户名
注册: 获取账户对象的登陆用户名
3> 拼接jid jidWithUser
4> 设置jid
2. 设置主机地址
3. 设置主机端口号(不设置的话,默认是5222)
4. 发起连接 connectWithTimeout方法
XMPPStream 代理方法
3. 连接成功 xmppStreamDidConnect方法
注意:
不管用户名存不存在,连接服务器都会成功,
1> 如果用户存在,密码正确 会调用xmppStreamDidAuthenticate
2> 如果用户名不存在会调用-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
步骤:
1> 发送密码,判断是登陆还是注册
登陆:
1. 从LCAccount单例中获取登陆密码
2. 发送 authenticateWithPassword方法
注册:
1. 从LCAccount单例中获取注册密码
2. 发送 registerWithPassword方法
2> 登陆成功 xmppStreamDidAuthenticate方法
1. 发送 "在线"消息 给服务器(默认登陆成功后,是不在线的)(在代理方法中调用)
-> 好处:可以通知其他好友,上线了
2. 回调resultBlock
3> 登陆失败 didNotAuthenticate方法
1. 回调resultBlock
4> 注册成功 xmppStreamDidRegister方法
1. 回调resultBlock
5> 注册失败 didNotRegister方法
1. 回调resultBlock
*/
/**
* ==== xmpp 注册 ====
保存block, 删除之前的连接 [_xmppStream disconnect];
1. 发送注册的 jid给服务器, 建立一个长连接
2. 连接成功后,发送注册密码(代理方法中实现)
*/
/**
* ==== xmpp 注销 ====
1. 发送 "离线消息"给服务器
// xmpp框架,已经把所有的指令 封装成 对象了
XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppStream sendElement:offline];
2. 断开与服务器的连接
[_xmppStream disconnect];
3. 把沙盒里的登陆状态修改为NO
[LCAccount shareAccount].login = NO;
[[LCAccount shareAccount] saveToSandBox];
*/
// ======================= 好友列表(花名册) ===========================
/**
* ==== xmpp 获取好友 ====
注意:
好友信息存放在沙盒的XMPPRoster.sql数据库里.
对数据库进行操作.
步骤:
1. 关联上下文
NSManagedObjectContext *rosterContext = [XMPPTool sharedXMPPTool].rosterStorage.mainThreadManagedObjectContext;
2. request 请求查询哪张表
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];
3. 设置查询条件
(升序排序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES];
request.sortDescriptors = @[sort];
(过滤)对方没有同意添加为好友的, 不显示出来
NSPredicate *pre = [NSPredicate predicateWithFormat:@"subscription != %@", @"none"];
request.predicate = pre;
4. 执行请求
1> 创建结果控制器,接收查找好友的 返回结果对象
_resultContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:rosterContext sectionNameKeyPath:nil cacheName:nil];
2> 设置代理
_resultContr.delegate = self;
3> 执行
NSError *error = nil;
[_resultContr performFetch:&error];
NSFetchedResultsController代理方法
当数据库内容改变时 ,触发 controllerDidChangeContent方法
1> 刷新表格,显示最新数据
*/
/**
* ==== 显示好友信息到界面 ====
注意:
默认的情况:不是程序一启动就有头像,第一次的话需要自己从服务器上去获取.
步骤:
1. 好友的名称
2. 判断用户是否在线
3. 显示好友头像
有头像:
1> 设置头像
没有头像:
1> 从服务器获取头像
NSData *imgData = [[XMPPTool sharedXMPPTool].avatar photoDataForJID:user.jid];
2> 设置头像
*/
/**
* ==== 添加好友 ====
注意:
1. 不能添加自己为好友
2. 已经存在的好友不能再添加
步骤:
1. 不能添加自己为好友
2. 已经存在的好友不能再添加
1> 根据用户jid从数据库中获取 输入的好友是否存在
BOOL userExists = [[XMPPTool sharedXMPPTool].rosterStorage userExistsWithJID:userJid xmppStream:[XMPPTool sharedXMPPTool].xmppStream];
3. 判断
userExists == yes 已经添加
4. 添加好友
[[XMPPTool sharedXMPPTool].roster subscribePresenceToUser:userJid];
问题:
xmpp 添加好友现有的openfire中存在的问题:
添加不存在的好友时,数据库、通讯录里面也显示.
解决1:
> 服务器可以拦截添加好友的请求,如果服务器当前数据库没有好友, 不要返回信息
解决2:
> 过滤数据库的查询请求,字段: subscription
字段类型: none ,对方没有同意添加好友
to , 发给对方添加好友请求的
form , 别人加你的请求
both , 双方互为好友
> 在 LCContactsViewController结果控制器中, 获取联系人信息时, 添加过滤条件,对方没有同意的好友不显示出来.
*/
/**
* ==== 删除好友 ====
步骤:
1. 获取要删除的好友 (_resultContr.fetchedObjects查询结果的数组,类似:users)
XMPPUserCoreDataStorageObject *user = _resultContr.fetchedObjects[indexPath.row];
2.删除好友(使用花名册模块)
[[XMPPTool sharedXMPPTool].roster removeUser:user.jid];
*/
// ======================== 实时 聊天 ================================
/**
* ==== 获取聊天信息 ====
注意:
步骤:
1. 设置上下文
2. 查询请求
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
3. 查询条件
1>. 只获取当前登录用户的消息,并且 只获取和当前聊天好友的消息
NSString *loginJidStr = [XMPPTool sharedXMPPTool].xmppStream.myJID.bare; //当前用户名
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@", loginJidStr, self.friendJid.bare]; // sql查询条件
request.predicate = pre;
2>. 设置时间排序(升序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES];
request.sortDescriptors = @[sort];
4. 执行
1>. 请求结果控制器
_resultContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
2>. 代理
_resultContr.delegate = self;
3>.执行
NSError *error = nil;
[_resultContr performFetch:&error];
下面的在 cellForRowAtIndexPath:(NSIndexPath *)indexPath方法里面设置
5. 得到结果对象 获取聊天信息
XMPPMessageArchiving_Message_CoreDataObject *msgObj = _resultContr.fetchedObjects[indexPath.row];
步骤:
1> 获取原始的xml数据
XMPPMessage *message = msgObj.message;
2> 获取文件的类型
NSString *bodyType = [message attributeStringValueForName:@"bodyType"];
3> 判断文件类型 [bodyType isEqualToString:@"image"]
文本:
直接赋值
cell.textLabel.text = message.body;
图片image:
1> 获取子节点data数据
1). 获取所有的子节点
NSArray *child = message.children;
2). 返回的数据
NSData *data = nil;
3). 遍历所有子节点
for (XMPPElement *note in child) {
// 获取节点的名字,进行判断
if ([[note name] isEqual:@"attachment"]) { // 此时说明有文件
// ** 1. 获取文件字符串
NSString *dataStr = [note stringValue];
// ** 2. 字符串 --> data
data = [[NSData alloc] initWithBase64EncodedString:dataStr options:0];
}
}
2> 赋值
image = [UIImage imageWithData:imageData];
音频sound:
后续
*/
/**
* ==== 发送聊天信息 ====
注意:
把图片转换成字符串(使用base64编码转换)
图片 --> data
NSData *imgData = UIImagePNGRepresentation(img);
data --> base64字符串
NSString *imgStr = [imgData base64EncodedStringWithOptions:0];
上面 = 等价于 = 下面
NSString *base64Str = [data base64EncodedStringWithOptions:0];
步骤:
1. 发送文本信息
1> 向服务器发送聊天数据
1> 编辑要发送的数据
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid];
[msg addBody:textField.text];
2> 发送消息
[[XMPPTool sharedXMPPTool].xmppStream sendElement:msg];
2> 清空输入框文本
textField.text = nil;
2. 发送文件,以发送图片为例
1> 获取图片
1). 从手机图片库 选取
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
2). 设置图片样式
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
3). 设置代理
picker.delegate = self;
4). 跳转
[self presentViewController:picker animated:YES completion:nil];
2> UIImagePickerController代理 didFinishPickingMediaWithInfo代理方法
1).获取图片
UIImage *img = info[UIImagePickerControllerOriginalImage];
2).发送图片
1> 编辑要发送的数据
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid];
2> 设置类型
[msg addAttributeWithName:@"bodyType" stringValue:bodyType];
[msg addBody:bodyType];
3> 把文件转换成字符串(使用base64编码转换)
NSString *base64Str = [data base64EncodedStringWithOptions:0];
4> 创建节点(定义附件)
XMPPElement *attachment = [XMPPElement elementWithName:@"attachment" stringValue:base64Str];
5> 添加子节点
[msg addChild:attachment];
6> 发送消息
[[XMPPTool sharedXMPPTool].xmppStream sendElement:msg];
3. 隐藏控制器
[self dismissViewControllerAnimated:YES completion:nil];
*/
// =========================== 我的信息 电子名片模块 =========================================
/**
* ==== 获取 电子名片 信息 ====
注意:
显示头像和微信号
从数据库中获取用户信息
步骤:
1.获取登陆用户的电子名片信息 (内部实现:会去数据库查找, 不需要我们自己写)
XMPPvCardTemp *myvCard = [XMPPTool sharedXMPPTool].vCard.myvCardTemp;
2.获取头像,设置头像
self.iconImage.image = [UIImage imageWithData:myvCard.photo];
3.微信号(显示用户名)
self.wcNumL.text = [NSString stringWithFormat:@"微信号:%@",[LCAccount shareAccount].loginUserName];
*/
/**
* ==== 修改电子名片信息 ====
注意:
1. 修改头像图片 didFinishPickingMediaWithInfo代理方法中
1> 获取修改后的图片
UIImage *editImage = info[UIImagePickerControllerEditedImage];
2> 更改cell的头像图片
self.iconImageV.image = editImage;
3> 移除图片选择控制器
[self dismissViewControllerAnimated:YES completion:nil];
4> 把新的头像保存到服务器
[self editvCardViewController:nil didFinishSave:nil];
2. 修改信息
把数据保存到服务器
1> 获取当前电子名片
XMPPvCardTemp *myVCard = [XMPPTool sharedXMPPTool].vCard.myvCardTemp;
2> 重新设置myVCrad的属性
myVCard.photo = UIImageJPEGRepresentation(self.iconImageV.image, 1.00); //按1.00比例上传图片
myVCard.nickname = self.nickNameL.text;
myVCard.orgName = self.orgNameL.text;
3> 把数据保存到服务器
[[XMPPTool sharedXMPPTool].vCard updateMyvCardTemp:myVCard];
内部实现:数据上传是把整个电子名片的数据都重新上传了一次,包括图片
*/
// =========================== 资源管理 =========================================
/**
* ==== xmpp 程序退出时释放相应的资源 ====
注意:
在 dealloc 方法中调用
步骤:
1. 移除代理
[_xmppStream removeDelegate:self];
2. 取消模块
[_avatar deactivate];
[_vCard deactivate];
3. 断开连接
[_xmppStream disconnect];
4. 清空资源
_xmppStream = nil;
_avatar = nil;
_vCard = nil;
_vCardStorage = nil;
*/
/**
* 电子名片(vCard)
1> 是一种机制: 获取电子名片 或者 发送电子名片数据.
2> 获取电子名片, 使用XMPPFramework 里面提供的 "电子名片模块 "
3> 如何使用:
> 导入 "电子名片模块" 头文件
> 激活 "电子名片模块"
4> "电子名片模块 "的内部实现:
> 发送请求,从服务器获取电子名片(个人信息)数据
<iq type="get" to="lisi@licheng.local"><vCard xmlns="vcard-temp"/></iq>
> 接收服务器返回的数据, 把数据缓存到本地的数据库
为什么mycVard.jid是空的?
因为: 服务器返回的电子名片的xml数据中没有JABBERID节点
*/
/**
* 如何 打开xmpp的日志
1> 打开XMPP/Core/XMPPLogging.h文件, 第67行, XMPP_LOGGING_ENABLED 设置为1
2> 需要配置xmpp日志的启动. 在appdelegate.m的didfinishlauching方法中添加:
前提: 导入:DDLog.h 和 DDTTYLogger.h
[DDLog addLogger:[DDTTYLogger sharedInstance]];
*/
/**
* 对openfire的二次开发
1> openfire基于java开发, 获取到源代码,进行二次开发
2> 每开发一个功能模块, 最终打包成一个插件
3> 将插件添加到openfire后台, app就可以实现该插件里面的功能.
*/
/**
* 花名册模块的数据存储原理
1> 沙盒里保存的数据库里的数据只是单个用户的
2> 如果其他用户登录, 则数据库里原来用户的数据会被删除
*/
/**
* XMPP框架 实时聊天实现
xmpp 提供了 "消息模块" , 来实现实时聊天.
不管是哪个用户登录, 实时聊天的数据都保存在同一个数据库的表中.
实现:
1> 接收到好友的聊天数据后, 把数据保存到数据库.
2> 从数据库中获取, 显示到表格上
*/
/**
* 文件发送
图片, 音频, 视频, 文档等.
xmpp对ios不支持音频等发送, 需要自己实现.
实现方式:
1> 方式二: 添加属性 bodyType 确定发送的文件类型
添加子节点 attachment
<message type="chat" from="" to="lisi@licheng.local" bodyType="image/text/sound/doc/xls">
<body>存文本消息</body>
<attachment>文件内容 转成 字符串 </attachment> // 自己添加这一行
</message>
这种方式一定要有body子节点, 不然没法发送
2> 方式二: 通过 文件服务器 来实现
-- 1. ios客户端A 把文件上传到 文件服务器
-- 2. 文件服务器 返回文件路径给 ios客户端A
-- 3. ios客户端A 把文件路径 传递给 openfire服务器
-- 4. openfire服务器 把文件路径 发送给 ios客户端B
-- 5. ios客户端B 通过文件路径 到 文件服务器 读取文件
*/
/**
* 电子名片(vCard)
1> 是一种机制: 获取电子名片 或者 发送电子名片数据.
2> 获取电子名片, 使用XMPPFramework 里面提供的 "电子名片模块 "
3> 如何使用:
> 导入 "电子名片模块" 头文件
> 激活 "电子名片模块"
4> "电子名片模块 "的内部实现:
> 发送请求,从服务器获取电子名片(个人信息)数据
<iq type="get" to="lisi@licheng.local"><vCard xmlns="vcard-temp"/></iq>
> 接收服务器返回的数据, 把数据缓存到本地的数据库
为什么mycVard.jid是空的?
因为: 服务器返回的电子名片的xml数据中没有JABBERID节点
*/
/**
* 如何 打开xmpp的日志
1> 打开XMPP/Core/XMPPLogging.h文件, 第67行, XMPP_LOGGING_ENABLED 设置为1
2> 需要配置xmpp日志的启动. 在appdelegate.m的didfinishlauching方法中添加:
前提: 导入:DDLog.h 和 DDTTYLogger.h
[DDLog addLogger:[DDTTYLogger sharedInstance]];
*/
/**
* 对openfire的二次开发
1> openfire基于java开发, 获取到源代码,进行二次开发
2> 每开发一个功能模块, 最终打包成一个插件
3> 将插件添加到openfire后台, app就可以实现该插件里面的功能.
*/
/**
* 花名册模块的数据存储原理
1> 沙盒里保存的数据库里的数据只是单个用户的
2> 如果其他用户登录, 则数据库里原来用户的数据会被删除
*/
/**
* XMPP框架 实时聊天实现
xmpp 提供了 "消息模块" , 来实现实时聊天.
不管是哪个用户登录, 实时聊天的数据都保存在同一个数据库的表中.
实现:
1> 接收到好友的聊天数据后, 把数据保存到数据库.
2> 从数据库中获取, 显示到表格上
*/
/**
* 文件发送
图片, 音频, 视频, 文档等.
xmpp对ios不支持音频等发送, 需要自己实现.
实现方式:
1> 方式二: 添加属性 bodyType 确定发送的文件类型
添加子节点 attachment
<message type="chat" from="" to="lisi@licheng.local" bodyType="image/text/sound/doc/xls">
<body>存文本消息</body>
<attachment>文件内容 转成 字符串 </attachment> // 自己添加这一行
</message>
这种方式一定要有body子节点, 不然没法发送
2> 方式二: 通过 文件服务器 来实现
-- 1. ios客户端A 把文件上传到 文件服务器
-- 2. 文件服务器 返回文件路径给 ios客户端A
-- 3. ios客户端A 把文件路径 传递给 openfire服务器
-- 4. openfire服务器 把文件路径 发送给 ios客户端B
-- 5. ios客户端B 通过文件路径 到 文件服务器 读取文件
*/