顯示具有 即時通訊 標籤的文章。 顯示所有文章
顯示具有 即時通訊 標籤的文章。 顯示所有文章

2015年11月12日 星期四

[iOS XMPP] 關於XMPP Framework的大小事 - xmpp stream設定

上一篇我們已經提到了XMPP Framework裡面主要分為兩個部分,xmpp core & xmpp extension。而xmpp core裡面最重要的就是xmpp stream,所以今天我們來看看如何在我們的app裡設定xmpp stream吧!
(在設定以下這些東西,通常都是在AppDelegate中進行實作)

1. 首先,設定連線

其實設定連線步驟很簡單,只要設定JID就可以了,範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"tungtungtsai@gmail.com"];
至於剩下的就交給xmpp stream完成吧!等等..我都還沒跟xmpp stream說我的server在哪啊?沒關係,在這裡,當你輸入了XMPPJID,xmpp stream就會去做SRV查找_xmpp-client._tcp.domain。例如,我在範例中打的domain是gmail.com,google server就很可能回傳像是"talk.google.com"的東西,然後xmpp stream就可以去和這個server建立連線。當然,如果說SRV查找失敗的話,那麼xmpp stream就直接去連JID的domain了。所以你要是已經知道你準備要連過去的xmpp server並沒有xmpp SRV records,你可以直接告訴xmpp stream你的host name,讓他不用花時間去查找了(反正也找不到...),範例如下:
xmppStream.myJID = [XMPPJID jidWithString:@"user@tungtungtsai.com"];
xmppStream.hostName = @"tungtungtsai.com";
你直接指派hostname有個最大好處就是他在development xmpp server也可以work。例如你的伺服器沒有DNS address只有實體IP,或是只能在local network run,那也沒關係,一樣能夠work。範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"user@dev1.myCompany.com"];
xmppStream.hostName = @"192.168.2.27";
設定了這些之後還有一件事,就是port。我們可以選擇性的看要不要給port,對每個xmpp spec來說,預設xmpp server都是開port 5222,當然如果你要開不同port就要特別設定一下hostPost了。


2. 添加代理(Delegates)

上一篇提到關於XMPPStream有些讓framework更具彈性、延展性、易開發的設計,而其一就是MulticastDelegate。由於xmpp framework需要支援很多的extensions,包含官方的和個人開發者所自己撰寫的plugin,所以傳統的代理模式(Delegate pattern)並不適合。因此,MulticastDelegate就可以讓你在你自己的extension中使用標準的delegate paradigm,並且允許多個classes去接收同一個delegate notification。這樣地好處是你不需要把你所有的xmpp handling code放進單一個class。

這裡簡單提一下:

在iOS開發中,主要有兩個callback systems,delegate和notification。Delegate很直覺也很簡單。使用者把自己設為delegate,然後就可以實作自己需要的delegate method。例如:

[worker setDelegate:self];

- (void)workerDidFinish:(Worker *)sender
{
}

- (void)worker:(Worker *)sender didFinishSubTask:(id)subtask inDuration:(NSTimeInterval)elapsed
{
}

- (BOOL)worker:(Worker *)sender shouldPerformSubTask:(id)subtask
{
}
Notifications也不難,使用者需要個別地註冊每個notification type (寫起來複雜一些),例如:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinish:)
                                             name:WorkerDidFinishNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinishSubTask:)
                                             name:WorkerDidFinishSubTaskNotification
                                           object:nil];

- (void)workerDidFinish:(NSNotification *)notification
{
    Worker *sender = [notification object];
}

- (void)workerDidFinishSubTask:(NSNotification *)notification
{
    Worker *sender = [notification object];
    id subtask = [[notification userInfo] objectForKey:@"subtask"];
    NSTimeInterval elapsed = [[[notification userInfo] objectForKey"duration"] doubleValue];
}

