切入今天的正題。今天要給之前的微信加入登陸,獲取好友列表,聊天(發送文字,表情,圖片,聲音等功能),最近聯系人等。在博客的開頭還是先來幾張圖來介紹一下功能,然後再給出核心代碼的實現。
一、功能模塊截圖 1.登陸和獲取好友列表 登陸的過程就是連接用XMPPFramework連接Openfire的過程,如果用戶登陸過,就從UserDefault裡獲取用戶的JID和密碼自動連接,如果用戶沒有登陸過則登陸。獲取好友列表也是通過XMPPFramework中的Roster來獲取的,運行截圖如下: 2.好友點擊去就是聊天頁面,聊天時如果是發送的圖片或者聲音,先存儲到服務器上存儲,服務器會返回存儲路徑然後再把URL發送給接收方,接收方再下載 (1)如果是發送的文字,把文字轉成屬性字符串,然後再轉成NSData,最後轉成字符串放在Message的Body中進行發送,下面是用Spark做接收端做得測試,截圖如下: (2)發送圖片,把圖片的存儲路徑發送給對方,讓對方從服務器上下載。截圖如下: (3)發送聲音和圖片一樣都是發送URL,截圖如下: 二、代碼實現部分 上面的部分是允許的效果截圖,從截圖上是不難看出功能點的。圖就先貼到這吧,下面給出核心代碼的實現。 1.使用XMPPFramework前的准備,獲取XmppStream和激活要用的組件,在AppDelegate添加代碼。以後要用xmppStream時,要通過AppDelegate獲取。下面的代碼是在AppDelegate.m中進行的相關組件的初始化,代碼如下 (1)實例化XMPPStream //創建xmppstream self.xmppStream = [[XMPPStream alloc]init]; (2)創建重連組件,並在xmppStream中激活 1 //創建重寫連接組件 2 xmppReconnect= [[XMPPReconnect alloc] init]; 3 //使組件生效 4 [xmppReconnect activate:self.xmppStream]; (3)創建message部分的內容,接受的消息我們保存在本地數據庫中,我們要顯示的時候是從數據庫中獲取的。在初始化消息組件的時候,要指定保存策略,一般可以選的是CoreData還是內存。指定完保存策略後實例化Message是要關聯保存策略,之後也是需要在XMPPStream中進行激活的,最後要獲取CoreData的上下文。代碼如下: 復制代碼 1 //創建消息保存策略(規則,規定) 2 messageStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; 3 //用消息保存策略創建消息保存組件 4 xmppMessageArchiving = [[XMPPMessageArchiving alloc]initWithMessageArchivingStorage:messageStorage]; 5 //使組件生效 6 [xmppMessageArchiving activate:self.xmppStream]; 7 //提取消息保存組件的coreData上下文 8 self.xmppManagedObjectContext = messageStorage.mainThreadManagedObjectContext; 復制代碼 (4),初始化獲取好友列表的相關組件並指定保存策略,和上面的代碼步驟極為相似。這也能看出來在XMPPFramework中進行組件的初始化步驟是差不多的。下面我們設定自動獲取花名冊,代碼如下: 復制代碼 1 xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init]; 2 xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage]; 3 //自動獲取用戶列表 4 xmppRoster.autoFetchRoster = YES; 5 xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES; 6 7 [xmppRoster activate:self.xmppStream]; 8 self.xmppRosterManagedObjectContext = xmppRosterStorage.mainThreadManagedObjectContext; 復制代碼 2.登陸模塊的實現 登陸時就是用戶輸入JID和Password,然後連接服務器和驗證密碼,如果認證成功則跳轉到好友列表才Controller,同時把JID和Password存儲到UserDefaults中便於下次自動連接。下面的代碼就是登陸部分的代碼(LoginViewController.m): (1).通過應用代理獲取XMPPStream,並注冊回調,代碼如下: 復制代碼 1 -(void) initXmpp 2 { 3 //獲取應用的xmppSteam(通過Application中的單例獲取) 4 UIApplication *application = [UIApplication sharedApplication]; 5 id delegate = [application delegate]; 6 self.xmppStream = [delegate xmppStream]; 7 8 //注冊回調 9 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 10 } 復制代碼 (2).創建JID連接服務器 復制代碼 1 //連接服務器 2 -(void) xmppConnect 3 { 4 if (![self.userNameTextFiled.text isEqualToString:@""] && self.userNameTextFiled.text != nil) 5 { 6 //1.創建JID 7 XMPPJID *jid = [XMPPJID jidWithUser:self.userNameTextFiled.text domain:MY_DOMAIN resource:@"iPhone"]; 8 9 //2.把JID添加到xmppSteam中 10 [self.xmppStream setMyJID:jid]; 11 12 //連接服務器 13 NSError *error = nil; 14 [self.xmppStream connectWithTimeout:10 error:&error]; 15 if (error) 16 { 17 NSLog(@"連接出錯:%@",[error localizedDescription]); 18 } 19 20 } 21 else 22 { 23 UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"用戶名不能為空" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil]; 24 [alter show]; 25 } 26 } 復制代碼 (3).連接成後需要認證密碼,代碼如下: 復制代碼 1 //連接後的回調 2 -(void)xmppStreamDidConnect:(XMPPStream *)sender 3 { 4 if (![self.passwordTextFiled.text isEqualToString:@""] && self.passwordTextFiled.text != nil) 5 { 6 //連接成功後認證用戶名和密碼 7 NSError *error = nil; 8 [self.xmppStream authenticateWithPassword:self.passwordTextFiled.text error:&error]; 9 if (error) 10 { 11 NSLog(@"認證錯誤:%@",[error localizedDescription]); 12 } 13 } 14 else 15 { 16 UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"密碼不能為空" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil]; 17 [alter show]; 18 } 19 } 復制代碼 (4)密碼認證成功後的回調 復制代碼 1 //認證成功後的回調 2 -(void)xmppStreamDidAuthenticate:(XMPPStream *)sender 3 { 4 NSLog(@"登陸成功"); 5 6 //密碼進入userDefault 7 NSUserDefaults *userDefult = [NSUserDefaults standardUserDefaults]; 8 [userDefult setObject:self.userNameTextFiled.text forKey:@"username"]; 9 [userDefult setObject:self.passwordTextFiled.text forKey:@"password"]; 10 11 //設置在線狀態 12 XMPPPresence * pre = [XMPPPresence presence]; 13 [self.xmppStream sendElement:pre]; 14 15 UIStoryboard *storybard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; 16 UIViewController *viewController = [storybard instantiateViewControllerWithIdentifier:@"mainController"]; 17 [self presentViewController:viewController animated:YES completion:^{ 18 }]; 19 } 復制代碼 (5)密碼認證失敗後的回調 1 //認證失敗的回調 2 -(void)xmppStream:sender didNotAuthenticate:(DDXMLElement *)error 3 { 4 NSLog(@"認證失敗"); 5 } (6),二次登陸自動連接代碼: 復制代碼 1 // 如果已登錄就直接填充密碼登陸 2 NSUserDefaults *userDefult = [NSUserDefaults standardUserDefaults]; 3 4 NSString *userName = [userDefult objectForKey:@"username"]; 5 NSString *password = [userDefult objectForKey:@"password"]; 6 NSLog(@"%@,%@",userName,password); 7 if (userName != nil && password != nil && ![userName isEqualToString:@""] && ![password isEqualToString:@""]) 8 { 9 self.userNameTextFiled.text = userName; 10 self.passwordTextFiled.text = password; 11 [self xmppConnect]; 12 } 復制代碼 3.獲取好友列表的XMPPFramework的代碼實現 在獲取用戶列表的代碼中就會用到我們之前注冊的Roster的內容,因為我們在實例化Roster的時候指定的保存策略是用CoreData進行保存的,並且是自動獲取好友列表。所以在獲取好友列表的TableViewController中我們只需要通過CoreData來獲取好友列表即可。下面將給出獲取好友列表的核心代碼: (1),獲取Roster對應的上下文,用於獲取存儲在Roster相應實體中的數據 1 //獲取Roster的上下文 2 UIApplication *application = [UIApplication sharedApplication]; 3 id delegate = [application delegate]; 4 self.xmppRosterManagedObjectContext = [delegate xmppRosterManagedObjectContext]; (2).獲取FetchRequst對象,並指定CoreData實體類,之後添加排序規則,代碼如下: 復制代碼 1 //從CoreData中獲取數據 2 //通過實體獲取FetchRequest實體 3 NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([XMPPUserCoreDataStorageObject class])]; 4 //添加排序規則 5 NSSortDescriptor * sortD = [NSSortDescriptor sortDescriptorWithKey:@"jidStr" ascending:YES]; 6 [request setSortDescriptors:@[sortD]]; 復制代碼 (3).獲取FetchedResultController並注冊回調,用於自動刷新TableView,代碼如下: 1 //獲取FRC 2 self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.xmppRosterManagedObjectContext sectionNameKeyPath:nil cacheName:nil]; 3 self.fetchedResultsController.delegate = self; (4)獲取存儲的內容 復制代碼 1 2 //獲取內容 3 NSError * error; 4 ; 5 if (![self.fetchedResultsController performFetch:&error]) 6 { 7 NSLog(@"%s %@",__FUNCTION__,[error localizedDescription]); 8 }