美文网首页
XMPPFramework(二)好友列表

XMPPFramework(二)好友列表

作者: O乞力马扎罗的雪O | 来源:发表于2018-06-13 16:58 被阅读0次

在介绍好友列表前,我们要先了解以下几个相关类:

  • 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;

相关文章

网友评论

      本文标题:XMPPFramework(二)好友列表

      本文链接:https://www.haomeiwen.com/subject/viijeftx.html