有時候要從notification中的Dictionary取出參數的話,就要先知道他擁有哪些keys。另外,notification是不允許回傳變數的。因此簡單地對兩個callback機制的優缺點分析如下:

Delegate優點:

1. 易於註冊多個callback
2. 當有多個參數時較易於使用
3. 允許回傳變數

Delegate缺點:

1. 只能夠是單一delegate

Notification優點:

1. 可以多個物件註冊同一個notification

Notification缺點:

1. 註冊多個callback較複雜
2. 要從dictionary中拿到參數很麻煩(要知道正確的key)
3. 當需要回傳變數時就不能使用

所以,對於XMPP framework來說哪一個方式適用呢?對XMPP來說,xmpp framework需要能夠廣播給多個listeners。例如當你有個message進來,你需要同時廣播給聊天的視窗、歷史紀錄和推播系統等等。此外,xmpp framework需要易於延展。xmpp要能夠支持很多的XEP,所以要能在broadcast端與listener端易於使用。xmpp framework 需要選擇能支持回傳變數的方式,也需要能夠幫助維持 thread-safety,因為xmpp framework大量地使用平行化與多線程。

講完你就發現好像delegate和notification都不是最好的解決方法,因此xmpp就創了一個GCDMulticastDelegate class。你可以在任何時候把你自己新增/移除為XMPPStream的delegate。

[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
...
[xmppStream removeDelegate:self];
簡單範例如下:
// Add myself as a delegate, and tell xmppStream to invoke my delegate methods on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// Then just implement whatever delegate methods you need like normal
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
   ...
}
就和傳統的代理模式差不多,但是你可以具體地說明thread-specific資訊。而如果你想要讓某些process離開主線程到背景執行也很容易,在iPhone中這種操作方式可以讓你的app維持良好的perfomance,範例如下
// Handle most stuff on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// But do that one slow thing on a background queue so it doesn't slow down the UI anymore
[xmppStream addDelegate:bgProcessor delegateQueue:bgProcessorQueue];


3. 添加模組(Modules)

接下來就是要加入我們自己需要的extensions了,當然你也可以自己寫自己需要的extensions,這裡我們不會探討到底有哪些extensions,僅簡單列出一些,因為這裡最重要的是要學會加入模組。
XMPPReconnect - 當斷線時自動重新連線
XMPPRoster - 提供聯絡人功能
XMPPRoom - 提供聊天室,群聊功能
XMPPPubSub - 提供發布與訂閱功能
現在我們來試著加入XMPPReconnect module到我們的xmpp stream吧!

xmppReconnect = [ [XMPPReconnect alloc] init];

// Optional configuration of xmppReconnect could go here.
// The defaults are fine for our purposes.

[xmppReconnect activate:xmppStream];

// You can also optionally add delegates to the module.

[xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];

// And that's all that is needed.
// The module will receive any delegate methods it needs automatically
// from the xmpp stream, and will continue to do its thing unless you deactivate it.

4. 建立連線

當我們都加好要用的modules,就可以開始建立連線了!

NSError *error = nil;
if (![xmppStream connect:&error])
{
    NSLog(@"Oops, I probably forgot something: %@", error);
}

若是我們在過程中忘記設定必須的屬性,例如JID,那連線就會回傳NO,並在error message中給予提示。

在連線過程中,client端和server端會建立xmpp handshake(交握),達成協定。如果你的伺服器需要建立安全連線,例如透過SSL/TLS方式,xmpp stream會自動建立安全連線。如果你的伺服器有不正確的X509 certificate,那就必須去實作xmppStream:willSecureWithSettings: 這個方法來改掉預設的安全設定。

5. 認證(Authenticating)

在所有連線handshaking都已經完成後,就會開始呼叫 xmppStreamDidConnect:方法,並開始進行認證程序。程式碼如下:

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    [xmppStream authenticateWithPassword:password error:NULL];
}


五個步驟進行完,在實際執行時就能與xmpp stream進行連線操作嘍!


