一、導(dǎo)入XMPP框架
-
下載
XMPPFramework
框架GitHub: XMPPFramework
-
導(dǎo)入依賴框架
CocoaLumberjack
: 日志框架CocoaAsyncSocket
: 底層網(wǎng)絡(luò)框架
需要添加CFNetwork
&Security
框架依賴(XCode 6+ 無需導(dǎo)入)KissXML
: XML解析框架
需要添加libxml2.dylib
框架依賴
需要指定如下編譯選項(xiàng):
Other Linker Flags = -lxml2
Header Search Paths = /usr/include/libxml2libidn
-
導(dǎo)入一下文件夾
Authentication
Categories
Core
Utilities
- 添加依賴:
libresolv.dylib
-
導(dǎo)入XMPP擴(kuò)展框架
-
Extensions
文件夾 - 導(dǎo)入
Sample_XMPPFramework.h
并更名為:XMPPFramework.h
- 添加PCH文件:
-
Add New File
->Other
->PCH文件
- 添加
#import<UIKit/UIKit.h>
到PCH文件中 - 設(shè)置編譯選項(xiàng),
Build Settings
->Precompile prefix Header
選擇Yes
- 設(shè)置編譯選項(xiàng),
Build Settings
->Prefix Header
設(shè)置PCH文件名:“項(xiàng)目名/“PCH文件名”
-
二、登錄 & 注銷
實(shí)現(xiàn)用戶登錄的步驟如下:
1. 實(shí)例化XMPPStream并設(shè)置代理,同時(shí)添加代理到工作隊(duì)列
2. 使用JID連接至服務(wù)器,默認(rèn)端口為5222,JID字符串中需要包含服務(wù)器的域名
3. 在完成連接的代理方法中驗(yàn)證用戶密碼,連接完成后XMPPStream的isConnect屬性為YES
4. 在驗(yàn)證代理方法中判斷用戶是否登錄成功
5. 上線或者下線成功后,向服務(wù)器發(fā)送Presence數(shù)據(jù),以更新用戶在服務(wù)器的狀態(tài)
各部分的實(shí)現(xiàn)代碼如下:
-
初始化
XMPPStream
并設(shè)置代理:-(void)setupXMPPStream{ _xmppStream = [[XMPPStream alloc] init]; // 設(shè)置代理 [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; }
-
連接到服務(wù)器
-(void)connectToHost{ NSLog(@"開始連接到服務(wù)器"); if (!_xmppStream) { [self setupXMPPStream]; } // 設(shè)置登錄用戶JID //resource 標(biāo)識用戶登錄的客戶端 iphone android XMPPJID *myJID = [XMPPJID jidWithUser:@"aaa" domain:@"bourne-mbp.local" resource:@"iphone" ]; _xmppStream.myJID = myJID; // 設(shè)置服務(wù)器域名 _xmppStream.hostName = @"bourne-mbp.local";//不僅可以是域名,還可是IP地址 // 設(shè)置端口 如果服務(wù)器端口是5222,可以省略 _xmppStream.hostPort = 5222; // 連接 NSError *err = nil; if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){ NSLog(@"%@",err); } }
-
連接成功后發(fā)送密碼驗(yàn)證
-(void)sendPwdToHost{ NSLog(@"再發(fā)送密碼授權(quán)"); NSError *err = nil; [_xmppStream authenticateWithPassword:@"123456" error:&err]; if (err) { NSLog(@"%@",err); } }
-
授權(quán)成功后,發(fā)送
在線
消息#pragma mark 授權(quán)成功后,發(fā)送"在線" 消息 -(void)sendOnlineToHost{ NSLog(@"發(fā)送 在線 消息"); XMPPPresence *presence = [XMPPPresence presence]; NSLog(@"%@",presence); [_xmppStream sendElement:presence]; }
需要實(shí)現(xiàn)的幾個(gè)代理方法
#pragma mark 與主機(jī)連接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
NSLog(@"與主機(jī)連接成功");
// 主機(jī)連接成功后,發(fā)送密碼進(jìn)行授權(quán)
[self sendPwdToHost];
}
#pragma mark 與主機(jī)斷開連接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
// 如果有錯(cuò)誤,代表連接失敗
NSLog(@"與主機(jī)斷開連接 %@",error);
}
#pragma mark 授權(quán)成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
NSLog(@"授權(quán)成功");
[self sendOnlineToHost];
}
#pragma mark 授權(quán)失敗
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
NSLog(@"授權(quán)失敗 %@",error);
}
注銷登錄
- 發(fā)送
離線
信息 - 斷開連接
-(void)logout{
// 1." 發(fā)送 `離線` 消息"
XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppStream sendElement:offline];
// 2. 與服務(wù)器斷開連接
[_xmppStream disconnect];
}
三、注冊
與登錄一樣,首先發(fā)送帳號建立連接
連接成功后,發(fā)送注冊的密碼
注冊成功后,框架會通知代理
實(shí)現(xiàn)以下代理方法
- (void)xmppStreamDidRegister:(XMPPStream *)sender {
NSLog(@"注冊成功");
if (_resultBlock) {
_resultBlock(BWXMPPLoginResultSuccessed);
}
}
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
if (_resultBlock) {
_resultBlock(BWXMPPLoginResultFailure);
}
}
四、用戶信息
XMPP是面向模塊的,每一個(gè)大的動能都屬于某一個(gè)模塊,需要使用時(shí),就在頭文件中將其暴露出來(原本是被注釋了的)。
-
工作原理:
添加用戶信息模塊之后,
XMPPFramework框架
會自動從服務(wù)器獲取用戶信息,并使用CoreData
保存到本地的數(shù)據(jù)庫中,使用XMPPvCardTempModule
可以訪問數(shù)據(jù) -
在
XMPPFramework.h
中將以下的頭文件前面的注釋去掉:// 電子名片模塊 #import "XMPPvCardTempModule.h" #import "XMPPvCardCoreDataStorage.h" // 頭像模塊 #import "XMPPvCardAvatarModule.h"
-
初始化模塊
//添加電子名片模塊 _vCardStorage = [XMPPvCardCoreDataStorage sharedInstance]; _vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage]; //激活 [_vCard activate:_xmppStream]; //添加頭像模塊 _avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard]; [_avatar activate:_xmppStream];
-
應(yīng)用
//xmpp提供了一個(gè)方法,直接獲取個(gè)人信息 XMPPvCardTemp *myVCard =[WCXMPPTool sharedWCXMPPTool].vCard.myvCardTemp; // 設(shè)置頭像 if(myVCard.photo){ self.haedView.image = [UIImage imageWithData:myVCard.photo]; } // 設(shè)置昵稱 self.nicknameLabel.text = myVCard.nickname;
五、好友
與用戶信息模塊相似,添加相應(yīng)的好友花名冊模塊即可。
-
頭文件
// 花名冊模塊 #import "XMPPRoster.h" #import "XMPPRosterCoreDataStorage.h"
-
初始化
// 添加花名冊模塊【獲取好友列表】 _rosterStorage = [[XMPPRosterCoreDataStorage alloc] init]; _roster = [[XMPPRoster alloc] initWithRosterStorage:_rosterStorage]; [_roster activate:_xmppStream];
-
應(yīng)用
//使用CoreData獲取數(shù)據(jù) // 1.上下文【關(guān)聯(lián)到數(shù)據(jù)庫XMPPRoster.sqlite】 NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].rosterStorage.mainThreadManagedObjectContext; // 2.FetchRequest【查哪張表】 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"]; // 3.設(shè)置過濾和排序 // 過濾當(dāng)前登錄用戶的好友 NSString *jid = [WCUserInfo sharedWCUserInfo].jid; NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@",jid]; request.predicate = pre; //排序 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES]; request.sortDescriptors = @[sort]; // 4.執(zhí)行請求獲取數(shù)據(jù) _resultsContrl = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil]; _resultsContrl.delegate = self; NSError *err = nil; [_resultsContrl performFetch:&err]; if (err) { WCLog(@"%@",err); }
- 注意:使用
NSFetchedResultsController
并設(shè)置代理,如果數(shù)據(jù)庫的內(nèi)容發(fā)生了變化,這個(gè)類會自動通知代理,就可以設(shè)置界面的數(shù)據(jù),做到實(shí)時(shí)更新。
- 注意:使用
六、消息
-
頭文件
- 注意:這幾個(gè)頭文件沒在
XMPPFramework.h
文件中,需要自己添加
// 消息模塊 #import "XMPPMessageArchiving.h" #import "XMPPMessageArchivingCoreDataStorage.h"
- 注意:這幾個(gè)頭文件沒在
-
初始化
// 添加聊天模塊 _msgStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init]; _msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgStorage]; [_msgArchiving activate:_xmppStream];
-
應(yīng)用
// 上下文
NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext;
// 請求對象
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
// 過濾、排序
// 1.當(dāng)前登錄用戶的JID的消息
// 2.好友的Jid的消息
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfo sharedWCUserInfo].jid,self.friendJid.bare];
NSLog(@"%@",pre);
request.predicate = pre;
// 時(shí)間升序
NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES];
request.sortDescriptors = @[timeSort];
// 查詢
_resultsContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
NSError *err = nil;
// 代理
_resultsContr.delegate = self;
[_resultsContr performFetch:&err];
NSLog(@"%@",_resultsContr.fetchedObjects);
if (err) {
WCLog(@"%@",err);
}
```
七、文件傳送(圖片,音頻)
-
原理分析
- 使用
base64
將文件轉(zhuǎn)化為字符串,然后再通過XMPPFramework
傳輸。 - 先將文件上傳到服務(wù)器,再將文件網(wǎng)址通過
XMPPFramework
轉(zhuǎn)輸給好友,好友收到后再自行下載文件。
- 使用
-
難點(diǎn)解析
- 需要給
XMPPFramework
的``數(shù)據(jù)體添加一個(gè)信息類型字段。
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; //text 純文本 //image 圖片 [msg addAttributeWithName:@"bodyType" stringValue:bodyType]; // 設(shè)置內(nèi)容 [msg addBody:text]; NSLog(@"%@",msg); [[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];
- 根據(jù)消息類型解析消息
XMPPMessageArchiving_Message_CoreDataObject *msg = _resultsContr.fetchedObjects[indexPath.row]; // 判斷是圖片還是純文本 NSString *chatType = [msg.message attributeStringValueForName:@"bodyType"]; if ([chatType isEqualToString:@"image"]) { //下圖片顯示 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:msg.body] placeholderImage:[UIImage imageNamed:@"DefaultProfileHead_qq"]]; cell.textLabel.text = nil; } else if ([chatType isEqualToString:@"text"]){ //顯示消息 if ([msg.outgoing boolValue]) {//自己發(fā) cell.textLabel.text = msg.body; }else{//別人發(fā)的 cell.textLabel.text = msg.body; } cell.imageView.image = nil; }
- 需要給
聲明:
以上內(nèi)容屬于本人整理的筆記,如有錯(cuò)誤的地方希望能告訴我,大家共同進(jìn)步。
以上內(nèi)容有些段落或語句可能是本人從其他地方Copy而來,如有侵權(quán),請及時(shí)告訴我。