Fork me on GitHub

项目开发--------XMPP即时通讯

一.基本框架结构:

 

StroyBoard的基本页面搭建:

 

 

 

二.个功能代码块的是实现

 

LoginViewController.m文件(登录页面的基本配置)

#import "LoginViewController.h"
#import "XMPPManager.h"

@interface LoginViewController ()<XMPPStreamDelegate>
@property (weak, nonatomic) IBOutlet UITextField *userNameTes;
@property (weak, nonatomic) IBOutlet UITextField *passwordTes;

@end

@implementation LoginViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //添加代理方法
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()]; 
}

#pragma mark ---实现XMPPStreamDelegate中的代理方法
//认证登录成功的代理方法 -(void)xmppStreamDidAuthenticate:(XMPPStream *)sender { NSLog(@"登录成功"); //登陆成功进入好友聊天页面 [self dismissViewControllerAnimated:YES completion:nil]; } //认证登录失败的代理方法 -(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error { NSLog(@"登录失败%@",error); } - (IBAction)loginWithUserNameAndPassword:(id)sender { //登录 [[XMPPManager sharedXMPPManager] loginWithUserName:self.userNameTes.text passWord:self.passwordTes.text]; } @end

 

 

RegisterViewController.m文(注册页面的基本配置)

#import "RegisterViewController.h"
#import "XMPPManager.h"

@interface RegisterViewController () <XMPPStreamDelegate>
@property (weak, nonatomic) IBOutlet UITextField *userNameTes;
@property (weak, nonatomic) IBOutlet UITextField *passwordTes;

@end

@implementation RegisterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   //给stream 设置代理监测注册是否成功
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

#pragma mark ---实现XMPPStreamDelegate中的代理方法
//注册成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender { NSLog(@"注册成功"); //注册成功 进入登陆页面 [self.navigationController popViewControllerAnimated:YES]; } //注册失败 -(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error { NSLog(@"注册失败"); } - (IBAction)registerWithUserNameAndPassword:(id)sender { //注册 [[XMPPManager sharedXMPPManager] registerWithUserName:self.userNameTes.text passWord:self.passwordTes.text]; } @end

 

 XMPPManager.h文件 (主要功能实现文件)

#import <Foundation/Foundation.h>
#import "XMPPFramework.h"

@interface XMPPManager : NSObject<XMPPStreamDelegate>

//声明一个'通道'属性 为了其他类中能够访问到
@property(nonatomic,strong)XMPPStream *stream;

//与好友有关的操作
@property(nonatomic,strong)XMPPRoster *roster;

//与聊天信息有关的  聊天信息托管上下文
@property(nonatomic,strong)NSManagedObjectContext *context;

//信息归档  聊天信息归档对象
@property(nonatomic,strong)XMPPMessageArchiving *messageAchiving;

#pragma mark ---获取一个单例对象
+(XMPPManager *)sharedXMPPManager;

#pragma mark ---登录
-(void)loginWithUserName:(NSString *)userName passWord:(NSString *)passWord;

#pragma mark ---注册
-(void)registerWithUserName:(NSString *)userName passWord:(NSString *)passWord;

#pragma mark ---删除好友
-(void)removeFriendName:(NSString *)name;


@end

 

XMPPManager.m文件(主要功能实现文件)

#import "XMPPManager.h"