參考連結:https://github.com/robbiehanson/XMPPFramework/wiki/IntroToFramework

2014年8月12日 星期二

[ iOS XMPP ] 即時聊天APP開發 - Part 1



最近在寫一支與即時訊息有關的社交APP,需要使用 XMPP 來完成即時訊息聊天的功能,因此在這裡我做個簡單筆記記錄一下最近的XMPP開發心得,不過我也是自己慢慢摸索,算不上什麼厲害的人就是了,所以不介意的話就參考看看吧。

<前言>

此篇主要是做一些最最最基本前置的設定,與程式碼都還無關,但是這些設定沒做好,後面可就頭大拉!

<正文開始>

首先,若是要開發與XMPP的APP,需要先架一台 XMPPServer,可以使用ejabber或是openfire伺服器,有關XMPP SERVER的細節可以參考: 這裡。因此有關伺服器的部分我就不多贅述了。在完成架設伺服器之後,接著就要來完成XMPP iOS Client端的部分嘍。




一開始,我們要先到 這裡 下載XMPP Framework,可以直接點選Download ZIP。下載完成後,開啓Xcode,建立一個新的專案(我將新專案取名為:XMPPTry),把剛剛在下載的XMPPFramework-master資料夾中的Authentication, Categories, Core, Extensions, Utilities, Vendor 匯入到新專案XMPPTry內(新專案的資料夾內應該要有這些檔案)。




其中,特別注意資料夾內有個Sample_XMPPFramework.h ,把它複製一份到你的APP資料夾中,並把名稱改為:XMPPFramework.h,這樣一來之後你就可以只要include這個header就可以了。至於裡面內容的設定細節後面會再討論,完成後在我的新專案資料夾XMPPTry內應如下圖。





然後打開Xcode把這些東西都匯入進去。



接下來,在Xcode中將一些你所需要的Framework加到專案內,如下圖匯入這些Framework。



然後將Build Setting裡的Header Search Path加上 /libxml2



到此為止,我們算是將XMPP一開始最基本的匯入工作完成了(當然,還沒正式開工寫程式呢)。
我們接著打開剛才曾經說過的XMPPFramework.h檔案,將你即將會需要用到的檔案import進來(基本上在Sample_XMPPFramework.h中除了XMPP.h外所有的module都被Comment掉了,你可以自己選擇接下來要使用哪些標頭檔),下面的是我所使用的,我把它們uncomment之後,將XMPPFramework.h加入我們的專案中。



其中,

XMPPReconnect.h
負責重新連線

XMPPMessageArchivingCoreDataStorage.h
負責處理訊息的資料儲存 (透過Core Data)

XMPPRoster.h
XMPPRosterCoreDataStorage.h
負責處理好友清單與資料儲存

XMPPvCardTemp.h
XMPPvCardTempModule.h
XMPPvCardAvatarModule.h
XMPPvCardCoreDataStorage.h
負責處理個人資料的內容,包含姓名、暱稱、大頭照、地址等等

XMPPCapabilitiesCoreDataStorage.h
XMPPCapabilities.h
負責支持Capabilities

XMPPMUC.h
XMPPRoomCoreDataStorage.h
負責處理聊天室與相關的資料儲存,多人聊天等等



現在,如果都沒有問題,就即將進入設定APP Delegate的部分拉!!

我們把一些XMPP的主要方法寫在APPDelegate中,基本上我這裡是參考剛剛所下載下來的XMPP Framework裡面的範例程式加以修改而成。

在 AppDelegate.h 中寫入以下程式碼。


//
//  JTAppDelegate.h
//  XMPPTry
//
//  Created by MacBook Pro on 2014/8/12.
//  Copyright (c) 2014 Jacky Tsai. All rights reserved.
//

#import <UIKit/UIKit.h>

#import "XMPPFramework.h"
#import <CoreData/CoreData.h>


