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>


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,868評論 18 139
  • 點擊查看原文 Web SDK 開發手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統...
    layjoy閱讀 13,890評論 0 15
  • 搞事前言 前一篇博客,我們對XMPPFramework的登錄注冊功能以及邏輯做了詳細的說明,用戶登錄完成之后,我們...
    神經騷棟閱讀 3,447評論 4 28
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,067評論 25 708
  • 接睿哥兒回來的路上,我告訴他我把白馬帶來了。白馬是我弟家養的小狗,特別嘴饞,每次我回家我吃什么都喜歡喂它一點...
    睿哥兒閱讀 354評論 0 0