XMPP即時通信(代碼實現)

1.配置XMPP(XMPPConfig.m)數組

2.配置XMPPFramework框架服務器

3.建立單例類(XMPPManager.h/XMPPManager.m)管理器app

XMPPManager.m:框架

 

#import "XMPPManager.h"dom

#import "AppDelegate.h"fetch

//鏈接服務器的目的this

typedef NS_ENUM(NSInteger, ConnectToServerPopurpose)atom

{spa

    ConnectToServerPopurposeLogin, //登陸代理

    ConnectToServerPopurposeRegist //註冊

};

@interface XMPPManager ()<XMPPStreamDelegate,XMPPRosterDelegate>

@property (nonatomic, assign) ConnectToServerPopurpose serverPurpose; //鏈接服務器的目的

@property (nonatomic, copy) NSString *loginPassword; //登陸密碼

@property (nonatomic, copy) NSString *registerPassword; //註冊密碼

- (void)connectServer; //鏈接服務器

- (void)disConnectWithServer; //斷開服務器

@end

@implementation XMPPManager

 

static XMPPManager *manager = nil;

+ (XMPPManager *)defaultXMPPManager {

    //gcd once 程序執行期間只執行一次

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        manager = [[XMPPManager alloc] init];

    });

    return manager;

}

//重寫init方法

- (instancetype)init

{

    self = [super init];

    if (self) {

        //建立通訊通道用於和服務器進行鏈接和溝通

        self.stream = [[XMPPStream alloc] init];

        //設置服務器

        self.stream.hostName = kHostName;

        //設置端口號

        self.stream.hostPort = kHostPort;

        //添加代理

        [self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

        

        //建立好友列表倉庫

        XMPPRosterCoreDataStorage *rosterCorDataStorage = [XMPPRosterCoreDataStorage sharedInstance];

        //建立花名冊對象

        self.roster = [[XMPPRoster alloc] initWithRosterStorage:rosterCorDataStorage dispatchQueue:dispatch_get_main_queue()];

        //將花名冊對象添加到stream活動

        [self.roster activate:self.stream];

        //添加代理

        [self.roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

        

        //建立信息歸檔對象

        XMPPMessageArchivingCoreDataStorage *messageArchingCoreStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];

        //建立信息歸檔對象

        self.messageArching = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageArchingCoreStorage dispatchQueue:dispatch_get_main_queue()];

        //添加活動到通訊管道

        [self.messageArching activate:self.stream];

        

        //獲取數據管理器

        self.managerContext = messageArchingCoreStorage.mainThreadManagedObjectContext;

    }

    return self;

}

 

#pragma mark - XMPPRosterDelegate

- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {

    //獲取請求對象的JID

    XMPPJID *requestJID = presence.from;

    //建立提示框

    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"添加好友提醒" message:requestJID.user preferredStyle:UIAlertControllerStyleAlert];

    //建立事件

    UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"添加" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

        //接受好友請求

        [self.roster acceptPresenceSubscriptionRequestFrom:requestJID andAddToRoster:YES];

    }];

    UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒絕" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {

        //拒絕好友請求

        [self.roster rejectPresenceSubscriptionRequestFrom:requestJID];

    }];

    //添加事件

    [alertVC addAction:addAction];

    [alertVC addAction:rejectAction];

    //彈出提示框

    //獲取AppDelegate

    AppDelegate *appDelegate  = [UIApplication sharedApplication].delegate;

    //獲取根視圖控制器

    UIViewController *rootVC = appDelegate.window.rootViewController;

    [rootVC presentViewController:alertVC animated:YES completion:nil];

}

 

//鏈接服務器

- (void)connectServer {

    if ([self.stream isConnected] || [self.stream isConnecting]) {

        //斷開鏈接

        [self disConnectWithServer];

    }

    //創建新的鏈接(30秒超時)

    NSError *error = nil;

    [self.stream connectWithTimeout:30 error:&error];

    if (error) {

        NSLog(@"connect fail");

    }

}

 

//登陸

