前言
前面幾篇文章我們主要搞了搞關于好友列表的相關技術以及邏輯,還有用戶上下線監控這個沒說,我準備放到最后再說,比較簡單,今天我們就說一下關于添加好友和刪除好友的邏輯和技術.添加好友的邏輯比較多.
XMPP好友關系
在前面的好友列表中,我們也設定當兩個JID相互訂閱才認定兩者是好友關系,那么為什么要這么設定呢?其實主要是因為添加好友的緣故,這文章的后面我會具體的說到,我們看一下兩個JID賬號都有什么訂閱關系.總共有五種訂閱關系.分別是None、To、From、Both、Remove;五種定影狀態楚翔的情況如下表所示.
訂閱狀態 | 情況出現情景 |
---|---|
None | 當A訂閱了B之后,B并沒有訂閱A的時候,A中的B的訂閱狀態就為None |
To和From | 當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>