iOS 即時(shí)通信系列之XMPP登入、注冊(cè)、聊天

說明:在即時(shí)通信系列XMPP之搭建本地服務(wù)器中闡述了通過XAMPP、Openfire工具搭建基于XMPP的服務(wù)器端,并使用spark軟件進(jìn)行XMPP客戶端的調(diào)試。在本文中主要闡述將XMPP添加到工程中,實(shí)現(xiàn)iOS中基于XMPP的即時(shí)通信。

項(xiàng)目環(huán)境配置

使用CocoaPods導(dǎo)入XMPP框架,如果不會(huì)CocoaPods,點(diǎn)擊CocoaPods安裝及使用教程,或者手動(dòng)導(dǎo)入XMPP框架。

創(chuàng)建XMPP管理類

為了讓XMPP不會(huì)對(duì)工程中其它業(yè)務(wù)代碼具有侵入性,方便我們管理整個(gè)關(guān)于XMPP的邏輯處理,通常會(huì)采用單例模式新建一個(gè)專門的管理類,為此,在工程中創(chuàng)建一個(gè)繼承自NSObject的XMPPManger,為了能夠在代碼中方便調(diào)用XMPPStreamDelegate的方法,讓其遵守XMPPStreamDelegate協(xié)議。
  XMPP框架中所有的連接、登入、注冊(cè)、授權(quán)、失去連接等操作的回調(diào)都是通過XMPPStream來管理,所以XMPPManger必須有一個(gè)XMPPStream實(shí)例。其它的還包括進(jìn)行添加好友、刪除好友、獲取好友列表等功能的XMPPRoster;消息保存組件XMPPMessageArchiving等。

XMPPManager.h
@interface XMPPManager : NSObject<XMPPStreamDelegate>

/**
 *  創(chuàng)建全局唯一的管理者對(duì)象
 *  @return 返回一個(gè)單例對(duì)象
 */
+ (XMPPManager *)sharedXMPPManager;

/**
 *  信息管道
 */
@property (nonatomic,strong)XMPPStream *stream;
/**
 *  進(jìn)行添加好友 刪除好友 獲取好友列表等功能
 */
@property (nonatomic,strong)XMPPRoster *roster;
/**
 *  消息保存組件
 */
@property (nonatomic,strong)XMPPMessageArchiving *messageArchiving;
/**
 *  消息保存組件的CoreData上下文
 */
@property (nonatomic,strong)NSManagedObjectContext *managerObjectContext;
/**
 *  登入
 *
 *  @param userName 賬戶
 *  @param password 密碼
 */
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password;
/**
 *  注冊(cè)
 *
 *  @param userName 賬戶
 *  @param password 密碼
 */
- (void)registWithUserName:(NSString *)userName password:(NSString *)password;

@end
XMPPManager.m
//服務(wù)器的地址
static NSString *const kHostName = @"127.0.0.1";
//端口號(hào)
static UInt16 const kHostPort = 5222;

//連接服務(wù)器類型
typedef NS_ENUM(NSUInteger,ConnectToServerStatus) {
    ConnectToServerStatusLogin,
    ConnectToServerStatusRegist,
};

@interface XMPPManager ()
//登入密碼
@property (nonatomic,strong)NSString *loginpassword;
//注冊(cè)密碼
@property (nonatomic,strong)NSString *registpassword;
//是登入還是注冊(cè)
@property (nonatomic,assign)ConnectToServerStatus connectToSercerStatus;

@end

@implementation XMPPManager

+ (XMPPManager *)sharedXMPPManager{
    static XMPPManager *xmppManager = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        xmppManager = [[XMPPManager alloc] init];
    });
    return xmppManager;
}

- (instancetype)init{
    if ([super init]) {
        //設(shè)置通信管道屬性
        self.stream = [[XMPPStream alloc] init];
        self.stream.hostName = kHostName;
        self.stream.hostPort = kHostPort;
        //設(shè)置當(dāng)前對(duì)象為stream的代理
        [self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
        
        //進(jìn)行好友存儲(chǔ)
        XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
        self.roster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_main_queue()];
        //激活
        [self.roster activate:self.stream];
        
        //進(jìn)行聊天信息存儲(chǔ)
        XMPPMessageArchivingCoreDataStorage *messageArchivingStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
        self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageArchivingStorage dispatchQueue:dispatch_get_main_queue()];
        [self.messageArchiving activate:self.stream];
        
        self.managerObjectContext = messageArchivingStorage.mainThreadManagedObjectContext;
    }
    return self;
}