- (void)loginWithUserName:(NSString *)username password:(NSString *)pw {

    self.loginPassword = pw; //記錄登陸密碼

    self.serverPurpose = ConnectToServerPopurposeLogin; //登陸標識

    //獲取jid 惟一標識

    XMPPJID *myJID = [XMPPJID jidWithUser:username domain:kDomin resource:kResource];

    self.stream.myJID = myJID; //設置JID

    //鏈接服務器

    [self connectServer];

}

//註冊

- (void)registWithUserName:(NSString *)username passeord:(NSString *)pw {

    self.registerPassword = pw; //記錄註冊密碼

    self.serverPurpose = ConnectToServerPopurposeRegist; //註冊標識

    //獲取JID惟一標識

    XMPPJID *myJID = [XMPPJID jidWithUser:username domain:kDomin resource:kResource];

    self.stream.myJID = myJID; //設置JID

    //鏈接服務器

    [self connectServer];

}

 

//添加好友

- (void)addFriend:(NSString *)name {

    //建立JID

    XMPPJID *myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kHostName]];

    //添加好友

    [self.roster subscribePresenceToUser:myJID];

}

//刪除好友

- (void)deleteFriend:(NSString *)name {

    //獲取JID

    XMPPJID *myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kHostName]];

    [self.roster removeUser:myJID];

}

 

//斷開服務器鏈接

- (void)disConnectWithServer {

    //斷開服務器

    [self.stream disconnect];

}

 

#pragma mark - XMPPStreamDelegate

//成功鏈接服務器

- (void)xmppStreamDidConnect:(XMPPStream *)sender {

    NSLog(@"connect success");

    switch (self.serverPurpose) {

        case ConnectToServerPopurposeLogin:

            //登陸

        {

            [self.stream authenticateWithPassword:self.loginPassword error:nil];

        }

            break;

        case ConnectToServerPopurposeRegist:

            //註冊

        {

            [self.stream registerWithPassword:self.registerPassword error:nil];

        }

            break;

        default:

            break;

    }

}

//鏈接超時

- (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender {

    NSLog(@"connect time out");

}

@end

用戶登陸:

#import "LoginViewController.h"

#import "XMPPManager.h"

#import "RosterTableViewController.h"

BOOL isClickButton = YES;

@interface LoginViewController ()<XMPPStreamDelegate>

@property (weak, nonatomic) IBOutlet UITextField *userNameTF;

@property (weak, nonatomic) IBOutlet UITextField *passwordTF;

 

@end

 

@implementation LoginViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    //添加代理

    [[XMPPManager defaultXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

}

 

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

/*

#pragma mark - Navigation

 

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    // Get the new view controller using [segue destinationViewController].

    // Pass the selected object to the new view controller.

}

*/

#pragma mark - handleAction

- (IBAction)handleLogin:(UIButton *)sender {

    isClickButton = YES; //標識點擊了登陸button

    [[XMPPManager defaultXMPPManager] loginWithUserName:self.userNameTF.text password:self.passwordTF.text];

}

 

- (IBAction)handleRegister:(UIButton *)sender {

    

}

 

#pragma mark - XMPPStreamDelegate

//登陸成功

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {

    //上線--更改狀態

    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];

    [[XMPPManager defaultXMPPManager].stream sendElement:presence];

 

    if (isClickButton) {

               //提示

        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"舒適提示" message:@"歡迎回來" preferredStyle:(UIAlertControllerStyleAlert)];

        //添加事件

        UIAlertAction *action = [UIAlertAction actionWithTitle:@"" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

            //獲取從storyBoard中獲取聯繫人列表界面

            RosterTableViewController *rosterVC = [self.storyboard instantiateViewControllerWithIdentifier:@"contact"];

            //傳值

            rosterVC.userName = self.userNameTF.text;

            rosterVC.paassWord = self.passwordTF.text;

            //push

            [self.navigationController pushViewController:rosterVC animated:YES];

        }];

        [alertVC addAction:action];

        //彈出提示

        [self presentViewController:alertVC animated:YES completion:nil];

       //更改BOOL

        isClickButton = NO;

    }

}

 

