iOS學習43即時通訊之XMPP(2),iosxmpp
本篇是 即時通訊之XMPP(2) 接上次 即時通訊之XMPP(1)
1. 好友名單 1> 初始化好友花名冊
// 擷取管理好友的單例對象 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; // 給roster屬性進行初始化 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)]; // 將好友名單在通道中啟用 [self.xmppRoster activate:self.xmppStream]; // 設定花名冊代理 [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
2> XMPPRoster代理方法
代碼:
#pragma mark - XMPPRosterDelegate代理方法/// 開始擷取好友- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{ NSLog(@"開始擷取好友");}/// 結束擷取好友- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{ // 當前頁面適用於顯示好友名單的,所以在結束好友擷取的代理方法中要進行重新整理頁面,然後將資料顯示 // 重新整理頁面 [self.tableView reloadData];}// 接收好友的資訊// 這個代理方法會被執行多次,每添加完好友,相對應的好友資訊都要有反饋- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{ /* both 互為好友 none 互不為好友 to 我已經添加對方為好友,但是對方還沒有接受 from 對方已經添加我為好友,但是我還沒有接受 remove 曾經刪除的好友 */ // 描述自己和對方之間的關係 NSString *description = [[item attributeForName:@"subscription"] stringValue]; NSLog(@"description = %@", description); if ([description isEqualToString:@"to"] || [description isEqualToString:@"none"] || [description isEqualToString:@"both"] || [description isEqualToString:@"from"]) { // 添加好友 // 擷取添加好友的JID NSString *friendJID = [[item attributeForName:@"jid"] stringValue]; XMPPJID *jid = [XMPPJID jidWithString:friendJID]; // 如果數組中有這個使用者,就不用再進行操作 if ([self.allRosterArray containsObject:jid]) { return; } // 添加好友到數組 [self.allRosterArray addObject:jid]; // 在tableView中添加這條資料 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0]; [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight]; }}// 收到好友的監聽請求(加好友請求),是否同意- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{ self.fromJID = presence.from; // 需要相關的提醒框去確定是否接受 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"好友請求" message:@"是否接受好友請求" preferredStyle:UIAlertControllerStyleAlert]; __weak typeof(self)weakSelf = self; UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"接受" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 在花名冊中去接受相關的好友 [[XMPPManager shareXMPPManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES]; }]; UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒絕" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [[XMPPManager shareXMPPManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID]; }]; [alertController addAction:acceptAction]; [alertController addAction:rejectAction]; [self presentViewController:alertController animated:YES completion:nil];}
3> 添加好友所需方法
代碼:
- (void)addFriend{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"請輸入添加好友的名字" preferredStyle:UIAlertControllerStyleAlert]; __weak typeof(self)mySlef = self; [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { mySlef.textField = textField; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { NSLog(@"取消移除朋友!"); }]; UIAlertAction *ensureAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 使用JID記錄 XMPPJID *friendJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@", mySlef.textField.text, kDomin]]; // 監聽好友的動作 [mySlef.xmppRoster subscribePresenceToUser:friendJID]; // 添加好友 [mySlef.xmppRoster addUser:friendJID withNickname:mySlef.textField.text]; }]; [alertController addAction:ensureAction]; [alertController addAction:cancelAction]; [[self getCurrentVC] presentViewController:alertController animated:YES completion:nil];}
4> 移除朋友
代碼:
#pragma mark - 移除朋友- (void)removeFriendWithName:(NSString *)name{ // 使用JID記錄 XMPPJID *friendJID = [XMPPJID jidWithUser:name domain:kDomin resource:kResource]; // 停止監聽好友 [self.xmppRoster unsubscribePresenceFromUser:friendJID]; // 移除朋友 [self.xmppRoster removeUser:friendJID];}
2. 聊天 1> 聊天的規則:
從伺服器擷取聊天記錄,根據資料屬性判斷訊息類型
發送訊息
接收訊息
2> 初始化訊息歸檔
// 擷取管理訊息的儲存物件 XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; // 訊息管理器的初始化 self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()]; // 啟用通道 [self.messageArchiving activate:self.xmppStream]; // 設定代理 [self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()]; // 設定訊息管理上下文 self.context = [storage mainThreadManagedObjectContext];
3> 擷取聊天記錄
擷取聊天記錄使用CoreData的方式
代碼:
#pragma mark - 顯示訊息- (void)showMessage{ // 擷取管理上下文 NSManagedObjectContext *contxt = [XMPPManager shareXMPPManager].context; // 初始化請求對象 NSFetchRequest *request = [NSFetchRequest new]; // 擷取實體 NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:contxt]; // 設定查詢請求的實體 [request setEntity:entity]; // 設定謂詞查詢 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@", [XMPPManager shareXMPPManager].xmppStream.myJID.bare, self.chatToJID.bare]; [request setPredicate:predicate]; // 按照時間順序排序 NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES]; [request setSortDescriptors:@[sort]]; // 執行相關的操作 NSArray *resultArray = [contxt executeFetchRequest:request error:nil]; // 先清空訊息數組 [self.allMessageArray removeAllObjects]; // 添加context執行結果數組 [self.allMessageArray addObjectsFromArray:resultArray]; // 重新整理UI [self.tableView reloadData]; // 當前聊天記錄跳到最後一行 if (self.allMessageArray.count > 0) { NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count-1 inSection:0]; // 跳到最後一行 [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom]; }}
4> 發送訊息
#pragma mark - 發送點擊方法- (void)sendMessageAction{ // 設定message的body XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJID]; // 發送的內容,一般是從輸入框擷取,這裡我們就寫成固定的值 [message addBody:@"可以"]; // 通過通道進行訊息發送 [[XMPPManager shareXMPPManager].xmppStream sendElement:message];}
5> 接收/發送訊息的回調
代碼:
#pragma mark 發送訊息成功- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{ // 重新顯示相關訊息 [self showMessage];}#pragma mark 接受訊息成功- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{ [self showMessage];}#pragma mark 發送訊息失敗- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error{ NSLog(@"發送訊息失敗");}
6> 訊息氣泡
代碼:
//重寫message方法,在cell上顯示聊天記錄- (void)setMessage:(NSString *)message{ if (_message != message) { _message = message; self.contentLabel.text = _message; // self.contentLabel.numberOfLines = 0; [self.contentLabel sizeToFit]; CGRect rect = self.frame; if (self.isOut) {//發出去的 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_to"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; self.backgroundImageView.frame = CGRectMake(rect.size.width - self.contentLabel.frame.size.width - 50-20, 10, self.contentLabel.frame.size.width+20, rect.size.height-20); }else{//接收的 self.backgroundImageView.image = [[UIImage imageNamed:@"chat_from"] stretchableImageWithLeftCapWidth:45 topCapHeight:40]; self.backgroundImageView.frame = CGRectMake(50, 10,self.contentLabel.frame.size.width+40, rect.size.height-20); } //因為contentLabel已經自適應文字大小,故不用設定寬高,但需要設定位置 self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0); }}
以上代碼均為練習代碼的部分代碼!完整練習代碼github: https://github.com/AlonerOwl/UISenior11_-_1
由於只是練習代碼,對於介面和部分功能沒有最佳化,看起來比較low,如果有需求,請自己進行最佳化。
代碼: