XMPP即时通讯(代码实现)

1.配置XMPP(XMPPConfig.m)

2.配置XMPPFramework框架

3.创建单例类(XMPPManager.h/XMPPManager.m)管理器

XMPPManager.m:

 

#import "XMPPManager.h"

#import "AppDelegate.h"

//连接服务器的目的

typedef NS_ENUM(NSInteger, ConnectToServerPopurpose)

{

    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

posted @ 2015-11-04 17:56  这个冬天(Winter)  阅读(2622)  评论(0编辑  收藏  举报