//與服務(wù)器建立鏈接
-(void)connectToServerWithUser:(NSString *)user{
    //要是正在鏈接的話那么就先斷開連接
    if ([self.stream isConnected]) {
        [self disconnectServer];
    }
    XMPPJID *jid = [XMPPJID jidWithUser:user domain:@"DH_Fantasy" resource:@"iPhone"];
    self.stream.myJID = jid;
    NSError *error = nil;
    [self.stream connectWithTimeout:30.0f error:&error];
    if (nil != error) {
        NSLog(@"%s__%d__鏈接出錯(cuò):%@",__FUNCTION__,__LINE__,error);
    }
}

//與服務(wù)器斷開鏈接
-(void)disconnectServer{
    [self.stream disconnect];
}
//登入
- (void)loginWithUserName:(NSString *)userName password:(NSString *)password{
    self.connectToSercerStatus = ConnectToServerStatusLogin;
    self.loginpassword = password;//將傳進(jìn)來的password傳給self.password
    [self connectToServerWithUser:userName];
}
//注冊(cè)
- (void)registWithUserName:(NSString *)userName password:(NSString *)password{
    self.connectToSercerStatus = ConnectToServerStatusRegist;
    self.registpassword = password;
    [self connectToServerWithUser:userName];
}

#pragma mark XMPPStreamDelegate
//與服務(wù)器鏈接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
#pragma mark 判斷與服務(wù)器建立連接是登陸還是注冊(cè)
    switch (self.connectToSercerStatus) {
        case ConnectToServerStatusLogin:
        {
            NSError *error = nil;
            [self.stream authenticateWithPassword:self.loginpassword error:&error];
            if (nil != error) {
                NSLog(@"%s__%d__驗(yàn)證出錯(cuò):%@",__FUNCTION__,__LINE__,error);
            }
            break;
        }
        case ConnectToServerStatusRegist:
        {
            NSError *err = nil;
            [self.stream registerWithPassword:self.registpassword error:&err];
            if (nil != err) {
                NSLog(@"%s__%d__注冊(cè)出錯(cuò):%@",__FUNCTION__,__LINE__,err);
            }
            break;
        }
        default:
            break;
    }
}
//與服務(wù)器鏈接失敗
-(void)xmppStreamConnectDidTimeout:(XMPPStream *)sender{
    NSLog(@"??與服務(wù)器鏈接失敗");
}

- (void)xmppStreamWillConnect:(XMPPStream *)sender {
    NSLog(@"??socket正在連接...");
}

- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket {
    NSLog(@"??socket連接成功");
    // 連接成功之后,由客戶端xmpp發(fā)送一個(gè)stream包給服務(wù)器,服務(wù)器監(jiān)聽來自客戶端的stream包,并返回stream feature包
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error {
    NSLog(@"??xmpp授權(quán)失敗:%@", error.description);
}

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
    NSLog(@"??xmpp授權(quán)成功。");
    // 只有進(jìn)入到這里,才算是真正的可以聊天了
}

- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error {
    NSLog(@"??xmpp失去連接。");
}

@end

AppDelegate

在AppDelegate中主要設(shè)置了下進(jìn)入APP時(shí)的登入狀態(tài),在一些場(chǎng)景中,某些控制器必須為登入狀態(tài)才能進(jìn)入,用NSUserDefaults存儲(chǔ)為Bool類型,這為判斷是否登入提供了方便。

AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    //設(shè)置主窗口并顯示
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    //設(shè)置進(jìn)入APP為沒登入狀態(tài)
    [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isLoginStatus"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    //設(shè)置好友列表控制器為根視圖
    RosterTableViewController *VC = [[RosterTableViewController alloc] init];
    UINavigationController *NC = [[UINavigationController alloc] initWithRootViewController:VC];
    self.window.rootViewController = NC;
    
    return YES;
}

RosterTableViewController

RosterTableViewController主要作用就是進(jìn)行自動(dòng)登入與顯示獲取到的好友。

RosterTableViewController.m
@interface RosterTableViewController ()

//用于保存獲取到的好友
@property (nonatomic,strong)NSMutableArray *rosterArray;

@end

