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


前言


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

添加好友


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>


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,347评论 0 15
  • 搞事前言 前一篇博客,我们对XMPPFramework的登录注册功能以及逻辑做了详细的说明,用户登录完成之后,我们...
    神经骚栋阅读 3,376评论 4 28
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,574评论 25 707
  • 接睿哥儿回来的路上,我告诉他我把白马带来了。白马是我弟家养的小狗,特别嘴馋,每次我回家我吃什么都喜欢喂它一点...
    睿哥儿阅读 319评论 0 0