XMPPFramework開(kāi)發(fā)(三):好友列表


搞事前言


前一篇博客,我們對(duì)XMPPFramework的登錄注冊(cè)功能以及邏輯做了詳細(xì)的說(shuō)明,用戶(hù)登錄完成之后,我們需要做的就是獲取到當(dāng)前賬號(hào)的好友列表和個(gè)人信息,今天這一篇博客就是對(duì)好友列表的相關(guān)邏輯以及代理方法來(lái)做一下講解說(shuō)明.我們先看看SDChat中的好友列表示意圖.


XMPPFramework中好友關(guān)系說(shuō)明解釋


在XMPPFramework中呢,好友關(guān)系是可以通過(guò)訂閱來(lái)實(shí)現(xiàn)的,也就是說(shuō)A與B相互訂閱,那么A與B就是好友了,如果A只是訂閱了B,B沒(méi)有訂閱A,那么我們就說(shuō)A與B兩者不是好友,當(dāng)然了,我在實(shí)際過(guò)程中搞好友添加的邏輯還是比較多的,這里需要了解A與B相互訂閱(openfire服務(wù)器中訂閱狀態(tài)為both,當(dāng)然了,訂閱狀態(tài)也有from和to,這樣的也算是好友.具體情況后面會(huì)詳細(xì)說(shuō)明),那么A與B就是好友這一個(gè)邏輯即可.


好友列表獲取流程.


當(dāng)用戶(hù)登錄成功之后,我們做的最主要的一個(gè)模塊就是加載好友列表模塊.那么好友加載模塊的整體流程是怎樣的呢?我們先看一個(gè)SDChat好友列表的流程圖,幫助我們熟悉好友列表在實(shí)際過(guò)程中如何展現(xiàn)的.(圖片可能看不清楚,請(qǐng)自行下載查看,謝謝.)


好友服務(wù)器數(shù)據(jù)獲取代碼部分

XMPPFramework中好友列表的管理核心類(lèi)是XMPPRoster,這個(gè)類(lèi)可以用來(lái)對(duì)好友的信息獲取,添加,刪除等操作.在SDChat中,我們把XMPPRoster聲明為SDXmppManager的一個(gè)屬性對(duì)象,并且在初始化過(guò)程中激活好友模塊.代碼如下所示.(說(shuō)明:XMPPRosterCoreDataStorage對(duì)象使用存儲(chǔ)好友數(shù)據(jù)的.)

self.rosterCoreDataStorage= [XMPPRosterCoreDataStorage sharedInstance];

self.roster = [[XMPPRoster alloc]initWithRosterStorage:self.rosterCoreDataStorage dispatchQueue:dispatch_get_global_queue(0, 0)];

//激活roster
[self.roster activate:self.stream];

其實(shí)好友獲取是有兩種方式的,騷棟使用的是代理方法獲取好友節(jié)點(diǎn)的.由于代理方法默認(rèn)的是在登錄成功之后默認(rèn)就會(huì)調(diào)用獲取好友節(jié)點(diǎn),但是我做頁(yè)面的時(shí)候需要調(diào)節(jié)一下好友節(jié)點(diǎn)獲取的時(shí)機(jī),所以,我就把XMPPRoster的自動(dòng)獲取好友節(jié)點(diǎn)功能關(guān)掉了.當(dāng)然了,你可以使用自動(dòng)獲取.這個(gè)需要根據(jù)實(shí)際情況而定,實(shí)現(xiàn)代碼如下所示.

self.roster.autoFetchRoster = NO;

這樣在我們登錄完成之后,我們需要在- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender這個(gè)方法中手動(dòng)調(diào)起獲取好友的方法.調(diào)起方法也很簡(jiǎn)單,只需要一行代碼就可以.

[[SDXmppManager defaulManager].roster fetchRoster];

當(dāng)我們調(diào)起了獲取好友的方法之后,我們需要在在聯(lián)系人列表(SDContactsVC)這個(gè)控制器中先設(shè)置XMPPRoster對(duì)象的代理.我是在初始化就設(shè)置了代理對(duì)象.

