優(yōu)秀的代碼是它自己最好的文檔。當(dāng)你考慮要添加一個(gè)注釋時(shí),問問自己,“如何能改進(jìn)這段代碼,以讓它不需要注釋?”*
XMPP簡(jiǎn)介
XMPP是一種基于標(biāo)準(zhǔn)通用標(biāo)記語言的子集XML的協(xié)議,它繼承了在XML環(huán)境中靈活的發(fā)展性。因此,基于XMPP的應(yīng)用具有超強(qiáng)的可擴(kuò)展性。經(jīng)過擴(kuò)展以后的XMPP可以通過發(fā)送擴(kuò)展的信息來處理用戶的需求,以及在XMPP的頂端建立如內(nèi)容發(fā)布系統(tǒng)和基于地址的服務(wù)等應(yīng)用程序。而且,XMPP包含了針對(duì)服務(wù)器端的軟件協(xié)議,使之能與另一個(gè)進(jìn)行通話,這使得開發(fā)者更容易建立客戶應(yīng)用程序或給一個(gè)配好系統(tǒng)添加功能。

XMPP協(xié)議的實(shí)現(xiàn)原理過程
當(dāng)我們知道XMPP是一種協(xié)議的時(shí)候,我們?nèi)绾瓮ㄟ^objective-c 代碼實(shí)現(xiàn)XMPP協(xié)議,進(jìn)而實(shí)現(xiàn)我們的即時(shí)通訊功能呢? 下面的圖片就是為我們做了很好的解釋.

XMPP協(xié)議的代碼實(shí)現(xiàn)
1.準(zhǔn)備工作
我們做的客戶端也服務(wù)器通訊通道的實(shí)現(xiàn)以及數(shù)據(jù)交流,那么首先要有我們自己的服務(wù)器,當(dāng)然了,在公司的好說一些,如果是個(gè)人研究技術(shù)怎么辦呢?我們可以自己搭建一個(gè)服務(wù)器或者使用leancloud這種第三方服務(wù)器,這里我給大家提供一些搭建服務(wù)器的工具,當(dāng)然了,自己搭建的服務(wù)器生命比較脆弱,請(qǐng)大家好好愛護(hù)~還有就是leancloud也是我推薦的一種方法.
----->點(diǎn)擊前往LeanCloud官方網(wǎng)站
----->XMPP本地服務(wù)器搭建工具下載
2.OC搭建Client和連接通道
工程完成目標(biāo):
1.創(chuàng)建通訊通道并完成賬號(hào)密碼的登錄
2.創(chuàng)建通道并完成賬號(hào)的申請(qǐng)
3.好友列表的獲取和顯示
4.即時(shí)通訊功能的實(shí)現(xiàn)
首先,我們需要導(dǎo)入我們的所需要導(dǎo)入的XMPPFramework(PS:點(diǎn)擊打開下載,完成之后直接解壓拖到工程中??),還有手動(dòng)的導(dǎo)入兩個(gè)庫(kù).如下.
libxml2.tbd
libresolv.tbd
然后,我們就要配置我們的build setting頁面的設(shè)置 search paths 選添加一個(gè)字段,添加如下
/usr/include/libxml2