@interface JTAppDelegate : UIResponder <UIApplicationDelegate, XMPPRosterDelegate>
{
XMPPStream *xmppStream;
XMPPReconnect *xmppReconnect;
        XMPPRoster *xmppRoster;
XMPPRosterCoreDataStorage *xmppRosterStorage;
        XMPPvCardCoreDataStorage *xmppvCardStorage;
XMPPvCardTempModule *xmppvCardTempModule;
XMPPvCardAvatarModule *xmppvCardAvatarModule;
XMPPCapabilities *xmppCapabilities;
XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
        XMPPMessageArchivingCoreDataStorage *xmppMessageArchivingCoreDataStorage;
        XMPPMessageArchiving *xmppMessageArchivingModule;
    
        UINavigationController *navigationController;
        LoginViewController *loginViewController;
NSString *password;
BOOL customCertEvaluation;
BOOL isXmppConnected;
}

@property (nonatomic, strong, readonly) XMPPStream *xmppStream;
@property (nonatomic, strong, readonly) XMPPReconnect *xmppReconnect;
@property (nonatomic, strong, readonly) XMPPRoster *xmppRoster;
@property (nonatomic, strong, readonly) XMPPRosterCoreDataStorage *xmppRosterStorage;
@property (nonatomic, strong, readonly) XMPPvCardTempModule *xmppvCardTempModule;
@property (nonatomic, strong, readonly) XMPPvCardAvatarModule *xmppvCardAvatarModule;
@property (nonatomic, strong, readonly) XMPPCapabilities *xmppCapabilities;
@property (nonatomic, strong, readonly) XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
@property (nonatomic, strong) XMPPMessageArchivingCoreDataStorage *xmppMessageArchivingCoreDataStorage;
@property (nonatomic, strong) XMPPMessageArchiving *xmppMessageArchivingModule;

- (NSManagedObjectContext *)managedObjectContext_roster;
- (NSManagedObjectContext *)managedObjectContext_capabilities;

- (BOOL)connect;
- (void)disconnect;

- (void)setupStream;
- (void)teardownStream;

- (void)goOnline;
- (void)goOffline;


@end


AppDelegate.h 中的程式碼,我們宣告了一些會使用到關於XMPP的變數,接下來,在AppDelegate.m 中 import 這些檔案。

#import "GCDAsyncSocket.h"
#import "XMPP.h"
#import "XMPPLogging.h"
#import "XMPPReconnect.h"
#import "XMPPCapabilitiesCoreDataStorage.h"
#import "XMPPRosterCoreDataStorage.h"
#import "XMPPvCardAvatarModule.h"
#import "XMPPvCardCoreDataStorage.h"
#import "DDLog.h"
#import "DDTTYLogger.h"
#import <AudioToolbox/AudioServices.h>
#import <CFNetwork/CFNetwork.h>

結束之後,synthesize在.h檔中的property,完成後如下圖。


接著在.m檔中開始撰寫以下的函數

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Configure logging framework
[DDLog addLogger:[DDTTYLogger sharedInstance]];
     
 //設定接收推播 (Push notification)
 [[UIApplication sharedApplication] registerForRemoteNotificationTypesUIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeBadge];
 // 設定 XMPP stream
[self setupStream];

// 設定XMPP連線,未連線成功則跳到logIn頁面 (loginViewController )
if (![self connect])
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[navigationController presentViewController:loginViewController animated:YES completion:NULL];
});
}
return YES;
}


- (void)applicationDidEnterBackground:(UIApplication *)application 
{
DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

#if TARGET_IPHONE_SIMULATOR
DDLogError(@"The iPhone simulator does not process background network traffic. "
  @"Inbound traffic is queued until the keepAliveTimeout:handler: fires.");
#endif

if ([application respondsToSelector:@selector(setKeepAliveTimeout:handler:)]) 
{
[application setKeepAliveTimeout:600 handler:^{
DDLogVerbose(@"KeepAliveHandler");
// Do other keep alive stuff here.
}];
}
}