//登陸失敗

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error {

    //提示

    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"舒適提示" message:@"帳號或密碼錯誤請覈對" preferredStyle:(UIAlertControllerStyleAlert)];

    //添加事件

    UIAlertAction *action = [UIAlertAction actionWithTitle:@"" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

        

    }];

    [alertVC addAction:action];

    //彈出提示

    [self presentViewController:alertVC animated:YES completion:nil];

 

}

@end

用戶註冊:

 

#import "RegisterViewController.h"

#import "XMPPManager.h"

 

@interface RegisterViewController ()<XMPPStreamDelegate>

@property (weak, nonatomic) IBOutlet UITextField *userNameTF;

@property (weak, nonatomic) IBOutlet UITextField *passwordTF;

@property (weak, nonatomic) IBOutlet UITextField *rePasswordTF;

 

@end

 

@implementation RegisterViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    //添加代理

    [[XMPPManager defaultXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

}

 

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

/*

#pragma mark - Navigation

 

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    // Get the new view controller using [segue destinationViewController].

    // Pass the selected object to the new view controller.

}

*/

#pragma mark - handleAction

- (IBAction)handleSubmit:(UIButton *)sender {

    //註冊

    [[XMPPManager defaultXMPPManager] registWithUserName:self.userNameTF.text passeord:self.passwordTF.text];

}

 

- (IBAction)handleCleare:(UIButton *)sender {

    

}

 

#pragma mark - XMPPStreamDelegate

//註冊成功

- (void)xmppStreamDidRegister:(XMPPStream *)sender {

    NSLog(@"register success");

}

//註冊失敗

- (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error {

    NSLog(@"register fail");

}

@end

聯繫人列表

 

#import "RosterTableViewController.h"

#import "RosterCell.h"

#import "ChatTableViewController.h"

#import "XMPPManager.h"

 

@interface RosterTableViewController ()<XMPPRosterDelegate>

@property (nonatomic, strong) NSMutableArray *contacts; //聯繫人數組

@end

 

@implementation RosterTableViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    

    // Uncomment the following line to preserve selection between presentations.

    // self.clearsSelectionOnViewWillAppear = NO;

    

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

//     self.navigationItem.rightBarButtonItem = self.editButtonItem;

    

    self.contacts = [NSMutableArray array]; //建立數組

    //添加代理

    [[XMPPManager defaultXMPPManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

    //再次登陸

    [[XMPPManager defaultXMPPManager] loginWithUserName:self.userName password:self.paassWord];

}

 

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

#pragma mark - Table view data source

 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;

}

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

 

    return self.contacts.count;

}

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    RosterCell *cell = [tableView dequeueReusableCellWithIdentifier:@"roster" forIndexPath:indexPath];

    //獲取對應的聯繫人JID

    XMPPJID *jid = self.contacts[indexPath.row];

    cell.textLabel.text = jid.user;

    return cell;

}

 

#pragma mark - XMPPRosterDelegate

//開始檢索好友

- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {

    NSLog(@"begin search friends");

}

//檢索好友,每執行一次獲取一個好友信息

- (void)xmppRoster:(XMPPRoster *)sender didRecieveRosterItem:(NSXMLElement *)item {

    NSLog(@"%@",[[item attributeForName:@"jid"] stringValue]);

    //獲取JIDStr

    NSString *jidStr = [[item attributeForName:@"jid"] stringValue];

    //獲取JID

    XMPPJID *myJID = [XMPPJID jidWithString:jidStr resource:kResource];

    //防止重複添加好友

    for (XMPPJID *JID in self.contacts) {

        if ([JID.user isEqualToString:myJID.user]) {

            return;

        }

    }

    //放入數組

    [self.contacts addObject:myJID];

    //刷新界面

    [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.contacts.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];

}

//結束檢索好友

- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {

    NSLog(@"end search friends");

}

 

 

#pragma mark - Navigation

 

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    //界面傳值

    //獲取下一個試圖控制器

    ChatTableViewController *chatVC = segue.destinationViewController;

    //獲取cell

    UITableViewCell *cell = sender;

    //獲取下標

    NSInteger index = [self.tableView indexPathForCell:cell].row;

    //獲取對應的對象

    XMPPJID *JID = self.contacts[index];

    chatVC.friendJID = JID;

}

@end

聊天控制器

 

#import "ChatTableViewController.h"