@implementation RosterTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.title = @"好友列表";
    
    self.rosterArray = [[NSMutableArray alloc] init];
    
    //判斷之前是否登入過
    if (nil != [[NSUserDefaults standardUserDefaults]objectForKey:@"userName"]) {
        
        //之前登入過,直接讀取用戶名和密碼進(jìn)行連接
        NSString *userName = [[NSUserDefaults standardUserDefaults]objectForKey:@"userName"];
        NSString *password = [[NSUserDefaults standardUserDefaults]objectForKey:@"password"];
        [[XMPPManager sharedXMPPManager]loginWithUserName:userName password:password];
        [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
        
    }else{
        
        //之前沒登入過則進(jìn)入到登入窗口
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        LoginViewController *loginVC = [storyboard instantiateViewControllerWithIdentifier:@"login"];
        
        [self.navigationController pushViewController:loginVC animated:YES];
    }
    
    [[XMPPManager sharedXMPPManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

#pragma mark UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.rosterArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:@"cell"];
    }
    
    XMPPJID *jid = self.rosterArray[indexPath.row];
    cell.textLabel.text = jid.user;
    
    return cell;
}

#pragma mark 進(jìn)入聊天界面
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    XMPPJID *jid = self.rosterArray[indexPath.row];
    ChatViewController *chat = [[ChatViewController alloc] init];
    chat.chatToJid = jid;
    [self.navigationController pushViewController:chat animated:YES];
}

#pragma mark XMPPStreamDelegate
//授權(quán)成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isLoginStatus"];
    
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    //我們驗(yàn)證之后默認(rèn)的狀態(tài)是離線的 于是我們想顯示在線,需要告訴服務(wù)器自己的狀態(tài)
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
    [[XMPPManager sharedXMPPManager].stream sendElement:presence];
}

//驗(yàn)證失敗
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
    NSLog(@"登入驗(yàn)證失敗??");
}

#pragma mark XMPPRosterDelegate
//剛開始獲取好友列表
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{
    NSLog(@"開始獲取好友列表");
}

//正在獲取好友列表
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{
    NSLog(@"正在獲取好友列表...%@",item);
    NSString *jidStr = [[item attributeForName:@"jid"]stringValue];
    XMPPJID *jid =[XMPPJID jidWithString:jidStr];
    [self.rosterArray addObject:jid];
    
    //將數(shù)據(jù)添加進(jìn)數(shù)組
    [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.rosterArray.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
}

//已經(jīng)完成好友列表獲取
-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{
    NSLog(@"已經(jīng)完成好友列表獲取??");
}

Friends List.png

LoginViewController

LoginViewController中使用XMPPManager單例在登入按鈕的點(diǎn)擊事件中調(diào)用- (void)loginWithUserName:(NSString ***)userName password:(NSString *)password;方法進(jìn)行登入。

LoginViewController.m
@interface LoginViewController ()

//登入賬戶
@property (weak, nonatomic) IBOutlet UITextField *userName;
//登入密碼
@property (weak, nonatomic) IBOutlet UITextField *password;

@end

@implementation LoginViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

//登入操作
- (IBAction)loginAction:(id)sender {
    [[XMPPManager sharedXMPPManager] loginWithUserName:self.userName.text password:self.password.text];
}

#pragma mark XMPPStreamDelegte
//驗(yàn)證成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
    //存儲(chǔ)賬戶密碼用于下次自動(dòng)登入
    [[NSUserDefaults standardUserDefaults] setObject:self.userName.text forKey:@"userName"];
    [[NSUserDefaults standardUserDefaults] setObject:self.password.text forKey:@"password"];
    
    //改變APP的登入狀態(tài)
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isLoginStatus"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    //驗(yàn)證之后默認(rèn)的狀態(tài)為離線,需要手動(dòng)告訴服務(wù)器自己的狀態(tài)
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
    [[XMPPManager sharedXMPPManager].stream sendElement:presence];
    
    //返回到根視圖
    [self.navigationController popToRootViewControllerAnimated:YES];
}

//驗(yàn)證失敗
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
    NSLog(@"登入驗(yàn)證失敗??");
}
LoginViewController.png

RegisterViewController

注冊(cè)和登入比較相似,只需在注冊(cè)按鈕的點(diǎn)擊事件中使用XMPPManager單例調(diào)用- (void)registWithUserName:(NSString *)userName password:(NSString *)password;方法進(jìn)行注冊(cè)。

RegisterViewController.m
@interface RegisterViewController ()

//注冊(cè)賬戶
@property (weak, nonatomic) IBOutlet UITextField *userName;
//注冊(cè)密碼
@property (weak, nonatomic) IBOutlet UITextField *password;
//密碼驗(yàn)證
@property (weak, nonatomic) IBOutlet UITextField *rePassword;

@end

@implementation RegisterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