- (void)applicationWillEnterForeground:(UIApplication *)application 
{
DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    
    [self connect ];
}

- (void)dealloc
{
[self teardownStream];
}


設定Core Data

#pragma mark Core Data

- (NSManagedObjectContext *)managedObjectContext_roster
{
    return [xmppRosterStorage mainThreadManagedObjectContext];
}

- (NSManagedObjectContext *)managedObjectContext_capabilities
{
    return [xmppCapabilitiesStorage mainThreadManagedObjectContext];

}




- (void)setupStream {
    
    NSAssert(xmppStream == nil, @"Method setupStream invoked multiple times");
    
    xmppStream = [[XMPPStream alloc] init];
    
//確定裝置並非模擬器
#if !TARGET_IPHONE_SIMULATOR
    {
        //let xmpp run in the background
        xmppStream.enableBackgroundingOnSocket = YES;

    }
#endif
    
    
    //init Reconnect 斷線後可以重新連線
    xmppReconnect = [[XMPPReconnect alloc] init];
    
    // 此處可以自行更動成你想要使用的儲存方式
    // 這裡是使用core data進行資料儲存
    xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
    xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage];
    
    // 設定是否自動從伺服器抓取你的好友名單
    xmppRoster.autoFetchRoster = YES;
    
    // 設定是否自動接受送來的好友邀請
    xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;
    
    
    
    //XMPPRoster 會自動整合 XMPPvCardAvatarModule 以取得roster中使用者的大頭照.
    //vCard Avatar module 會和標準的 vCard Temp module 一起下載使用者大頭照.
    xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
    xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:xmppvCardStorage];
    xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:xmppvCardTempModule];

    
    // 設定 capabilities: For hashing
    xmppCapabilitiesStorage = [XMPPCapabilitiesCoreDataStorage sharedInstance];
    xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:xmppCapabilitiesStorage];
    
    xmppCapabilities.autoFetchHashedCapabilities = YES;
    xmppCapabilities.autoFetchNonHashedCapabilities = NO;
    
    // 設定Core Data
    xmppMessageArchivingCoreDataStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
    xmppMessageArchivingModule = [[XMPPMessageArchiving alloc]initWithMessageArchivingStorage:xmppMessageArchivingCoreDataStorage];
    
    [xmppMessageArchivingModule setClientSideMessageArchivingOnly:YES];
    
    // Activate xmpp modules
    [xmppReconnect         activate:xmppStream];
    [xmppRoster            activate:xmppStream];
    [xmppvCardTempModule   activate:xmppStream];
    [xmppvCardAvatarModule activate:xmppStream];
    [xmppCapabilities      activate:xmppStream];
    [xmppMessageArchivingModule activate:xmppStream];

    
    // 加自己為 delegate
    [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppvCardTempModule addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppvCardAvatarModule addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [xmppMessageArchivingModule addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    //設定伺服器的Host Name & Port (這裡請設定你自己的伺服器IP和Port)
    [xmppStream setHostName:@"140.112.XXX.XX"];
    [xmppStream setHostPort:5222];
    
    // You may need to alter these settings depending on the server you're connecting to
    customCertEvaluation = YES;
    
}


- (void)teardownStream {
    [xmppStream removeDelegate:self];
    [xmppRoster removeDelegate:self];
    
    [xmppReconnect         deactivate];
    [xmppRoster            deactivate];
    [xmppvCardTempModule   deactivate];
    [xmppvCardAvatarModule deactivate];
    [xmppCapabilities      deactivate];
    
    [xmppStream disconnect];
    
    xmppStream = nil;
    xmppReconnect = nil;
    xmppRoster = nil;
    xmppRosterStorage = nil;
    xmppvCardStorage = nil;
    xmppvCardTempModule = nil;
    xmppvCardAvatarModule = nil;
    xmppCapabilities = nil;
    xmppCapabilitiesStorage = nil;
}

設定使用者上下線

- (void)goOnline
{
    XMPPPresence *presence = [XMPPPresence presence];
    [[self xmppStream] sendElement:presence];
}

- (void)goOffline
{
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
    [[self xmppStream] sendElement:presence];
}


設定XMPP的連線

#pragma mark Connect/disconnect

- (BOOL)connect
{
    if (![xmppStream isDisconnected]) {
        NSLog(@"已經連好線拉");
        return YES;
    }
    
    NSString *myJID = [[NSUserDefaults standardUserDefaults] stringForKey: @"JID"];
    NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey: @"JPassword"];
    
    
    if (myJID == nil || myPassword == nil) {
       
        return NO;
    }
    
    
    [xmppStream setMyJID:[XMPPJID jidWithString:myJID]];
    password = myPassword;
    
    NSError *error = nil;
    if (![xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error])
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error connecting"
                                                            message:@"See console for error details."
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
        
        DDLogError(@"Error connecting: %@", error);
        return NO;
    }
    
    return YES;
}