//枚举 typedef NS_ENUM(NSUInteger,connectToSeverType){ connectToSeverLogin, connectToSeverRegister }; //枚举 //typedef enum : NSUInteger { // <#MyEnumValueA#>, // <#MyEnumValueB#>, // <#MyEnumValueC#>, //} <#MyEnum#>; @interface XMPPManager () //登录用户名 @property(nonatomic,strong)NSString *LoginuserName; //登录密码 @property(nonatomic,strong)NSString *loginPassWord; //注册名 @property(nonatomic,strong)NSString *registerUserName; //注册密码 @property(nonatomic,strong)NSString *registerPassWord; //用来表示访问服务器的目的(登录或注册) @property(nonatomic,assign)connectToSeverType connectPurpose; @end @implementation XMPPManager #pragma mark ---获取一个单例对象 +(XMPPManager *)sharedXMPPManager { static XMPPManager *manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[XMPPManager alloc] init]; }); return manager; } //重写XMPPManager的init方法,是为了创建 通道(xmppStream) //init方法 - (instancetype)init { self = [super init]; if (self) { //初始化通道 self.stream = [[XMPPStream alloc] init]; //指定其连接的服务器IP地址 self.stream.hostName = kHostName; //指定服务器端口 默认为5222 //端口号 是制定当前连接的是什么服务 self.stream.hostPort = kHostPort; //给stream设置代理,来检测链接状态和认证状态 [self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()]; //初始化管理好友对象roster(与好友相关的操作) XMPPRosterCoreDataStorage *storage = [XMPPRosterCoreDataStorage sharedInstance]; self.roster = [[XMPPRoster alloc] initWithRosterStorage:storage dispatchQueue:dispatch_get_main_queue()]; //在管道上激活对象 [self.roster activate:self.stream]; //初始化messageAchiving对象 XMPPMessageArchivingCoreDataStorage *messageStroge = [XMPPMessageArchivingCoreDataStorage sharedInstance]; self.messageAchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageStroge dispatchQueue:dispatch_get_main_queue()]; //激活 [self.messageAchiving activate:self.stream]; //托管上下文对象 self.context = messageStroge.mainThreadManagedObjectContext; } return self; } #pragma mark ---登录 -(void)loginWithUserName:(NSString *)userName passWord:(NSString *)passWord { self.loginPassWord = userName; self.LoginuserName = userName; //此时将枚举属性的值设为connectToSeverLogin self.connectPurpose = connectToSeverLogin; //链接 [self linkConnect]; } //链接的方法 -(void)linkConnect { //在真正链接之前,需要告诉服务器你是谁, //XMPPJID在Xmpp中身份的唯一标识 XMPPJID *myJID = nil; switch (self.connectPurpose) { case connectToSeverLogin:{ myJID = [XMPPJID jidWithUser:self.LoginuserName domain:kDomin resource:kResource]; break; } case connectToSeverRegister:{ myJID = [XMPPJID jidWithUser:self.registerUserName domain:kDomin resource:kResource]; break; } default: break; } // XMPPJID *myJID = [XMPPJID jidWithUser:self.LoginuserName domain:kDomin resource:kResource]; //将自己的JID设置到Stream通道上 self.stream.myJID = myJID; //开始链接 if ([self.stream isConnected] || [self.stream isConnecting]) { //先断开 [self disconnectToSever]; } //连接到服务器 NSError *error = nil; [self.stream connectWithTimeout:30.0 error: &error]; if (error) { NSLog(@"error:%@",error); } } //断开链接 -(void)disconnectToSever{ //设置下线状态元素 XMPPPresence *pressence = [XMPPPresence presenceWithType:@"unavailable"]; //将stream上的状态改为下线 [self.stream sendElement:pressence]; //断开链接 [self.stream disconnect]; } #pragma mark ---注册 -(void)registerWithUserName:(NSString *)userName passWord:(NSString *)passWord { self.registerPassWord = passWord; self.registerUserName = userName; //将枚举值设为注册 self.connectPurpose = connectToSeverRegister; //链接 [self linkConnect]; } #pragma mark ---XMPPStreamDelegate的方法 //链接成功 -(void)xmppStreamDidConnect:(XMPPStream *)sender { NSLog(@"连接成功"); //进行认证 switch (self.connectPurpose) { case connectToSeverLogin: //如果链接的目的是登录,这里需要一个登录的passWord [self.stream authenticateWithPassword:self.loginPassWord error:nil]; break; case connectToSeverRegister: //如果链接的目的是注册,这里需要一个注册的passWord [self.stream registerWithPassword:self.registerPassWord error:nil]; break; default: break; } } //链接失败 -(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error { NSLog(@"连接失败%@",error); } //链接超时 -(void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { NSLog(@"链接超时"); } //认证成功 -(void)xmppStreamDidAuthenticate:(XMPPStream *)sender { NSLog(@"认证成功"); //设置上线状态 XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"]; [self.stream sendElement:presence]; } #pragma mark ---删除好友 -(void)removeFriendName:(NSString *)name { //根据username和域名,创建一个jid XMPPJID *friendJid = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kDomin ]]; //取消对好友的监听 [self.roster unsubscribePresenceFromUser:friendJid]; //将好友移除 [self.roster removeUser:friendJid]; } @end

 

 

 RosterTableViewController.m文件(好友界面功能的实现)