- (IBAction)registerAction:(id)sender {
    //簡(jiǎn)單驗(yàn)證:用戶名不為空且密碼和密碼驗(yàn)證輸入一致
    if (![self.userName.text isEqualToString:@""] && [self.rePassword.text isEqualToString:self.password.text]) {
        [[XMPPManager sharedXMPPManager] registWithUserName:self.userName.text password:self.password.text];
    }else{
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"注冊(cè)失敗" message:@"" preferredStyle:(UIAlertControllerStyleAlert)];
        
        if ([self.userName.text isEqualToString:@""]) {
            alertController.title = @"賬戶為空";
            alertController.message = @"請(qǐng)輸入賬戶";
        }
        if (![self.rePassword.text isEqualToString:self.password.text]) {
            alertController.title = @"密碼驗(yàn)證錯(cuò)誤";
            alertController.message = @"請(qǐng)重新輸入密碼驗(yàn)證";
        }
        
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
            [alertController dismissViewControllerAnimated:YES completion:nil];
        }];
        
        [alertController addAction:okAction];
        [self presentViewController:alertController animated:YES completion:nil];
    }
}

#pragma mark--XMPPStreamDelegate
//注冊(cè)成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender{
    NSLog(@"%s__%d__注冊(cè)成功",__FUNCTION__,__LINE__);
    
    //存儲(chǔ)賬戶密碼用于下次自動(dòng)登入
    [[NSUserDefaults standardUserDefaults] setObject:self.userName.text forKey:@"userName"];
    [[NSUserDefaults standardUserDefaults] setObject:self.password.text forKey:@"password"];
    
    //改變APP的登入狀態(tài)
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isLoginStatus"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    //驗(yàn)證之后默認(rèn)的狀態(tài)為離線,需要手動(dòng)告訴服務(wù)器自己的狀態(tài)
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
    [[XMPPManager sharedXMPPManager].stream sendElement:presence];
    
    //注冊(cè)成功之后返回到登陸界面
    [self.navigationController popToRootViewControllerAnimated:YES];
}
//注冊(cè)失敗
-(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{
    NSLog(@"%s__%d__注冊(cè)失敗%@",__FUNCTION__,__LINE__,error);
}

RegisterViewController.png

ChatViewController

在ChatViewController中需要公開一個(gè)屬性,用來接收好友列表頁(yè)面?zhèn)鱽淼腦MPPJID。

ChatViewController.h
@interface ChatViewController : UIViewController

@property (nonatomic,strong)XMPPJID *chatToJid;

@end
ChatViewController.m
@interface ChatViewController ()<UITableViewDataSource,UITableViewDelegate>

//顯示消息
@property (weak, nonatomic) IBOutlet UITableView *messageContent;
//消息輸入框
@property (weak, nonatomic) IBOutlet UITextView *messageTextView;
//鍵盤底部約束
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageTextfieldConstraint;
//存儲(chǔ)消息數(shù)據(jù)
@property (nonatomic,strong) NSMutableArray *allMessageArray;

@end

@implementation ChatViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.messageTextView.text = @"";

    self.messageContent.delegate = self;
    self.messageContent.dataSource = self;
    
    self.allMessageArray = [[NSMutableArray alloc] init];
    //將歷史消息加入到數(shù)組
    [self.allMessageArray addObjectsFromArray:[self fromDataBaseFetchResult]];
    
    //監(jiān)聽鍵盤frame的改變
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(kbFrameWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
    
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    [self reloadMessage];
    
    self.messageContent.separatorStyle = UITableViewCellSeparatorStyleNone;
}

//消息發(fā)送按鈕
- (IBAction)sendMessageAction:(id)sender {
    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJid];
    //要發(fā)送的消息添加到Body
    [message addBody:self.messageTextView.text];
    //發(fā)送消息
    [[XMPPManager sharedXMPPManager].stream sendElement:message];
}

//鍵盤即將改變frame
- (void)kbFrameWillChange:(NSNotification *)noti{
    //獲取窗口的高度
    CGFloat windowH = [UIScreen mainScreen].bounds.size.height;
    //鍵盤結(jié)束的frm
    CGRect kbEndFrm = [noti.userInfo [UIKeyboardFrameEndUserInfoKey]CGRectValue];
    //鍵盤結(jié)束的y值
    CGFloat kbEndY = kbEndFrm.origin.y;
    self.messageTextfieldConstraint.constant = windowH - kbEndY;
    
    [self scrollsToBottomAnimated:YES];
}