- (void)disconnect
{
    [self goOffline];
    [xmppStream disconnect];
}


設定XMPPStream Delegate (這裡的Function 較多,要仔細看一下)


#pragma mark XMPPStream Delegate

- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    NSString *expectedCertName = [xmppStream.myJID domain];
    if (expectedCertName)
    {
        [settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
    }
    
    if (customCertEvaluation)
    {
        [settings setObject:@(YES) forKey:GCDAsyncSocketManuallyEvaluateTrust];
    }
}

- (void)xmppStream:(XMPPStream *)sender didReceiveTrust:(SecTrustRef)trust
 completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(bgQueue, ^{
        
        SecTrustResultType result = kSecTrustResultDeny;
        OSStatus status = SecTrustEvaluate(trust, &result);
        
        if (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) {
            completionHandler(YES);
        }
        else {
            completionHandler(NO);
        }
    });
}

- (void)xmppStreamDidSecure:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    isXmppConnected = YES;
    
    NSError *error = nil;
    
    if (![[self xmppStream] authenticateWithPassword:password error:&error])
    {
        DDLogError(@"Error authenticating: %@", error);
    }
}


//這個函數特別重要,他是認證使用者是否為user的函數,當使用者欲建立連線時,會call這個函數
//很多人在做註冊新會員時,就需要透過此函數新增使用者
//這裡寫的是如果通過認證就上傳vcard(個人資料)


- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);

    NSLog(@"認證通過~~");
    
    // 上傳使用者的資料 (vCard) , 先拿看看vCard,若不存在創一個新的上傳,若已存在,直接更新
    XMPPvCardTemp *temp =  [self.xmppvCardTempModule myvCardTemp];
    
    if (!temp)
    {
        NSXMLElement *vCardXML = [NSXMLElement elementWithName:@"vCard" xmlns:@"vcard-temp"];
        XMPPvCardTemp *newvCardTemp = [XMPPvCardTemp vCardTempFromElement:vCardXML];
        [newvCardTemp setNickname:@"UserABC"];
        [newvCardTemp setPhoto:nil];
        [xmppvCardTempModule updateMyvCardTemp:newvCardTemp];
    }
    else{
        
        temp.nickname = @"UserABC";
        temp.photonil ;
        [self.xmppvCardTempModule updateMyvCardTemp:temp];
    }
    [self goOnline];

}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
}

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    return NO;
}


//收到他人送來的訊息時會呼叫此函數

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    
    if ([message isChatMessageWithBody])
    {
        XMPPUserCoreDataStorageObject *user = [xmppRosterStorage userForJID:[message from]
                                                                 xmppStream:xmppStream
                                                       managedObjectContext:[self managedObjectContext_roster]];
        
        NSString *nickName = [user nickname];
        if (nickName == NULL) {
            nickName = @"Someone";
        }
        
        if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
        {
            //處理使用者正在使用APP時收到新訊息的狀況,本處先讓手機震動一下
            AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
        }
        else
        {
            //若收到訊息時並未開啟APP,則推播新訊息給User
            UILocalNotification *localNotification = [[UILocalNotification alloc] init];
            localNotification.alertAction = @"OK";
            localNotification.alertBody = [NSString stringWithFormat:@"%@ send you a message",nickName];
            
            [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
        }
        
    }
}

