流水不争先,争的是滔滔不绝

XMPPFramework开发(五):添加/删除好友

openfire 云聊IM 1059℃

前言

前面几篇文章我们主要搞了搞关于好友列表的相关技术以及逻辑,还有用户上下线监控这个没说,我准备放到最后再说,比较简单,今天我们就说一下关于添加好友和删除好友的逻辑和技术.添加好友的逻辑比较多.

添加好友

XMPP好友关系

在前面的好友列表中,我们也设定当两个JID相互订阅才认定两者是好友关系,那么为什么要这么设定呢?其实主要是因为添加好友的缘故,这文章的后面我会具体的说到,我们看一下两个JID账号都有什么订阅关系.总共有五种订阅关系.分别是None、To、From、Both、Remove;五种定影状态楚翔的情况如下表所示.

XMPPFramework中添加/删除好友相关的方法

XMPPFramework的好友管理类是XMPPRoster,下面我们就看一下我们所需要的方法以及代理回调方法.

//根据JID发送一个订阅信息
- (void)subscribePresenceToUser:(XMPPJID *)jid;

//同意某个订阅
- (void)acceptPresenceSubscriptionRequestFrom:(XMPPJID *)jid andAddToRoster:(BOOL)flag;

//拒绝某个订阅
- (void)rejectPresenceSubscriptionRequestFrom:(XMPPJID *)jid;

//删除好友,这里可不是取消订阅,而是删除.
- (void)removeUser:(XMPPJID *)jid;

//收到好友请求的回调方法
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence;

//好友状态的获取回调方法(XMPPStream类)
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence;

XMPPFramework 中添加好友的流程

骚栋在SDChat中采用的是先发送订阅的消息,如果被拒绝再删除好友的整体逻辑.所以我们需要先看一下逻辑的流程图.流程图我把它分成两种情况区别对待,如下所示.

第一种是用户A向用户B发送订阅请求,用户B同意A的订阅请求.

第二种是用户A向用户B发送订阅请求,用户B拒绝了A的订阅请求.

上面我们看到了用户的好友添加的流程图,下面我们就对着SDChat中的代码来具体看一下我们的流程.当然了,这个过程是需要两个用户来完成的,所以,我们假设有用户A和用户B两个用户.

用户A: 在SDAddContactVC这个类中输入用户B的账号,我们通过组装,然后使用XMPPRoster类中的- (void)subscribePresenceToUser:(XMPPJID *)jid方法向用户B发送订阅请求.

XMPPJID *jid = [XMPPJID jidWithUser:name domain:kDomin resource:kResource];
[[SDXmppManager defaulManager].roster subscribePresenceToUser:jid];

用户B:当用户A发出好友请求的时候,如果用户B不在线,那么用户A中好友列表的用户B的订阅状态就为To,用户B上线之后仍然会受到订阅消息;如果用户B当前在线,那么用户B会通过AppDelegate中的- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscription Request:(XMPPPresence *)presence这个代理方法获取到好友订阅的消息.由于一个好友请求可能发送多次,但是我们只需要提取其中一次就好,这样我们就需要判断从服务器来的好友订阅消息在SDUser中的addFriendArray数组中是否已经存在,如果不存在,那么我们将添加到数组中,并且发出一个通知,通知各个界面做出对应的改变.给用户一个直观的视觉体验.具体代码如下所示.

BOOL isExist = NO;
SDUser *user = [SDUser defaulUser];
//判断是否重复请求
for (XMPPJID *objJID in user.addFriendArray) {
    
    if ([presence.from.user isEqualToString:objJID.user] &&[presence.from.domain isEqualToString:objJID.domain]) {
        isExist = YES;
    }
    
}

if (!isExist) {
    [user.addFriendArray addObject:presence.from];
    //发送通知
    [[NSNotificationCenter defaultCenter]postNotificationName:AddNewContectMessage object:nil];
}

