IOS開發,OC和Swift語言對照,XMPP通信的實現:
一、導入XMPP框架
-
下載
XMPPFramework
框架GitHub: XMPPFramework
-
導入依賴框架
-
CocoaLumberjack
: 日誌框架 -
CocoaAsyncSocket
: 底層網絡框架
需要添加CFNetwork
&Security
框架依賴(XCode 6+ 無需導入) -
KissXML
: XML解析框架
需要添加libxml2.dylib
框架依賴
需要指定如下編譯選項:
Other Linker Flags = -lxml2
Header Search Paths = /usr/include/libxml2 -
libidn
-
-
導入一下文件夾
Authentication
Categories
Core
Utilities
- 添加依賴:
libresolv.dylib
-
導入XMPP擴展框架
-
Extensions
文件夾 -
導入
Sample_XMPPFramework.h
並更名爲:XMPPFramework.h
- 添加PCH文件:
-
Add New File
->Other
->PCH文件
-
添加
#import<UIKit/UIKit.h>
到PCH文件中 -
設置編譯選項,
Build Settings
->Precompile prefix Header
選擇Yes
-
設置編譯選項,
Build Settings
->Prefix Header
設置PCH文件名:“項目名/“PCH文件名”
-
-
- 導入Swift-OC橋接頭文件(Swift)
- 添加文件
-
Add New File
-> Source -> HeaderFile
-
添加 #import<XMPP.h>到橋接頭文件中
- 設置編譯選項,Build Settings -> Objective-C Bridging Header 設置橋接頭文件名 "項目名/橋接頭文件名"
-
- 添加文件
二、登錄 & 註銷
實現用戶登錄的步驟如下:
1. 實例化XMPPStream並設置代理,同時添加代理到工作隊列
2. 使用JID連接至服務器,默認端口爲5222,JID字符串中需要包含服務器的域名
3. 在完成連接的代理方法中驗證用戶密碼,連接完成後XMPPStream的isConnect屬性爲YES
4. 在驗證代理方法中判斷用戶是否登錄成功
5. 上線或者下線成功後,向服務器發送Presence數據,以更新用戶在服務器的狀態
各部分的實現代碼如下:
-
初始化
XMPPStream
並設置代理:-(void)setupXMPPStream{ _xmppStream = [[XMPPStream alloc] init]; // 設置代理 [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; }
func buildStream(){
xs = XMPPStream()
xs?.addDelegate(self, delegateQueue: dispatch_get_main_queue())
}
-
連接到服務器
-(void)connectToHost{ NSLog(@"開始連接到服務器"); if (!_xmppStream) { [self setupXMPPStream]; } // 設置登錄用戶JID //resource 標識用戶登錄的客戶端 iphone android XMPPJID *myJID = [XMPPJID jidWithUser:@"aaa" domain:@"bourne-mbp.local" resource:@"iphone" ]; _xmppStream.myJID = myJID; // 設置服務器域名 _xmppStream.hostName = @"bourne-mbp.local";//不僅可以是域名,還可是IP地址 // 設置端口 如果服務器端口是5222,可以省略 _xmppStream.hostPort = 5222; // 連接 NSError *err = nil; if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){ NSLog(@"%@",err); } }
func connect() -> Bool{
buildStream()
//通道已連接
if (xs!.isConnected()){
return true
}
let userNmae = NSUserDefaults.standardUserDefaults().stringForKey(Constants.UserName)
let server = NSUserDefaults.standardUserDefaults().stringForKey(Constants.Server)
if let userName = userNmae,server = server{
me.name = userName
me.domain = Constants.Domain
//通道的用戶名
xs!.myJID = XMPPJID.jidWithString(me.fullName)
xs!.hostName = server
return xs!.connectWithTimeout(5000, error: nil)
}
return false
}
-
連接成功後發送密碼驗證
-(void)sendPwdToHost{ NSLog(@"再發送密碼授權"); NSError *err = nil; [_xmppStream authenticateWithPassword:@"123456" error:&err]; if (err) { NSLog(@"%@",err); } }
//連接成功
func xmppStreamDidConnect(sender: XMPPStream!) {
//驗證密碼
let password = NSUserDefaults.standardUserDefaults().stringForKey(Constants.Password)
xs!.authenticateWithPassword(password, error: nil)
}
-
授權成功後,發送
在線
消息#pragma mark 授權成功後,發送"在線" 消息 -(void)sendOnlineToHost{ NSLog(@"發送 在線 消息"); XMPPPresence *presence = [XMPPPresence presence]; NSLog(@"%@",presence); [_xmppStream sendElement:presence]; }
func xmppStreamDidAuthenticate(sender: XMPPStream!) {
//上線
goOnline()
me.presenceType = Constants.Available
statusDelegate?.meOn(me)
func goOnline(){
var available = XMPPPresence()
xs?.sendElement(available)
}
需要實現的幾個代理方法
#pragma mark 與主機連接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
NSLog(@"與主機連接成功");
// 主機連接成功後,發送密碼進行授權
[self sendPwdToHost];
}
#pragma mark 與主機斷開連接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
// 如果有錯誤,代表連接失敗
NSLog(@"與主機斷開連接 %@",error);
}
#pragma mark 授權成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
NSLog(@"授權成功");
[self sendOnlineToHost];
}
#pragma mark 授權失敗
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
NSLog(@"授權失敗 %@",error);
}
//連接成功
func xmppStreamDidConnect(sender: XMPPStream!) {
//驗證密碼
let password = NSUserDefaults.standardUserDefaults().stringForKey(Constants.Password)
xs!.authenticateWithPassword(password, error: nil)
}
//斷開連接
func xmppStreamDidDisconnect(sender: XMPPStream!, withError error: NSError!) {
me.presenceType = Constants.Unavailable
statusDelegate?.meOff(me)
}
//驗證成功
func xmppStreamDidAuthenticate(sender: XMPPStream!) {
//上線
goOnline()
me.presenceType = Constants.Available
statusDelegate?.meOn(me)
}
//驗證失敗
func xmppStream(sender: XMPPStream!, didNotAuthenticate error: DDXMLElement!) {
me.presenceType = Constants.Unavailable
NSUserDefaults.standardUserDefaults().setBool(false, forKey: Constants.AutoLogin)
let alert = UIAlertView(title: "警告", message: "賬號或密碼錯誤.", delegate: self, cancelButtonTitle: "確定")
alert.show()
}
註銷登錄
- 發送
離線
信息 - 斷開連接
-(void)logout{
// 1." 發送 `離線` 消息"
XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
[_xmppStream sendElement:offline];
// 2. 與服務器斷開連接
[_xmppStream disconnect];
}
func disConnect(){
if let xs = xs{
if xs.isConnected(){
goOffline()
xs.disconnect()
}
}
}
func goOffline(){
var unavailable = XMPPPresence(name: Constants.Unavailable)
xs?.sendElement(unavailable)
}
三、註冊
-
與登錄一樣,首先發送帳號建立連接
-
連接成功後,發送註冊的密碼
-
註冊成功後,框架會通知代理
實現以下代理方法
- (void)xmppStreamDidRegister:(XMPPStream *)sender {
NSLog(@"註冊成功");
if (_resultBlock) {
_resultBlock(BWXMPPLoginResultSuccessed);
}
}
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
if (_resultBlock) {
_resultBlock(BWXMPPLoginResultFailure);
}
}
四、用戶信息
XMPP是面向模塊的,每一個大的動能都屬於某一個模塊,需要使用時,就在頭文件中將其暴露出來(原本是被註釋了的)。
-
工作原理:
添加用戶信息模塊之後,
XMPPFramework框架
會自動從服務器獲取用戶信息,並使用CoreData
保存到本地的數據庫中,使用XMPPvCardTempModule
可以訪問數據 -
在
XMPPFramework.h
中將以下的頭文件前面的註釋去掉:// 電子名片模塊 #import "XMPPvCardTempModule.h" #import "XMPPvCardCoreDataStorage.h" // 頭像模塊 #import "XMPPvCardAvatarModule.h"
Swift中在橋接頭文件中添加以上引用。
-
初始化模塊
//添加電子名片模塊 _vCardStorage = [XMPPvCardCoreDataStorage sharedInstance]; _vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage]; //激活 [_vCard activate:_xmppStream]; //添加頭像模塊 _avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard]; [_avatar activate:_xmppStream];
-
應用
//xmpp提供了一個方法,直接獲取個人信息 XMPPvCardTemp *myVCard =[WCXMPPTool sharedWCXMPPTool].vCard.myvCardTemp; // 設置頭像 if(myVCard.photo){ self.haedView.image = [UIImage imageWithData:myVCard.photo]; } // 設置暱稱 self.nicknameLabel.text = myVCard.nickname;
五、好友
與用戶信息模塊相似,添加相應的好友花名冊模塊即可。
-
頭文件
// 花名冊模塊 #import "XMPPRoster.h" #import "XMPPRosterCoreDataStorage.h"
-
初始化
// 添加花名冊模塊【獲取好友列表】 _rosterStorage = [[XMPPRosterCoreDataStorage alloc] init]; _roster = [[XMPPRoster alloc] initWithRosterStorage:_rosterStorage]; [_roster activate:_xmppStream];
-
應用
//使用CoreData獲取數據 // 1.上下文【關聯到數據庫XMPPRoster.sqlite】 NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].rosterStorage.mainThreadManagedObjectContext; // 2.FetchRequest【查哪張表】 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"]; // 3.設置過濾和排序 // 過濾當前登錄用戶的好友 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.執行請求獲取數據 _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
並設置代理,如果數據庫的內容發生了變化,這個類會自動通知代理,就可以設置界面的數據,做到實時更新。
- 注意:使用
六、消息
-
頭文件
- 注意:這幾個頭文件沒在
XMPPFramework.h
文件中,需要自己添加
// 消息模塊 #import "XMPPMessageArchiving.h" #import "XMPPMessageArchivingCoreDataStorage.h"
- 注意:這幾個頭文件沒在
-
初始化
// 添加聊天模塊 _msgStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init]; _msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgStorage]; [_msgArchiving activate:_xmppStream];
-
應用
// 上下文 NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext; // 請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"]; // 過濾、排序 // 1.當前登錄用戶的JID的消息 // 2.好友的Jid的消息 NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfo sharedWCUserInfo].jid,self.friendJid.bare]; NSLog(@"%@",pre); request.predicate = pre; // 時間升序 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
將文件轉化爲字符串,然後再通過XMPPFramework
傳輸。 - 先將文件上傳到服務器,再將文件網址通過
XMPPFramework
轉輸給好友,好友收到後再自行下載文件。
- 使用
-
難點解析
- 需要給
XMPPFramework
的``數據體添加一個信息類型字段。
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; //text 純文本 //image 圖片 [msg addAttributeWithName:@"bodyType" stringValue:bodyType]; // 設置內容 [msg addBody:text]; NSLog(@"%@",msg); [[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];
- 根據消息類型解析消息
```objc
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]) {//自己發
cell.textLabel.text = msg.body;
}else{//別人發的
cell.textLabel.text = msg.body;
}
cell.imageView.image = nil;
}
```
聲明:
-
以上內容屬於本人整理的筆記,如有錯誤的地方希望能告訴我,大家共同進步。
-
以上內容有些段落或語句可能是本人從其他地方Copy而來,如有侵權,請及時告訴我。