[[SDXmppManager defaulManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

設(shè)置完成之后代理方法其實(shí)總共是有三個(gè)的,三個(gè)代理調(diào)取的實(shí)際分別是所有好友節(jié)點(diǎn)獲取開(kāi)始,每一個(gè)好友節(jié)點(diǎn)獲取到的時(shí)候,所有好友節(jié)點(diǎn)獲取完成之后,我們可以根據(jù)實(shí)際情況來(lái)進(jìn)行不同的操作.比如我們?cè)讷@取開(kāi)始之前初始化好友節(jié)點(diǎn)數(shù)組,獲取結(jié)束刷新頁(yè)面等等,具體的三個(gè)代理方法如下所示.

//開(kāi)始獲取好友節(jié)點(diǎn)列表的時(shí)候
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender;

//獲取每一個(gè)好友節(jié)點(diǎn)的時(shí)候
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item;

//結(jié)束獲取好友節(jié)點(diǎn)列表的時(shí)候
-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender;

這是我只使用了后面的兩個(gè)代理方法,這是有原因的,因?yàn)槲业暮糜压?jié)點(diǎn)數(shù)組([SDUser defaulUser].contactsArray)不需要每一次獲取都進(jìn)行更新,而且這三個(gè)代理方法在實(shí)際使用過(guò)程中,自動(dòng)調(diào)取的次數(shù)很多,比如添加完好友或者刪除完好友都能自動(dòng)調(diào)取這個(gè)三個(gè)代理方法,為了不必要的麻煩,所以我只是用了后面的兩個(gè)代理方法.還是那句話,大家可以根據(jù)自己的實(shí)際情況自行調(diào)用不同的代理方法.

我們先看一下SDChat中在-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item;騷棟都做了什么樣的操作.首先,我們獲取到每一個(gè)好友節(jié)點(diǎn)item,然后我們先判斷item節(jié)點(diǎn)的訂閱信息subscription的值,我們需要的好友是雙方相互訂閱,也就是"subscription”屬性為"both"、"from”、”to”才是我們需要的好友節(jié)點(diǎn).所以符合這三種情況的都是我們需要的好友節(jié)點(diǎn),所以if的篩選條件就出來(lái)了,如下代碼所示.其他訂閱類(lèi)型不同的節(jié)點(diǎn)我們后面會(huì)說(shuō)到具體的情況.

if ([[[item attributeForName:@"subscription"] stringValue] isEqualToString:@"both"]||[[[item attributeForName:@"subscription"] stringValue] isEqualToString:@"from"]||[[[item attributeForName:@"subscription"] stringValue] isEqualToString:@"to"]) {

}

在篩選完成之后,我們需要做的事情就是獲取到item節(jié)點(diǎn)的JID信息了,這里我們只需要兩行代碼就可以完成了.

NSString *SJid = [[item attributeForName:@"jid"] stringValue];
     
XMPPJID *jid = [XMPPJID jidWithString:SJid];

不管是前期界面上顯示好友的JID信息,還是后期顯示電子名片信息,我們都需要先遍歷好友節(jié)點(diǎn)數(shù)組([SDUser defaulUser].contactsArray)判斷數(shù)組中是否已經(jīng)存在該好友信息了.這樣做的原因是因?yàn)?code>-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item;這個(gè)代理方法可能對(duì)一個(gè)好友節(jié)點(diǎn)獲取多次,如果我們不進(jìn)行選擇性的添加的話,數(shù)組可能會(huì)出現(xiàn)好友重復(fù)的現(xiàn)象的,所以我們需要先判斷是否存在該好友信息,具體代碼如下所示.(關(guān)于isDeleteFriend布爾值的存在的意義,當(dāng)我們刪除好友的時(shí)候,訂閱信息subscription的值可能并不是"remove",有可能是"both",所有我們這里需要加一個(gè)布爾值,后面的刪除好友,我們會(huì)詳細(xì)說(shuō)明的.)

BOOL isExist = NO;
            
for (SDContactModel *contact in self.user.contactsArray) {
                
       if ([contact.jid.user isEqualToString:jid.user]) {
                    
            isExist = YES;
                    
          }
}         

判斷完是否存在好友信息之后,我們就可以根據(jù)isExist這個(gè)布爾值來(lái)判斷了是否要添加數(shù)據(jù)了,添加數(shù)據(jù)過(guò)程如下代碼所示,這里我是獲取了好友的名片信息進(jìn)行的添加,前期的話可以直接添加JID.

if (!isExist) {
    //添加數(shù)據(jù)
    XMPPvCardTemp *vCard =  [[SDXmppManager defaulManager].vCardTempModule vCardTempForJID:jid shouldFetch:YES];
    
    SDContactModel *contact =[[SDContactModel alloc]init];
    contact.jid = jid;
    contact.vCard =vCard;
    contact.isAvailable = NO;
    
    [self.user.contactsArray addObject:contact];
   
}