#import "RosterTableViewController.h"
#import "XMPPManager.h"
#import "ChatTableViewController.h"

@interface RosterTableViewController ()<XMPPRosterDelegate,UIAlertViewDelegate,XMPPStreamDelegate>

//所有好友
@property(nonatomic,strong)NSMutableArray *allRoster;

//请求者的jid
@property(nonatomic,strong)XMPPJID *requestJid;

@end

//存放所有的好友
@implementation RosterTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //对数组进行初始化
    self.allRoster = [NSMutableArray array];
    
    //通过代理协议,获取好友
    [[XMPPManager sharedXMPPManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

    //监听好友的状态  通过代理协议的形式
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    
}

#pragma mark ---实现XMPPRosterDelegate协议中的方法
//开始获取好友
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender
{
    NSLog(@"开始获取好友");

}
//正在获取好友  有几个好友这个放法就会执行几次
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
{
    NSLog(@"%@",item);
    //to 订阅了别人
    //from 别人订阅了你
    //both 互为好友
    NSString *subscription = [[item attributeForName:@"subscription"] stringValue];
    if ([subscription isEqualToString:@"from"] || [subscription isEqualToString:@"both"]) {
        //获取好友的jid字符串
        NSString *jidStr = [[item attributeForName:@"jid" ] stringValue];
        //获取好友的jid
        XMPPJID *frienfJid = [XMPPJID jidWithString:jidStr];
        
        //如果之前加过了,表示好友已经存在
        if ([_allRoster containsObject:frienfJid ]) {
            return;
        }
        
        //将好友对象(frienfJid) 添加到数组中
        [self.allRoster addObject:frienfJid];
        
        //插入行
        //插入到最后一行
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.allRoster.count - 1 inSection:0];
        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        
    }

}


//结束获取好友
-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender
{

    NSLog(@"好友获取结束");
}

//获取好友请求
-(void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
{

    NSLog(@"获取好友请求!");
    
    self.requestJid =  presence.from;
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"好友请求" message:_requestJid.user delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
    [alert show];
    
    
}

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch (buttonIndex) {
        case 0:
            //点击拒绝按钮
          [[XMPPManager sharedXMPPManager].roster rejectPresenceSubscriptionRequestFrom:self.requestJid];
            break;
        case 1:
            //接受请求按钮
            [[XMPPManager sharedXMPPManager].roster acceptPresenceSubscriptionRequestFrom:self.requestJid andAddToRoster:NO];
            break;
        default:
            break;
    }
    
    self.requestJid = nil;
}

#pragma mark --实现XMPPStreamDelegate中的方法
//监听好友的状态
-(void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
    //监测好友的状态
    NSString *presenceType =  presence.type;
    if ([presenceType isEqualToString:@"available"]) {
        NSLog(@"好友在线状态!");
    }  else if ([presenceType isEqualToString:@"unavailable"])
        NSLog(@"好友处于下线状态!");

}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    return self.allRoster.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"roster" forIndexPath:indexPath];
    
    XMPPJID *friendJid = _allRoster[indexPath.row];
    cell.textLabel.text = friendJid.user;
    
    return cell;
}



// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return NO if you do not want the specified item to be editable.
    return YES;
}



// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        
        //删除数据源
        XMPPJID *jid = _allRoster[indexPath.row];
        [[XMPPManager sharedXMPPManager]removeFriendName:jid.user];
        [self.allRoster removeObjectAtIndex:indexPath.row];
        
        
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}


#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 {

    //获取点击的cell
    UITableViewCell *cell = (UITableViewCell *)sender;
   NSIndexPath *indexPah =  [self.tableView indexPathForCell:cell];
    
    ChatTableViewController *chatVC =  (ChatTableViewController *)segue.destinationViewController;
    chatVC.chatToJid = _allRoster[indexPah.row];
    
        
}


@end

 

ChatTableViewController.h文件(聊天界面功能的实现)

 

#import <UIKit/UIKit.h>
#import "XMPPManager.h"

@interface ChatTableViewController : UITableViewController


@property(nonatomic,strong)XMPPJID *chatToJid;

@end

 

 ChatTableViewController.m文件(聊天界面功能的实现)

#import "ChatTableViewController.h"
#import "XMPPManager.h"

@interface ChatTableViewController ()<XMPPStreamDelegate>

//信息数组
@property(nonatomic,strong)NSMutableArray *allMessages;
- (IBAction)sendMessage:(UIBarButtonItem *)sender;

@end

@implementation ChatTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //初始化数组
    self.allMessages = [NSMutableArray array];
    
    //设置代理
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    //显示历史记录
    [self parseMesage];
    
}

#pragma mark --实现协议中的方法
//已经接受好友发送的信息
-(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{

    NSLog(@"已经接受信息:%@",message);
    
    [self parseMesage];
}

-(void)parseMesage
{
    //查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:[XMPPManager sharedXMPPManager].context];
    [fetchRequest setEntity:entity];
    // Specify criteria for filtering which objects to fetch
    //谓词  查询条件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@ AND streamBareJidStr == %@", _chatToJid.bare,[XMPPManager sharedXMPPManager].stream.myJID.bare];
    [fetchRequest setPredicate:predicate];
    // Specify how the fetched objects should be sorted
    //排序
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"
                                                                   ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
    
    NSError *error = nil;
    NSArray *fetchedObjects = [[XMPPManager sharedXMPPManager].context executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"没有聊天记录!");
    } else {
      
        //清空之前的聊天记录
        [self.allMessages removeAllObjects];
        
        [self.allMessages addObjectsFromArray:fetchedObjects];
        
        [self.tableView reloadData];
     
        
        if (_allMessages.count == 0) {
            return;
        }
        
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.allMessages.count -1 inSection:0];
        [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
    
    }
}
#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Incomplete implementation, return the number of sections
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of rows
    return _allMessages.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chat" forIndexPath:indexPath];


    XMPPMessageArchiving_Message_CoreDataObject *message = _allMessages[indexPath.row];
    if (message.isOutgoing == YES) {  //自己发的
        cell.detailTextLabel.text = message.body;
        cell.textLabel.text = @"";
    } else {
        cell.textLabel.text = message.body;
        cell.detailTextLabel.text = @"";
    }
 
    return cell;
}


#pragma mark --给好友发送消息
- (IBAction)sendMessage:(UIBarButtonItem *)sender {
    
    //发送给谁消息(Jid)
    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJid];
    //设置发送内容
    [message addBody:@"你好!"];
    //开始发送
    [[XMPPManager sharedXMPPManager].stream sendElement:message];
  
}
//信息发送成功
-(void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{
    NSLog(@"信息发送成功:%@",message);
    [self parseMesage];

}
//信息发送失败
-(void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error
{
    NSLog(@"信息发送失败:%@",error);

}

@end

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
posted @ 2015-11-09 21:10  DengHuiCheng  阅读(316)  评论(1编辑  收藏  举报