在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] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert|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.photo = nil ;
[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的註冊、登入、使用者資料修改、好朋友與訊息傳送接收和聊天室拉。