//收到他人上下線時會呼叫此函數

- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
    DDLogVerbose(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, [presence fromStr]);
  
    if (presence.status) {
        //處理接到他人上下線狀態更動
    }

}

- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    if (!isXmppConnected)
    {
        DDLogError(@"Unable to connect to server. Check xmppStream.hostName");
    }
}



最後,處理關於好友請求的部分與Push Notification的設定就ok拉!



#pragma mark xmppRoster Deledgate

//當接收到他人對你的訂閱請求
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
{
    
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    XMPPUserCoreDataStorageObject *user = [xmppRosterStorage userForJID:[presence from]
                                                             xmppStream:xmppStream
                                                   managedObjectContext:[self managedObjectContext_roster]];

    DDLogVerbose(@"didReceivePresenceSubscriptionRequest from user %@ ", user.jidStr);
    
    //直接接受他人對你的訂閱請求
    [xmppRoster acceptPresenceSubscriptionRequestFrom:[presence from]
                                       andAddToRoster:YES];
    
    
}


#pragma mark Receive Notification

//處理推播相關的函數

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
    NSLog(@"Received description: %@", [userInfo description]);
#if 0
    NSDictionary *aps = [userInfo objectForKey:@"aps"];
    if(aps != nil) {
        NSDictionary *alert = [aps objectForKey:@"alert"];
        if(alert != nil) {
            NSString *loc_key = [alert objectForKey:@"loc-key"];
            NSString *body = [alert objectForKey:@"body"];
            if(loc_key != nil) {
                if([loc_key isEqualToString:@"IM_MSG"]) {
                    // ???
                }
                else if([loc_key isEqualToString:@"IC_MSG"]) {
                    // ???
                }
            }
            if (body != nil) {
                PNMessage *pnMessage = [NSEntityDescription insertNewObjectForEntityForName:@"PNMessage"
                                                                     inManagedObjectContext:[self managedObjectContext]];
                
                NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
                [dateFormatter setDateFormat:@"HH:mm:ss"];
                NSString *TimeStr = [dateFormatter stringFromDate:[NSDate date]];
                [dateFormatter setDateFormat:@"yyyy/MM/dd"];
                NSString *DateStr = [dateFormatter stringFromDate:[NSDate date]];
                [dateFormatter release];
                
                NSString *fromStr =[NSString stringWithFormat:@"推播訊息 %@ %@", DateStr, TimeStr];
                
                pnMessage.fromUser = fromStr;
                pnMessage.messageText = body;
                NSError *error = nil;
                if (![[self managedObjectContext]save:&error]) {
                    NSLog(@"新增物件時遇到錯誤");
                }
                else {
                    NSLog(@"儲存成功:fromUser=%@ messageText=%@",
                          pnMessage.fromUser, pnMessage.messageText);
                }
            }
        }
    }
#endif
}


//Receive the push notification
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    // process background incoming call
    NSLog(@"didReceiveLocalNotification!");
    
    [self showAlarm:notification.alertBody];
    application.applicationIconBadgeNumber = 0;
    
}

- (void)showAlarm:(NSString *)text {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Alarm"
                                                        message:text delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil];
    [alertView show];
}




終於把APP Delegate的最初設定做完了,不過在這裡還有一些細節並未執行,像是收到訊息時應該如何顯示/收到好友上下線狀態改變等等,如果之後有時間再來處理吧!但是現在至少要確定它可以執行無誤,接下來就可以比較輕鬆地處理與XMPP的註冊、登入、使用者資料修改、好朋友與訊息傳送接收和聊天室拉。