上面就是從服務(wù)器獲取到好友數(shù)據(jù)的基本流程了.


好友數(shù)據(jù)本地整理代碼部分

當(dāng)我們獲取好友數(shù)據(jù)完成之后,我們并不是直接展現(xiàn)到頁(yè)面上,我們需要對(duì)好友數(shù)據(jù)進(jìn)行整理然后再展現(xiàn)到界面之上.我們通過(guò)-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender;這個(gè)代理方法來(lái)調(diào)取我們的好友數(shù)據(jù)整理方法-(void)networkingWithContactsArray;.

-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{

    [self networkingWithContactsArray];

}

SDChat的好友界面是類(lèi)似于微信的好友界面的,是分組展示的.所以,數(shù)據(jù)存儲(chǔ)的整體思路是,列表的數(shù)據(jù)源是存儲(chǔ)于一個(gè)字典當(dāng)中,我們把首字母相同的JID或者是用戶(hù)名存儲(chǔ)于一個(gè)數(shù)組當(dāng)中.每個(gè)key是每一個(gè)JID的首字母大寫(xiě)(或者是用戶(hù)名稱(chēng)的首字母大寫(xiě)).因?yàn)樽值涫菬o(wú)序的,那么如何做到有序的排列呢?我們需要建立另外一個(gè)數(shù)組作為排序數(shù)組,同時(shí)也起著索引數(shù)組的作用.我們把字典中所有的Key放入數(shù)組中,然后排序,數(shù)據(jù)提取過(guò)程中我們只需要根據(jù)數(shù)組的排列順序拿取即可.示意圖如下所示.

那么我們看一下實(shí)際代碼過(guò)程中,對(duì)于字典的數(shù)據(jù)添加整理部分,首先我們要初始化字典對(duì)象,然后我們遍歷好友節(jié)點(diǎn)數(shù)組([SDUser defaulUser].contactsArray),取出我們需要排序的每一個(gè)關(guān)鍵字符串(不管是JID還是用戶(hù)名).我們調(diào)用-(NSString *)transform:(NSString *)chinese這個(gè)方法回去首字母并且大寫(xiě).在這個(gè)方法中我們有幾種情況需要處理,一種是獲取字符串失敗,也就是說(shuō)傳入的是一個(gè)nil值,我們直接返回 "#" ,另外一種是如果首字母是數(shù)字,那么我們也是需要返回"#"的.所以這樣返回首字母所使用到的方法總共就有了三個(gè),兩個(gè)用來(lái)判斷是否是數(shù)組,一個(gè)則是截取并且進(jìn)行字母大寫(xiě)的操作.三個(gè)方法如下所示.

//截取首字母并且大寫(xiě)
-(NSString *)transform:(NSString *)chinese{
    
    if (chinese == nil ||[chinese isEqualToString:@""]) {
        
        return @"#";
        
    }
    
    NSMutableString *pinyin = [chinese mutableCopy];
    CFStringTransform((__bridge CFMutableStringRef)pinyin, NULL, kCFStringTransformMandarinLatin, NO);
    CFStringTransform((__bridge CFMutableStringRef)pinyin, NULL, kCFStringTransformStripCombiningMarks, NO);
    
    NSString *subString = [[pinyin uppercaseString] substringWithRange:NSMakeRange(0, 1)];
    
    if ([self isPureInt:subString] || [self isPureFloat:subString]) {
        
        return  @"#";
    }
    
    return subString;
}

//判斷是否為整型:
- (BOOL)isPureInt:(NSString*)string{
    NSScanner* scan = [NSScanner scannerWithString:string];
    int val;
    return [scan scanInt:&val] && [scan isAtEnd];
}
//判斷是否為浮點(diǎn)型:
- (BOOL)isPureFloat:(NSString*)string{
    NSScanner* scan = [NSScanner scannerWithString:string];
    float val;
    return[scan scanFloat:&val] && [scan isAtEnd];
}

那么通過(guò)返回首字母,我們需要判斷一下當(dāng)前的字典對(duì)象中是否已經(jīng)存在了改分組的數(shù)組,如果存在,那么直接存儲(chǔ),如果不存在,那么初始化一個(gè)數(shù)組之后,以首字母為Key,空數(shù)組為Value保存到字典中,然后再把數(shù)據(jù)存儲(chǔ)到數(shù)組中去.具體代碼如下所示.