用户B:如果用户B当前在SDContactsVC联系人列表页面中,那么通过通知,我们刷新了页面.用户B可以直接通过页面来知道有新的好友请求消息.在这里为了防止内存的不必要的损耗,我们先判断一下当前的列表显示的cell是否在有”新的朋友”这一个Cell,如果有才进行刷新,没有的话,用户B在往上滑动到最顶端的时候回自动刷新.具体代码如下所示.

-(void)addNewContactAction{
    NSIndexPath *indexPath=[NSIndexPath indexPathForRow:0 inSection:0];
    
    //判断当前是否是在列表的最上端
    NSArray *indexPathArray = [self.contactsList indexPathsForVisibleRows];
    for (NSIndexPath *obj in indexPathArray) {
        if ([obj isEqual:indexPath]) {
            [self.contactsList reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone];
        }
    }
}

用户B:如果用户B在SDAddContactVC好友添加页面,那么通过App的好友通知,我们刷新了页面,用户B看到了当前的好友请求.如下图所示.

那么接下来还是要分用户B同意好友添加和用户B拒绝好友添加两种情况了.

第一种是用户A向用户B发送订阅请求,用户B同意A的订阅请求.

用户B: 用户B点击了好友同意,那么通过调用- (void)acceptPresenceSubscriptionRequest From:(XMPPJID *)jid andAddToRoster:(BOOL)flag这个方法,用户B就添加了用户A,这时候用户A和B的关系为Both或者是To-From的关系,认定两者是好友关系,代码如下所示.

XMPPJID *jidName = [XMPPJID jidWithUser:jidUser domain:kDomin resource:kResource];
XMPPRoster *roster = [SDXmppManager defaulManager].roster;
[roster acceptPresenceSubscriptionRequestFrom:jidName andAddToRoster:YES];

然后,我们需要把SDUser中addFriendArray的对应的JID删除掉,并且刷新页面通知用户,已经操作成功了.代码如下所示.

XMPPJID *deleteJID;

for (int i = 0; i<[SDUser defaulUser].addFriendArray.count; i++) {
    XMPPJID *obj = [SDUser defaulUser].addFriendArray[i];
    if ([obj.user isEqualToString:jidUser]) {       
        deleteJID = obj;
    }
}
[[SDUser defaulUser].addFriendArray removeObject:deleteJID];

UIAlertController *alerView =[UIAlertController alertControllerWithTitle:@"添加成功!" message:[NSString stringWithFormat:@"成功添加:%@成为了好友",jidName.user] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleCancel handler:nil];
[alerView addAction: alertAction];
[self presentViewController:alerView animated:YES completion:nil];
[self.addFirendList reloadData];

用户A: 如果用户B添加成功之后,那么用户A在好友列表中刷新页面就会获取到用户B了,至此,用户A成功的添加了用户B的好友.

第二种是用户A向用户B发送订阅请求,用户B拒绝了A的订阅请求.

用户B:用户B收到好友请求消息的时候,用户B点击"拒绝",然后我们会调用- (void) rejectPresenceSubscriptionRequestFrom:(XMPPJID *)jid这个方法,我们想用户A发送拒绝的消息.同时要删除好友列表中用户A,有人会问用户A与B现在不是还没有好友关系吗?为什么用户B的好友列表中会有A?原因是这样的,当用户A发送好友请求的时候,用户B的列表就有了用户A,不过订阅状态却是None,这时候,我们需要删除好友A.代码如下所示.

XMPPJID *jidName = [XMPPJID jidWithUser:jidUser domain:kDomin resource:kResource];
XMPPRoster *roster = [SDXmppManager defaulManager].roster;
[roster rejectPresenceSubscriptionRequestFrom:jidName];
[[SDXmppManager defaulManager].roster removeUser:jidName];

用户B:于此同时,我们还要删除本地中的好友请求数组中对应的数据.然后刷新页面,如下所示.

XMPPJID *deleteJID;
for (int i = 0; i<[SDUser defaulUser].addFriendArray.count; i++) {
    XMPPJID *obj = [SDUser defaulUser].addFriendArray[i];
    if ([obj.user isEqualToString:jidUser]) {
        deleteJID = obj;
    }
}
[[SDUser defaulUser].addFriendArray removeObject:deleteJID];
[self.addFirendList reloadData];