- (void)scrollsToBottomAnimated:(BOOL)animated{
    CGFloat offset = self.messageContent.contentSize.height - self.messageTextfieldConstraint.constant - 55;
    if (offset > 0){
        [self.messageContent setContentOffset:CGPointMake(0, offset) animated:animated];
    }
}

#pragma mark XMPPStreamDelegate
//發(fā)送信息成功
-(void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{
    NSLog(@"發(fā)送成功??");
    [self reloadMessage];//發(fā)送信息時(shí)刷新一次頁(yè)面
}

//發(fā)送信息失敗
-(void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error{
    NSLog(@"發(fā)送失敗??");
}

//收到信息
-(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
    NSLog(@"收到消息%@",message);
    if ([message isChatMessageWithBody]) {
        [self reloadMessage];//收到信息是刷新一次頁(yè)面
    }
}

#pragma mark 加載聊天信息
-(void)reloadMessage{
    
    //從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)
    NSArray *fetchedObjects = [self fromDataBaseFetchResult];
    
    // 清空消息數(shù)組里的所有數(shù)據(jù)
    [self.allMessageArray removeAllObjects];
    // 將新的聊天記錄添加到數(shù)組中
    [self.allMessageArray addObjectsFromArray:fetchedObjects];
    
    //將信息加載到tableView上
    [self.messageContent reloadData];
    
    //加載時(shí)滾動(dòng)到最底部
    [self scrollToBottomWithAnimated:YES];
}

//從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)
- (NSArray *)fromDataBaseFetchResult{
    //1.上下文
    NSManagedObjectContext *managerObjectContext =[XMPPManager sharedXMPPManager].managerObjectContext;
    
    //2.創(chuàng)建查詢請(qǐng)求
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
    
    //3.設(shè)置過濾條件(提取當(dāng)前用戶jid,好友jid)
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@ And streamBareJidStr == %@", self.chatToJid.bare,[XMPPManager sharedXMPPManager].stream.myJID.bare];
    fetchRequest.predicate = predicate;
    
    //4.設(shè)置排序(時(shí)間升序)
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];
    fetchRequest.sortDescriptors = @[sortDescriptor];
    
    //5.執(zhí)行請(qǐng)求
    NSError *error = nil;
    NSArray *fetchedResult = [managerObjectContext executeFetchRequest:fetchRequest error:&error];
    
    return fetchedResult;
}

//滾動(dòng)到最底部
- (void)scrollToBottomWithAnimated:(BOOL)animated{
    
    if (animated && [self.messageContent numberOfSections] > 0) {
        
        NSInteger lastSectionIndex = [self.messageContent numberOfSections] - 1;
        NSInteger lastRowIndex = [self.messageContent numberOfRowsInSection:lastSectionIndex] - 1;
        
        if (lastRowIndex > 0) {
            
            NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:lastRowIndex inSection:lastSectionIndex];
            [self.messageContent scrollToRowAtIndexPath:lastIndexPath atScrollPosition: UITableViewScrollPositionBottom animated:animated];
        }
    }
}

#pragma mark UITableViewDataSourceDelegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.allMessageArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (nil == cell) {
        cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleValue1) reuseIdentifier:@"cell"];
    }
    
    XMPPMessageArchiving_Message_CoreDataObject *chatMessage = self.allMessageArray[indexPath.row];
    
    //判斷聊天信息是發(fā)出去的 還是接受進(jìn)來的,在cell上顯示不一樣的樣式
    if (chatMessage.isOutgoing == YES) {
        cell.detailTextLabel.text = chatMessage.body;
        cell.textLabel.text = @"";
    } else {
        cell.textLabel.text = chatMessage.body;
        cell.detailTextLabel.text = @"";
    }
    
    self.messageTextView.text = @"";
    
    return cell;
}
Use Test_XMPP And Spark Chat.png

以上就是將XMPP添加到工程,實(shí)現(xiàn)基于XMPP的登入、注冊(cè)、聊天功能,還有一大批別的功能有待探索,之后將進(jìn)行持續(xù)的更新。

總結(jié)

1.XMPP框架中所有的連接、登入、注冊(cè)、授權(quán)、失去連接等操作的回調(diào)都是通過XMPPStream來管理;
2.使用XMPP開發(fā)即時(shí)通信,大部分功能都是通過回調(diào)XMPPStreamDelegate協(xié)議中的方法來實(shí)現(xiàn)。


聯(lián)系作者:簡(jiǎn)書·DH_Fantasy 新浪微博·DH_Fantasy
版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(CC BY-NC-ND 3.0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容