完成上面的設(shè)置之后 我們需要?jiǎng)?chuàng)建一個(gè)單例類XMPPManager,用它來創(chuàng)建通訊通道實(shí)現(xiàn)上面的四個(gè)功能.
XMPPManager.h中如下
#import <Foundation/Foundation.h>
#import "XMPPFramework.h"
@interface XMPPManager : NSObject
//通訊管道
@property(nonatomic,strong)XMPPStream *stream;
//和通訊錄對(duì)象很像,用來管理好友類~
@property(nonatomic,strong)XMPPRoster *roster;
//XMPP聊天消息本地化處理對(duì)象
@property(nonatomic,strong)XMPPMessageArchiving *messageArchiving;
//消息上下文對(duì)象
@property(nonatomic,strong)NSManagedObjectContext *messageContext;
+(instancetype)defaulManager;
-(void)LoginWithUserName:(NSString *)name AndPassWord:(NSString *)password;
-(void)RegiserWithUserName:(NSString *)name AndPassWord:(NSString *)password;
//與服務(wù)器斷開鏈接
-(void)disconnectWithServer;
@end
XMPPManager.h文件的解釋:
stream : 這是C和S之間的通訊通道.
roster : 這個(gè)屬性管理好友列表的一個(gè)屬性.
messageArchiving : 這個(gè)屬性用來管理本地聊天記錄的一個(gè)類
messageContext : 消息上下文對(duì)象.
+(instancetype)defaulManager:創(chuàng)建單例的方法
-(void)LoginWithUserName:(NSString *)name AndPassWord:(NSString *)password : 登錄的方法
-(void)RegiserWithUserName:(NSString *)name AndPassWord:(NSString *)password : 注冊(cè)新賬號(hào)的方法
-(void)disconnectWithServer; 與服務(wù)器斷開鏈接
了解完各個(gè)方法之后,我們就要在XMPPManager.m實(shí)現(xiàn)一下,實(shí)現(xiàn)如下
#import "XMPPManager.h"
//代表與服務(wù)器進(jìn)行連接的類型.
typedef enum : NSUInteger {
DoLgin,
DORegiser,
} ConnetType;
@interface XMPPManager()<XMPPStreamDelegate,XMPPRosterDelegate,XMPPMessageArchivingStorage>
@property(nonatomic,strong)NSString *password;
@property(nonatomic,strong)NSString *regiserPassword;
//聲明一個(gè)屬性 記錄連接的類型
@property(nonatomic,assign)ConnetType type;
@end
@implementation XMPPManager
static XMPPManager *manager;
+(instancetype)defaulManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[XMPPManager alloc]init];
});
return manager;
}
-(instancetype)init{
if (self = [super init]) {
self.stream = [[XMPPStream alloc]init];
self.stream.hostName = kHostName;
self.stream.hostPort = kHostPort;
//設(shè)置stream的代理
[self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//下面這一堆其實(shí)是對(duì)roster對(duì)象進(jìn)行初始化.
//系統(tǒng)寫好的XMPP存儲(chǔ)對(duì)象
XMPPRosterCoreDataStorage *dataStorage = [XMPPRosterCoreDataStorage sharedInstance];
self.roster = [[XMPPRoster alloc]initWithRosterStorage:dataStorage dispatchQueue:dispatch_get_global_queue(0, 0)];
//激活roster
[self.roster activate:self.stream];
//給roster對(duì)象指定代理
[self.roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
//初始化聊天記錄管理對(duì)象
XMPPMessageArchivingCoreDataStorage *messageArchivingCoreDataStorage= [XMPPMessageArchivingCoreDataStorage sharedInstance];
self.messageArchiving = [[XMPPMessageArchiving alloc]initWithMessageArchivingStorage:messageArchivingCoreDataStorage dispatchQueue:dispatch_get_main_queue()];
//激活管理對(duì)象
[self.messageArchiving activate:self.stream];
//設(shè)置管理對(duì)象代理
[self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
self.messageContext = messageArchivingCoreDataStorage.mainThreadManagedObjectContext;
}
return self;
}
//與服務(wù)器的建立鏈接
-(void)connectToServerWintUser:(NSString *)name{
if ([self.stream isConnected]) {
[self.stream disconnect];
}
//jid jabberID,是基于jabber協(xié)議的由用戶名生成的唯一ID
self.stream.myJID = [XMPPJID jidWithUser:name domain:kDomin resource:kResource];
NSError *error = nil;
//與服務(wù)器建立鏈接.
[self.stream connectWithTimeout:30.0f error:&error];
if (error != nil) {
@throw [NSException exceptionWithName:@"CQ_Error" reason:@"與服務(wù)器建立連接失敗,請(qǐng)查看代碼" userInfo:nil];
}
}
//與服務(wù)器斷開鏈接
-(void)disconnectWithServer{
[self.stream disconnect];
}
-(void)LoginWithUserName:(NSString *)name AndPassWord:(NSString *)password{
self.password = password;
self.type = DoLgin;
[self connectToServerWintUser:name];
}
//與服務(wù)器建立連接
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
NSLog(@"與服務(wù)器建立鏈接正常");
//與服務(wù)器進(jìn)行登錄認(rèn)證
NSError *error = nil;
switch (self.type) {
case DoLgin:
[self.stream authenticateWithPassword:self.password error:&error];
if (error != nil) {
NSLog(@"認(rèn)證過程出錯(cuò)!");
}
break;
case DORegiser:
[self.stream registerWithPassword:self.regiserPassword error:&error];
if (error != nil) {
NSLog(@"注冊(cè)過程出錯(cuò)!");
}
break;
default:
break;
}
}
-(void)xmppStreamConnectDidTimeout:(XMPPStream *)sender{
@throw [NSException exceptionWithName:@"CQ_Error" reason:@"與服務(wù)器建立連接超時(shí),請(qǐng)查看代碼" userInfo:nil];
}
-(void)RegiserWithUserName:(NSString *)name AndPassWord:(NSString *)password{
self.type = DORegiser;
self.regiserPassword = password;
[self connectToServerWintUser:name];
}
看完了上面的代碼,連我自己都覺得亂亂的,所以 我們一個(gè)功能一個(gè)功能看這些代碼的實(shí)現(xiàn)原理,
登錄功能
我們想要登錄我們的服務(wù)器,首先要有我們的賬號(hào)和密碼,然后我們就需要建立通道
(a) defaulManager
創(chuàng)建單例這個(gè)方法中就是創(chuàng)建了我們的單例.
(b) init
初始化這個(gè)方法中我們需要對(duì)我們的通訊管道屬性stream進(jìn)行初始化一下,設(shè)置stream服務(wù)器IP地址和服務(wù)器端口,還有就是設(shè)置stream的代理對(duì)象.實(shí)現(xiàn)XMPPStreamDelegate協(xié)議方法.這里設(shè)置代理對(duì)象的方法不同于以前,這里是使用runtime設(shè)計(jì)模式可以為stream設(shè)置多個(gè)代理對(duì)象.
(c) LoginWithUserName:(NSString )name AndPassWord:(NSString )password
這個(gè)方法中首先我們需要保存我們的密碼,用于傳值到下一個(gè)方法中.self.type = DoLgin;這句代碼有作何解釋呢?因?yàn)椴还苁堑卿浐妥?cè),我們都要與我們的服務(wù)器創(chuàng)建聯(lián)系,那么服務(wù)器是如何知道我們是創(chuàng)建的什么聯(lián)系的呢?就是通過這句代碼實(shí)現(xiàn)的,當(dāng)然了,現(xiàn)在你可能聽得糊涂,當(dāng)看到下面的方法的時(shí)候你就明白了.[self connectToServerWintUser:name];這句代碼就是要?jiǎng)?chuàng)建于服務(wù)器之間的聯(lián)系.這時(shí)候,賬號(hào)name就通過參數(shù)的形式傳到了connectToServerWintUser這個(gè)函數(shù),而password通過屬性的傳值到xmppStreamDidConnect(當(dāng)完成通道的建立的時(shí)候執(zhí)行的代理方法).
(d) connectToServerWintUser
[self.stream disconnect];這句代碼就是讓客戶端斷開連接通道,綜合上面來看,當(dāng)我們已經(jīng)存在的連接的通道的時(shí)候,我們就會(huì)讓通道斷開,這是為什么呢?因?yàn)镃與S之間的通道只能創(chuàng)建一條,當(dāng)我們重復(fù)創(chuàng)建的時(shí)候,就會(huì)導(dǎo)致我們的程序崩潰.所以我們要保障我們的通道是只有一條的. [self.stream connectWithTimeout:30.0f error:&error];這句代碼就是我們創(chuàng)建通道,當(dāng)然了等待時(shí)間是30秒.
(e)xmppStreamDidConnect
這是一個(gè)代理的方法,當(dāng)我們完成通道的創(chuàng)建之后,我們就會(huì)調(diào)用這個(gè)方法,在這個(gè)代理方法中我們需要做的就是對(duì)我們的密碼進(jìn)行驗(yàn)證.[self.stream authenticateWithPassword:self.password error:&error];這就是登錄驗(yàn)證我們的密碼.驗(yàn)證成功之后就會(huì)調(diào)用-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender這個(gè)代理方法,驗(yàn)證失敗就會(huì)調(diào)用-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error 這個(gè)代理方法,當(dāng)然了,這兩個(gè)方法是寫在我們的登錄頁面的,因?yàn)槲覀冃枰獙?duì)使用者有個(gè)用戶的交互不是?比如彈出一個(gè)彈窗.提醒一下用戶.xmppStreamDidConnect不管是注冊(cè)和登錄都會(huì)調(diào)用,我們?cè)趺磪^(qū)分呢?我們現(xiàn)在.m文件的頂部設(shè)置了一個(gè)枚舉值,通過枚舉值的值判斷我們所需要的操作.
(f)xmppStreamConnectDidTimeout
這個(gè)方法就是說,當(dāng)我們與服務(wù)器建立連接超時(shí)的時(shí)候會(huì)進(jìn)行的操作.@throw [NSException exceptionWithName:@"CQ_Error" reason:@"與服務(wù)器建立連接超時(shí),請(qǐng)查看代碼" userInfo:nil];是我們手動(dòng)的拋出一個(gè)異常.
注冊(cè)功能
注冊(cè)功能與登錄功能在實(shí)現(xiàn)上是相似的,下面的屬性就是區(qū)別的開始.
@property(nonatomic,strong)NSString *password;
@property(nonatomic,strong)NSString *regiserPassword;
xmppStreamDidConnect
在這個(gè)方法中,我們需要對(duì)我們的注冊(cè)方法與登錄方法分別開來. [self.stream registerWithPassword:self.regiserPassword error:&error];這個(gè)方法就是我們把注冊(cè)的密碼傳到服務(wù)器上保存的方法.當(dāng)然了,當(dāng)我們注冊(cè)成功的時(shí)候,就會(huì)調(diào)動(dòng)-(void)xmppStreamDidRegister:(XMPPStream *)sender這個(gè)協(xié)議方法,當(dāng)注冊(cè)不成功的時(shí)候,我們就會(huì)調(diào)用-(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error這個(gè)方法.這里我們也要拍給我們的用戶一些交互,讓我們用戶知道自己注冊(cè)的結(jié)果.
好友列表功能
在XMPPManager.m文件中,我們需要做的就是在 *** init *** 方法中對(duì)管理好友列表的roster對(duì)象進(jìn)行一下初始化并且制定代理,這里需要注意一個(gè)地方,當(dāng)我們?cè)O(shè)置roster的初始化的時(shí)候,我們需要使用 *** dispatch_get_global_queue(0, 0) ***全局線程,不能使用主線程,原因是如果使用主線程會(huì)出現(xiàn)一些莫名其妙的Bug. [self.roster activate:self.stream]; 激活roster的意思就是給roster可以通過stream通道的權(quán)限..(PS:大白話 ??)
在我們的好友列表頁面中,首先我們需要確定他是一個(gè)UITableViewController,然后我們需要從我們的服務(wù)器拿到我們的好友的列表數(shù)組.通道stream連接在我們的登錄的時(shí)候已經(jīng)完成了,所以我們不需要再管理通道了,我們需要在好友列表的控制器中實(shí)現(xiàn)XMPPRosterDelegate的代理方法來獲取到我們的好友列表.代碼如下
在MainTableViewController.m中
#import "MainTableViewController.h"
#import "XMPPManager.h"
#import "ChatTableViewController.h"
@interface MainTableViewController ()<XMPPRosterDelegate>
//用來存儲(chǔ)所有的好友信息的.
@property(nonatomic,strong)NSMutableArray *dataArray;
@end
@implementation MainTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.dataArray = [NSMutableArray array];
[[XMPPManager defaulManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
//roster代理方法
//開始獲取好友列表的時(shí)候
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{
NSLog(@"開始獲取好友列表");
}
//結(jié)束獲取好友列表
-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{
NSLog(@"獲取好友列表完成的時(shí)候.");
}
//獲取好友信息的時(shí)候
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{
// //將每一個(gè)好友存儲(chǔ)下來.
// NSLog(@"%@",[item children]);
//
// NSLog(@"%@",[item name]);
NSString *SJid = [[item attributeForName:@"jid"] stringValue];
//把字符串類型的JID轉(zhuǎn)換成XMPPJID
XMPPJID *jid = [XMPPJID jidWithString:SJid];
//把JID存儲(chǔ)到數(shù)組中,相當(dāng)修改數(shù)據(jù)源
[self.dataArray addObject:jid];
//更新UI
NSIndexPath *path = [NSIndexPath indexPathForRow:self.dataArray.count-1 inSection:0];
[self.tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MainCell" forIndexPath:indexPath];
//把字符串類型的JID轉(zhuǎn)換成XMPPJID
XMPPJID *jid = self.dataArray[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"%@ %@ %@",jid.user,jid.resource,jid.domain];
return cell;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
UITableViewCell *cell = (UITableViewCell *)sender;
//拿到下一步要跳轉(zhuǎn)的controller對(duì)象
ChatTableViewController *chatVC = segue.destinationViewController;
//判斷選中的cell在當(dāng)前的Table中的位置
NSIndexPath *path = [self.tableView indexPathForCell:cell];
chatVC.chatToJID = self.dataArray[path.row];
}
@end
方法解釋
在上面的代碼中我們要解釋的只有兩個(gè)方法 ,一個(gè)是獲取好友信息的時(shí)候調(diào)用的
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
另外一個(gè)是我們點(diǎn)擊好友進(jìn)入聊天頁面所需要的方法.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
這個(gè)是XMPPRosterDelegate協(xié)議中代理方法,如果我們有很多的好友,這個(gè)方法會(huì)調(diào)用很多次,知道我們的好友全部遍歷完. NSString *SJid = [[item attributeForName:@"jid"] stringValue]; 和XMPPJID *jid = [XMPPJID jidWithString:SJid];這兩個(gè)方法就是當(dāng)我們從服務(wù)器接到我們的數(shù)據(jù)時(shí)候,我們要先將他轉(zhuǎn)化成一下,轉(zhuǎn)成成我們所需要的數(shù)據(jù),然后存入我們的數(shù)據(jù)源數(shù)組中.更新UI 使用的方法是[self.tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom]; 為什么使用這個(gè)方法呢?為什么不使用reloadData這個(gè)方法?因?yàn)檫@個(gè)代理方法會(huì)執(zhí)行很多次,我們?yōu)榱吮苊鉀]有必要的內(nèi)存負(fù)擔(dān),所以我們只需要更新一下我們最后一條數(shù)據(jù)就行,這樣大大減少了內(nèi)存的負(fù)擔(dān),提高了我們的工程效率.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
這個(gè)方法是因?yàn)槲沂褂胹toryboard的原因,我們?yōu)榱四苷_找到我們點(diǎn)擊對(duì)應(yīng)的cell所使用的方法.傳值的時(shí)候我們需要把JID傳到聊天界面,這樣服務(wù)器就會(huì)清楚的知道我們是對(duì)誰進(jìn)行聊天了.
聊天界面功能
對(duì)于聊天界面的搭建,我們也是使用到UITableViewController,邏輯是我們需要往服務(wù)器發(fā)送我們的消息,然后服務(wù)器在通過JID發(fā)送到指定的消息,發(fā)送者發(fā)送消息的時(shí)候和接收者接收到消息的時(shí)候刷新我們的UI.
那我們就先看看在XMPPManager的類中我們需要做一些什么事情吧.
在XMPPManager.h中 我們創(chuàng)建了兩個(gè)屬性,一個(gè)是XMPP聊天消息本地化處理對(duì)象的messageArchiving,另外一個(gè)是消息上下文對(duì)象messageContext.
在XMPPManager.m中 的init方法中,我們對(duì)這兩個(gè)屬性進(jìn)行了初始化.如下
//初始化聊天記錄管理對(duì)象
XMPPMessageArchivingCoreDataStorage *messageArchivingCoreDataStorage= [XMPPMessageArchivingCoreDataStorage sharedInstance];
self.messageArchiving = [[XMPPMessageArchiving alloc]initWithMessageArchivingStorage:messageArchivingCoreDataStorage dispatchQueue:dispatch_get_main_queue()];
//激活管理對(duì)象
[self.messageArchiving activate:self.stream];
//設(shè)置管理對(duì)象代理
[self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
self.messageContext = messageArchivingCoreDataStorage.mainThreadManagedObjectContext;
messageArchiving對(duì)象也是需要我們激活通道權(quán)限的.然后設(shè)置了代理.
在ChatTableViewController.h界面 我們需要設(shè)置一個(gè)XMPPJID對(duì)象 來接受好友列表傳來的JID值.代碼如下.
#import <UIKit/UIKit.h>
#import "XMPPManager.h"
@interface ChatTableViewController : UITableViewController
//接收好友列表傳來的JID
@property(nonatomic,strong)XMPPJID *chatToJID;
@end
在ChatTableViewController.m文件中,我們需要做的就是實(shí)現(xiàn)XMPPStreamDelegate的代理方法,從代理方法中實(shí)現(xiàn)往服務(wù)器發(fā)送數(shù)據(jù)和從服務(wù)器接收數(shù)據(jù)的操作.代碼如下
#import "ChatTableViewController.h"
@interface ChatTableViewController ()<XMPPStreamDelegate>
//存放所有的消息
@property(nonatomic,strong)NSMutableArray *messageArray;
@end
@implementation ChatTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.messageArray = [NSMutableArray array];
//添加代理
[[XMPPManager defaulManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//更新聊天記錄信息
[self reloadMessage];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
//展現(xiàn)聊天記錄
-(void)reloadMessage{
NSManagedObjectContext *context = [XMPPManager defaulManager].messageContext;
#pragma mark----直接一個(gè) fet 下面全都出來了----
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//這里面要填的是XMPPARChiver的coreData實(shí)例類型
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Specify criteria for filtering which objects to fetch
//對(duì)取到的數(shù)據(jù)進(jìn)行過濾,傳入過濾條件.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@", [XMPPManager defaulManager].stream.myJID.bare,self.chatToJID.bare];
[fetchRequest setPredicate:predicate];
// Specify how the fetched objects should be sorted
//設(shè)置排序的關(guān)鍵字
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"
ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
//完成之后干什么?
NSLog(@"和此人的激情交談");
}
/********獲取和這個(gè)人所有的聊天記錄***************/
//清空聊天數(shù)組中的消息
[self.messageArray removeAllObjects];
//將新的聊天記錄添加到數(shù)組中
self.messageArray = [NSMutableArray arrayWithArray:fetchedObjects];
NSLog(@"%ld",self.messageArray.count);
//刷新UI
[self.tableView reloadData];
//將tableview直接滑動(dòng)到最底部
NSIndexPath * indexpath = [NSIndexPath indexPathForRow: self.messageArray.count-1 inSection:0];
if (indexpath.row > 0) {
[self.tableView selectRowAtIndexPath:indexpath animated:YES scrollPosition:UITableViewScrollPositionBottom];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.messageArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ChatCell" forIndexPath:indexPath];
//取到我們對(duì)應(yīng)的信息
XMPPMessageArchiving_Message_CoreDataObject *message = self.messageArray[indexPath.row];
if (message.isOutgoing == YES) {
cell.detailTextLabel.text = message.body;
cell.textLabel.text = @"";
}else {
cell.textLabel.text = message.body;
cell.detailTextLabel.text = @"";
}
return cell;
}
//發(fā)送消息
- (IBAction)sendAction:(id)sender {
XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJID];
[message addBody:@"nice to meet you"];
//發(fā)送消息
[[XMPPManager defaulManager].stream sendElement:message];
}
-(void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{
// tipWithMessage(@"消息發(fā)送成功!");
[self reloadMessage];
}
-(void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error{
tipWithMessage(@"消息發(fā)送失敗");
}
-(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
[self reloadMessage];
}
@end
頁面的邏輯:
當(dāng)我們進(jìn)入聊天界面的時(shí)候,我們會(huì)先調(diào)用reloadMessage這個(gè)方法,從服務(wù)器下載當(dāng)前用戶與這個(gè)JID的聊天記錄,當(dāng)我們點(diǎn)擊發(fā)送消息的時(shí)候,我們就會(huì)調(diào)用sendAction這個(gè)方法,發(fā)送到服務(wù)器上去,當(dāng)我們發(fā)送成功后調(diào)用didSendMessage這個(gè)協(xié)議方法,然后在這協(xié)議方法中調(diào)用reloadMessage這個(gè)方法重新從服務(wù)器下載新的聊天記錄并且刷新UI,然后服務(wù)器上有當(dāng)前用戶新的消息的時(shí)候,就會(huì)調(diào)用didReceiveMessage這個(gè)代理方法,我們只需要在這個(gè)方法中再次調(diào)用reloadMessage方法就可.(PS:因?yàn)槲矣玫氖莝toryboard 所有有些地方會(huì)有所不同??)
方法解釋:
-(void)reloadMessage
這個(gè)方法是整個(gè)聊天界面的核心.我們?cè)谶@里面做的就是從服務(wù)器下載我們的數(shù)據(jù).當(dāng)我們下載完數(shù)據(jù)的時(shí)候,就要把我們數(shù)據(jù)源數(shù)組中所有的元素清空,然后將所有從服務(wù)器中下載的元素添加到我們的數(shù)組中.然后刷新我們的UI,當(dāng)我一刷新之后tableView就會(huì)重頭開始了,所以我們?cè)O(shè)置讓最后一個(gè)cell顯示在屏幕上.還有就是下面的一段代碼,這是我們先前寫好的,我們需要敲出 *** fetch *** 就會(huì)給我們提示,是不是很簡(jiǎn)答??? 這段代碼就是從網(wǎng)上下載我們所需要的數(shù)據(jù).當(dāng)然了,我們需要使用謂詞對(duì)這些數(shù)據(jù)進(jìn)行過濾.過濾后的數(shù)據(jù)才是我們所需要的數(shù)據(jù).
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//這里面要填的是XMPPARChiver的coreData實(shí)例類型
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Specify criteria for filtering which objects to fetch
//對(duì)取到的數(shù)據(jù)進(jìn)行過濾,傳入過濾條件.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@", [XMPPManager defaulManager].stream.myJID.bare,self.chatToJID.bare];
[fetchRequest setPredicate:predicate];
// Specify how the fetched objects should be sorted
//設(shè)置排序的關(guān)鍵字
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"
ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
//完成之后干什么?
NSLog(@"和此人的激情交談");
}
XMPP協(xié)議實(shí)現(xiàn)即時(shí)通訊的過程大體就是這樣了,當(dāng)然了,我還有一些功能沒有完善,比如添加好友的功能.這將在后期進(jìn)行再次的完善.謝謝大家的查看..
--->XMPP的Demo資源下載
在Demo拿到手的時(shí)候,我們首先要對(duì)我們的服務(wù)器IP進(jìn)行設(shè)置.在XMPPConfig.h文件中設(shè)置服務(wù)器相關(guān)信息!!!