用户A:当用户B拒绝了好友请求的时候,用户A通过AppDelegate中的- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence方法可以获取到用户B拒绝的消息,然后把用户A好友列表中的用户B删除掉.当然了,这个代理方法另外一个作用就是获取好友上下线状态,所以消息类型较多,我们需要判断一下消息类型,然后酌情处理.具体代码如下所示.

if ([presence.type isEqualToString:@"unsubscribe"]) {
    //从我的本地通讯录中将他移除
    [[SDXmppManager defaulManager].roster removeUser:presence.from];
}

XMPPFramework 中删除好友的流程

在SDChat中,用户是可以删除好友的,那就是联系人列表左滑菜单会出现删除按钮,如图所示.

那么逻辑实现也是比较简单,但是还是需要两个用户,这里,我们仍然假设有用户A和用户B,现在两者互为好友关系.

用户A :用户A在用户B所对应的Cell上左滑,然后出现删除按钮,用户A点击删除,就会调用如下的方法,删除好友,这里需要注意的一点就是,这是说的是直接删除好友,而不是取消订阅.具体代码如下所示.

[SDXmppManager defaulManager].roster removeUser:jid];

用户A :当上面这句代码执行完成之后,获取每一个好友节点的代理方法-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item就会重洗执行一遍,这时候,用户A好友列表中用户B的订阅状态为"Remove";所以我们要做的是在代理方法中删除本地数据并且刷新页面,代码如下所示.

if ([[[item attributeForName:@"subscription"] stringValue] isEqualToString:@"remove"]) {
    NSString *SJid = [[item attributeForName:@"jid"] stringValue];
    //把字符串类型的JID转换成XMPPJID
    XMPPJID *jid = [XMPPJID jidWithString:SJid];
    SDContactModel *contact;
    for (SDContactModel *obj in self.user.contactsArray) {
        if ([obj.jid.user isEqualToString:jid.user]) {
            contact = obj;
        }
    }
    [self.user.contactsArray removeObject:contact];
    [self networkingWithContactsArray];
}

用户B :用户A虽然删除了用户B,但是用户B好友列表中含有用户A.所以,当用户A删除了用户B之后,用户B通过AppDelegate中的- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence方法可以获取到用户B拒绝的消息,然后把用户B好友列表中的用户A删除掉.这个其实添加好友的拒绝好友添加情况的最后一步是一致的.具体代码如下所示.

if ([presence.type isEqualToString:@"unsubscribe"]) {
    //从我的本地通讯录中将他移除
    [[SDXmppManager defaulManager].roster removeUser:presence.from];
}

经过上述的步骤,用户A和用户B就相互解除了好友关系了.

SDChat好友添加存在问题(下述问题已于12.21号解决)

现在的SDChat基本上好友添加这一模块逻辑上没有太大的问题,就是一些限制条件还没做,比如限制不能添加自己为好友,比如对方已经请求添加好友了,但是你也请求添加对方为好友,这样两者直接就是为好友关系.逻辑上可能出现问题.再比如优化问题,通过JID并不能很好展现一个联系人的信息,如果我们通过JID获取好友请求人的电子名片是不是能更人性化一些呢,等等.SDChat并不是完美的,所以如果遇到任何问题,可以联系骚栋.谢谢.

结束

XMPPFramework的添加/删除好友到这里就基本结束了,骚栋在搞添加/删除好友这个模块的时候,因为不太了解XMPPFramework中各种方法,所以坑填了比较多,当然了,自己测试的过程中也可能遇到很多坑点,如果有任何疑问欢迎联系骚栋.接下来一篇,我们说一下XMPPFramework的核心模块单人聊天模块,比较简单,希望大家能持续关注.谢谢.最后还把SDChat的传送门送给大家.大家可以对照着Demo来看本篇博客.

关于作者

作者:神经骚栋

链接:https://www.jianshu.com/p/51920743b1d8

版权声明:部分文章、图片等内容为用户发布或互联网整理而来,仅供学习参考。如有侵犯您的版权,请联系我们,将立刻删除。
点击这里给我发消息