#import "ChatCell.h"

 

@interface ChatTableViewController ()<XMPPStreamDelegate>

@property (nonatomic, strong) NSMutableArray *messageArray; //用來存儲信息

@end

 

@implementation ChatTableViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    

    // Uncomment the following line to preserve selection between presentations.

    // self.clearsSelectionOnViewWillAppear = NO;

    

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

     self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"發送" style:(UIBarButtonItemStyleDone) target:self action:@selector(sendMessage)];

    //添加代理

    [[XMPPManager defaultXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

    //建立數組

    self.messageArray = [NSMutableArray array];

    //獲取本地聊天信息

    [self reloadMessage];

    

}

 

//發送新的消息

- (void)sendMessage

{

    //建立新的信息

    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.friendJID];

    //添加信息體

    [message addBody:@"好比說"];

    [[XMPPManager defaultXMPPManager].stream sendElement:message];

}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

#pragma mark - Table view data source

 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;

}

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return self.messageArray.count;

}

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chat" forIndexPath:indexPath];

    //獲取對應數組元素

    XMPPMessage *message = self.messageArray[indexPath.row];

    if ([message isKindOfClass:[XMPPMessage class]]) {

     //將收到的信息放到左邊,發送的放到右邊

    if ([message.from.user isEqualToString:self.friendJID.user]) {

        cell.textLabel.text = message.body;

        cell.detailTextLabel.text = @"";

    }else {

        cell.textLabel.text = @"";

        cell.detailTextLabel.text = message.body;

    }

    }else {

        //數組中的對象時XMPPMessageArchiving_Mesage_CoreDataObject類型

        XMPPMessageArchiving_Message_CoreDataObject *mes = (XMPPMessageArchiving_Message_CoreDataObject *)message;

        //Outgoing發送,用來判斷消息是接受的 仍是發送的

        if (![mes isOutgoing]) {

            cell.textLabel.text = mes.message.body;

            cell.detailTextLabel.text = @"";

        }else {

            cell.textLabel.text = @"";

            cell.detailTextLabel.text = mes.message.body;

        }

    }

    return cell;

}

 

 

//讀取本地信息

- (void)reloadMessage

{

    //獲取數據管理器

    NSManagedObjectContext *managerContext = [XMPPManager defaultXMPPManager].managerContext;

    //請求對象

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    //實體描述對象

    //XMPPMessageArchiving_Message_CoreDataObject是持久化信息對應的實體類

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:managerContext];

    [fetchRequest setEntity:entity];

    // 查詢條件

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@ AND streamBareJidStr == %@", self.friendJID.bare,[XMPPManager defaultXMPPManager].stream.myJID.bare];

    [fetchRequest setPredicate:predicate];

    // 排序

    //按照時間排序

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"

                                                                   ascending:YES];

    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];

    NSError *error = nil;

    //執行查詢,獲取符合條件的對象

    NSArray *fetchedObjects = [managerContext executeFetchRequest:fetchRequest error:&error];

    if (fetchedObjects == nil) {

        NSLog(@"your content is null for search");

    }

    //將查詢到的本地聊天信息存放到數組中

    [self.messageArray addObjectsFromArray:fetchedObjects];

    //刷新數據

    [self.tableView reloadData];

}

 

//展現信息

- (void)showMessageWithMessage:(XMPPMessage *)message {

    //將信息放入數組

    [self.messageArray addObject:message];

    //刷新數據

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messageArray.count - 1 inSection:0];

    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];

    

    //滑動tableView到對應的cell

    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:(UITableViewScrollPositionBottom) animated:YES];

}

 

#pragma mark - XMPPSteamDelegate

 

//接收信息

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {

    NSLog(@"%@",message.body);

    //只獲取當前好友的聊天信息

    if ([message.from.user isEqualToString:self.friendJID.user]) {

        //展現信息

        [self showMessageWithMessage:message];

    }

}

//發送信息

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message {

    [self showMessageWithMessage:message];

}

/*

#pragma mark - Navigation

 

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    // Get the new view controller using [segue destinationViewController].

    // Pass the selected object to the new view controller.

}

*/

 

@end

相關文章
相關標籤/搜索