if (self.contactsPinyinDic[firstWord] ==nil) {
    
    //如果聯(lián)系人字典數(shù)組中沒(méi)有該分組,那么就初始化一個(gè)分組數(shù)組,然后存儲(chǔ).
    NSMutableArray *sectionArray =[NSMutableArray  arrayWithCapacity:16];
    
    [sectionArray addObject:contact];
    
    [self.contactsPinyinDic setValue:sectionArray forKey:firstWord];
    
}else{
    
    NSMutableArray *sectionArray =self.contactsPinyinDic[firstWord];
    
    [sectionArray addObject:contact];
    
}

添加完成之后,我們就需要對(duì)索引數(shù)組進(jìn)行操作了,首先我們還是先初始化我們的索引數(shù)組,初始化的過(guò)程中,我們就把所有的key值添加到我們的數(shù)組當(dāng)中去.然后我們需要添加一個(gè)空字符串@"",這是為了給"新的朋友"那個(gè)分組做準(zhǔn)備的.代碼如下所示.

self.indexArray = [NSMutableArray arrayWithArray:self.contactsPinyinDic.allKeys];
[self.indexArray addObject:@""];//添加一個(gè)空的字符串,用于菜單分組

然后,我們對(duì)索引數(shù)組進(jìn)行遍歷排序操作.

    for (int i = 0; i<self.indexArray.count; i++) {
        
        for (int j = 0; j<i; j++) {
            
            if (self.indexArray[j]>self.indexArray[i]) {
                
                NSString *objString = self.indexArray[j];
                self.indexArray[j] =  self.indexArray[i];
                self.indexArray[i] = objString;
                
            }
        }
    }

如果存在"#",為了界面的美觀,我們把"#"放在索引數(shù)組的最后一位,然后,我們就刷新我們的頁(yè)面即可.

//索引數(shù)組移動(dòng)#號(hào)到最后.
if ([self isIncludeWithJing]) {
    
    [self.indexArray removeObject:@"#"];
    
    [self.indexArray addObject:@"#"];
}

[self.contactsList reloadData];

這樣在tableView的數(shù)據(jù)源方法中,分組個(gè)數(shù)為索引數(shù)組的元素個(gè)數(shù)self.indexArray.count;每一個(gè)section中元素的個(gè)數(shù)(除了一個(gè)分組)都是為字典中對(duì)應(yīng)的每一個(gè)value數(shù)組的個(gè)數(shù).

然后,我們就可以做出開(kāi)始的界面的樣子來(lái)了.當(dāng)然了,這樣的畫(huà)面需要我們做很多工作的,也是我們下一篇博客所要說(shuō)到的,電子名片的實(shí)現(xiàn).


結(jié)束


SDChat中的好友獲取的邏輯和代理方法就說(shuō)到這里了,這里我要先聲明一下,SDChat中可能還存在著B(niǎo)ug,如果有任何問(wèn)題,歡迎聯(lián)系騷棟,謝謝.接下來(lái)的一篇我覺(jué)得應(yīng)該先把XMPPFramework電子名片的實(shí)現(xiàn)說(shuō)一下,XMPPFramework我覺(jué)得最坑的就是添加好友這一塊了,邏輯比較多,準(zhǔn)備在第五篇中進(jìn)行講解說(shuō)明.希望大家持續(xù)關(guān)注~最后把SDChat的傳送門(mén)送給大家.大家可以對(duì)照著Demo來(lái)看本篇博客.

-->SDChat傳送門(mén)??


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 前面幾篇文章我們主要搞了搞關(guān)于好友列表的相關(guān)技術(shù)以及邏輯,還有用戶(hù)上下線監(jiān)控這個(gè)沒(méi)說(shuō),我準(zhǔn)備放到最后再說(shuō),比...
    神經(jīng)騷棟閱讀 3,354評(píng)論 9 12
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 前言 上一篇博客中我們說(shuō)到如何通過(guò)XMPPFramework中的代理方法來(lái)獲取到好友節(jié)點(diǎn)數(shù)據(jù)信息,但是我們發(fā)現(xiàn)節(jié)點(diǎn)...
    神經(jīng)騷棟閱讀 3,663評(píng)論 6 10
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,582評(píng)論 30 472
  • 我媽很笨,不會(huì)養(yǎng)花。這是我家公認(rèn)的事實(shí),除此之外我媽還不會(huì)養(yǎng)孩子,這是她自己講的。但我否定。 一 我是個(gè)內(nèi)向的人,...
    daladel閱讀 524評(píng)論 0 0

友情鏈接更多精彩內(nèi)容