美文网首页iOS开发专区IMiOS 开发随笔
XMPPFramework开发(五):添加/删除好友

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

作者: 神经骚栋 | 来源:发表于2016-12-21 09:59 被阅读1671次

    前言


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

    添加好友

    XMPP好友关系


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

    订阅状态 情况出现情景
    None 当A订阅了B之后,B并没有订阅A的时候,A中的B的订阅状态就为None
    ToFrom 当A订阅了B之后,B上线之后同意之后,A中B的订阅状态就为To,B中A的订阅状态就为From
    Both 当A订阅了B之后,B也在线同意了A的订阅请求,那么A中B的订阅状态就为Both
    Remove 当A删除B之前,在B中的定义状态就为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同意好友添加和用户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来看本篇博客.

    -->SDChat传送门🚪

    </br>


    相关文章

      网友评论

      • 心语风尚:使用XMPPFramework开发即时通讯 这样是不是意味着用第三方的即时通讯 不是自己做的了 面试官说我们自己做即时通讯是什么意思
      • Szbdzs:我遇到了个问题,我手机和模拟器是在同一个wifi下,我用手机登录去加模拟器上的用户,然后添加成功后,回到好友列表也有数据,但是我重启一次demo再重登的后,好友列表里面就没有数据了
        神经骚栋:@Szbdzs 每一次登录都是要获取好友列表的~
      • 褪色的旧衬衫:- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence A在删除或拒绝B后,登录B 不会返回presence.type 为unsubscribe吧
      • Joy___:效率可以呀,:smile:
        神经骚栋:@Joy___ 一般一般:smile::smile::smile:
      • GavinKang:很给力,会一直关注
        神经骚栋:@iOS开发的毛毛虫 谢谢

      本文标题:XMPPFramework开发(五):添加/删除好友

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