在介绍好友列表前,我们要先了解以下几个相关类:
- XMPPRosterCoreDataStorage //好友本地存储器
- XMPPRoster //好友实体类
- XMPPUserCoreDataStorageObject //用户管理实例
- XMPPRosterDelegate //好友相关代理
另外,代码里会涉及到
- XMPPvCardTempModule //电子名片模块
- XMPPvCardAvatarModule //头像模块
这两个类,我会在后面为大家详解
xmpp里获取好友有两种方法,第一种是登录后自动获取存储到本地,展示列表时从本地取,第二种是通过代理获取.别急,我们一个一个来.
代码
xmppstream 是通信通道,不管干什么,都要先开启通道,后面的好友,消息列表,电子名片,断线重连啊等等所有的功能都要基于xmppstream激活.后面就不重复XMPPStream创建的代码了.
.h文件
@interface XMPPManager : NSObject
//通讯管道,所有通讯 都是通过xmppStream管道来完成
@property (nonatomic, strong) XMPPStream *stream;
//好友本地存储器
@property (nonatomic, strong) XMPPRosterCoreDataStorage *roseterDataStorage;
//好友实体类
@property (nonatomic, strong) XMPPRoster *roster;
.m文件
@implementation XMPPManager
#pragma mark --单例
+(XMPPManager *)sharedInstance
{
static XMPPManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[XMPPManager alloc] init];
});
return manager;
}
#pragma mark --对xmpp进行初始化
- (instancetype)init{
self = [super init];
if (self) {
/***************************** 通信通道 *****************************/
self.stream = [[XMPPStream alloc] init];
//openfire服务器地址
self.stream.hostName = KHostName;
//openfire端口号
self.stream.hostPort = KHostPort;
//为xmppSream添加代理
[self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
/***************************** 好友列表 *****************************/
self.roseterDataStorage = [XMPPRosterCoreDataStorage sharedInstance];
self.roster = [[XMPPRoster alloc]initWithRosterStorage:self.roseterDataStorage dispatchQueue:dispatch_get_main_queue()];
//激活
[self.roster activate:self.stream];
[self.roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
//自动获取好友,默认为YES,如果想手动获取,关闭自动获取
self.roster.autoFetchRoster = NO;
return self;
}
- 有了上面的代码,成功登录后会自动把好友存储到本地
第一种:从本地存储获取好友列表
#import "ChatContacts2ViewController.h"
#import "SDContactsModel.h"
@interface ChatContacts2ViewController ()<XMPPStreamDelegate,XMPPRosterDelegate,UITableViewDataSource,UITableViewDelegate,NSFetchedResultsControllerDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSFetchedResultsController *resultsController;
@property (nonatomic, strong) NSMutableArray *contactsArr;
@property (strong,nonatomic) UITextField *friendSearchTextField;
@end
@implementation ChatContacts2ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//UI
_tableView = [[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStyleGrouped];
[self.view addSubview:_tableView];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.rowHeight = 88;
_tableView.tableFooterView = [UIView new];
_tableView.sectionHeaderHeight = 0.01;
_tableView.sectionFooterHeight = 20;
//添加代理
[[XMPPManager sharedInstance].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//获取好友列表
[self loadUser];
}
/**
* 获取好友的结果集
*/
- (void)loadUser
{
// 1.上下文
NSManagedObjectContext *rosterContext = [XMPPManager sharedInstance].roseterDataStorage.mainThreadManagedObjectContext;
// 2.请求查询
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];
// 3.设置排序
NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey:@"jidStr" ascending:YES];
request.sortDescriptors = @[sort];
self.resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:rosterContext sectionNameKeyPath:nil cacheName:nil];
self.resultsController.delegate = self;
NSError * error = nil;
[self.resultsController performFetch:&error];
NSLog(@"%@",[[error userInfo] description]);
if (self.resultsController.fetchedObjects.count) {
_contactsArr = [NSMutableArray array];
//转模型
for (XMPPUserCoreDataStorageObject *user in self.resultsController.fetchedObjects) {
/**
* 好友关系subscription状态有5种
both - 互为好友(只有这个状态,俩人才能互发消息)
none - 互不为好友
to - 请求添加对方为好友,对方还没有同意
from - 对方添加我为好友,自己还没有同意
remove - 曾经删除的好友
*/
//互为both才加入
if ([user.subscription isEqualToString:@"both"]) {
SDContactsModel *model = [[SDContactsModel alloc]init];
model.jid = user.jid;
model.jidStr = user.jidStr;
model.photo = user.photo;
model.relation = user.subscription;
model.displayName = user.displayName;
model.presenceType = @"unavailable";
[_contactsArr addObject:model];
}
}
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.contactsArr.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * ID = @"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell==nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
// 获取好友列表
SDContactsModel *model = self.contactsArr[indexPath.section];
cell.textLabel.text= [NSString stringWithFormat:@"%@(%@)",model.displayName,model.relation];
//状态
if ([model.presenceType isEqualToString:@"available"]) {
cell.detailTextLabel.text = @"在线";
cell.detailTextLabel.textColor = [UIColor greenColor];
}else if ([model.presenceType isEqualToString:@"unavailable"]) {
cell.detailTextLabel.text = @"离线";
cell.detailTextLabel.textColor = [UIColor grayColor];
}else
{
cell.detailTextLabel.text = model.presenceType;
cell.detailTextLabel.textColor = [UIColor grayColor];
}
if (model.photo) { // 先从本地数据库中取数据
cell.imageView.image = model.photo;
}else{ // 从服务器上取数据
NSData * imagedata = [[XMPPManager sharedInstance].vCardAvatarModule photoDataForJID:model.jid];
cell.imageView.image = [UIImage imageWithData:imagedata];
model.photo = cell.imageView.image;
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
ChatTwoViewController *chatVC = [[ChatTwoViewController alloc]init];
SDContactsModel *user = [self.contactsArr objectAtIndex:indexPath.section];
chatVC.chatJIDStr = user.jidStr;
[self.navigationController pushViewController:chatVC animated:YES];
}
#pragma mark - XMPPStreamDelegate
//好友状态变化通知(好友上线下线,好友添加请求都在这个代理收到)
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
//上线状态 - "available"
//离线状态 - "unavailable"
//同意添加请求 - "subscribed"
//拒绝添加请求 - "unsubscribed"
//删除确认-"unsubscribe"
if ([presence.type isEqualToString:@"subscribed"]) {
[self showAlertWithTitle:[NSString stringWithFormat:@"%@同意了您的请求",presence.from.user]];
//刷新列表
}else if ([presence.type isEqualToString:@"unsubscribed"]) {
[self showAlertWithTitle:[NSString stringWithFormat:@"%@拒绝了您的请求",presence.from.user]];
}
NSLog(@"好友状态变化通知");
//刷新好友的在线状态
[self.contactsArr enumerateObjectsUsingBlock:^(SDContactsModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
if ([model.jid.user isEqualToString:presence.from.user]) {
model.presenceType = presence.type;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:idx];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
*stop = YES;
}
}];
}
第二种:通过代理获取
注意:如果手动获取,那么在xmppManager中创建xmppRoster的时候要关闭自动获取.
方法:
self.roster.autoFetchRoster = NO;
.m文件
#import "ChatContactsViewController.h"
#import "SDContactsModel.h"
@interface ChatContactsViewController ()<XMPPRosterDelegate,XMPPvCardTempModuleDelegate,UITableViewDataSource,UITableViewDelegate>
@property (nonatomic, strong) NSMutableArray *rosterArr;
@property (nonatomic, strong) UITableView *tableView;
@end
@implementation ChatContactsViewController
- (void)viewDidLoad {
[super viewDidLoad];
//UI
_tableView = [[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStyleGrouped];
[self.view addSubview:_tableView];
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"RosterCellID"];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.rowHeight = 88;
_tableView.tableFooterView = [UIView new];
_tableView.sectionHeaderHeight = 0.01;
_tableView.sectionFooterHeight = 20;
self.rosterArr = [NSMutableArray array];
//添加代理
[[XMPPManager sharedInstance].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
[[XMPPManager sharedInstance].vCardTempModule addDelegate:self delegateQueue:dispatch_get_main_queue()];
//获取好友
[[XMPPManager sharedInstance].roster fetchRoster];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.rosterArr.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"RosterCellID"];
SDContactsModel *model = [self.rosterArr objectAtIndex:indexPath.section];
cell.textLabel.text = model.jid.user;
cell.detailTextLabel.text = model.relation;
UIImage *image = [UIImage imageWithData:model.vCard.photo];
cell.imageView.image = image;
return cell;
}
#pragma mark - XMPPRosterDelegate
//开始检索
- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender
{
NSLog(@"%s__%d__|开始检索", __FUNCTION__, __LINE__);
}
//每检索到一个好友都会执行一次
- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
{
NSLog(@"listTVC 接收好友信息 %d", __LINE__);
/**
* 好友信息状态有5种
both - 互为好友
none - 互不为好友
to - 请求添加对方为好友,对方还没有同意
from - 对方添加我为好友,自己还没有同意
remove - 曾经删除的好友
*/
// 自己和对方之间的关系
NSString *description = [[item attributeForName:@"subscription"] stringValue];
NSLog(@"关系%@", description);
// 显示我的好友
if ([description isEqualToString:@"both"]) {
// 获取好友的JID
NSString *friendJID = [[item attributeForName:@"jid"] stringValue];
XMPPJID *jid = [XMPPJID jidWithString:friendJID];
// 如果数组中含有这个用户,那么不添加进数组
if ([self.rosterArr containsObject:jid]) {
NSLog(@"已经有该好友");
return;
}
//通过这个方法,获取名片(在代理里获取到)
[[XMPPManager sharedInstance].vCardTempModule fetchvCardTempForJID:jid ignoreStorage:YES];
// 添加好友到数组中
SDContactsModel *model = [[SDContactsModel alloc]init];
model.jid = jid;
model.relation = description;
[self.rosterArr addObject:model];
}
}
//检索结束
- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender
{
NSLog(@"%s__%d__|接受结束", __FUNCTION__, __LINE__);
[self.tableView reloadData];
}
#pragma mark --XMPPvCardTempModuleDelegate
-(void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule didReceivevCardTemp:(XMPPvCardTemp *)vCardTemp forJID:(XMPPJID *)jid
{
for (SDContactsModel *model in self.rosterArr) {
if ([model.jid.user isEqualToString:jid.user]) {
model.vCard = vCardTemp;
}
}
[self.tableView reloadData];
}
-(void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule failedToFetchvCardForJID:(XMPPJID *)jid error:(DDXMLElement *)error
{
NSLog(@"获取名片失败:%@",error);
}
那么两种方法有什么区别呢?
第一种:从本地可以直接获取好友JID,以及照片等信息
第二种,只能获取到JID,照片要再发起请求.
第二种方法有个好处是:比如现在你新加了一个好友,在代理里会立马检索到,然后更新你的好友列表,(和服务器同步的).第一种的话,你只能在加到好友后做本地UI假更改,来改变数组,从而实现即时刷新列表了.(本地存储里不会更新)
添加好友于删除好友
- 添加好友
方法一:如果目标用户不在Roster中,建议使用方法二
XMPPJID *jid = [XMPPJID jidWithUser:self.friendSearchTextField.text domain:KDomin resource:kResource];
[[XMPPManager sharedInstance].roster subscribePresenceToUser:jid];
方法二:(推荐使用方法)
XMPPJID *jid = [XMPPJID jidWithUser:self.friendSearchTextField.text domain:KDomin resource:kResource];
[[XMPPManager sharedInstance].roster addUser:jid withNickname:jid.user];
- 删除好友
方法一:
XMPPJID *jid = [XMPPJID jidWithUser:self.friendSearchTextField.text domain:KDomin resource:kResource];
[[XMPPManager sharedInstance].roster removeUser:jid];
XMPPRosterDelegate
/**
* 接收到好友状态变化会调用,或Subscription申请:from to之类
**/
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence;
/**
* 开始检索好友
**/
- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender withVersion:(NSString *)version;
/**
* 每检索到一个好友调用一次
*
* Example:
*
* <item jid='romeo@example.net' name='Romeo' subscription='both'>
* <group>Friends</group>
* </item>
**/
- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(NSXMLElement *)item;
/**
* 检索结束
**/
- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender;
网友评论