//
// ViewController.m
// XMPP_lesson
//
// Created by 王聪 on 14/8/20.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import "ViewController.h"
#import "XMPPHelper.h"
@interface ViewController ()<XMPPStreamDelegate>
@property (weak, nonatomic) IBOutlet UITextField *userTF;//用户名TF
@property (weak, nonatomic) IBOutlet UITextField *passWordTF;//密码TF
@end
@implementation ViewController
//登陆方法
- (IBAction)loginAction:(UIButton *)sender {
//发起登陆操作
[[XMPPHelper sharedXMPPHelper]loginServerWithUserName:self.userTF.text password:self.passWordTF.text];
}
#pragma mark -- 密码验证成功回调方法
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
[self performSegueWithIdentifier:@"segue" sender:nil];
}
//注册方法
- (IBAction)registerAction:(UIButton *)sender {
[[XMPPHelper sharedXMPPHelper] registerServerWithUserName:self.userTF.text password:self.passWordTF.text];
}
- (void)viewDidLoad {
[super viewDidLoad];
//再把当前的对象添加为xmppStream的代理
[[XMPPHelper sharedXMPPHelper].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
//
// FriendListViewController.m
// XMPP_lesson
//
// Created by 王聪 on 15/8/21.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import "FriendListViewController.h"
#import "XMPPHelper.h"
#import "ChatTableViewController.h"
@interface FriendListViewController ()<UITableViewDelegate, UITableViewDataSource, XMPPRosterDelegate, UIAlertViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
//shujvyuan shuxing ,laizhanshixinxi
@property (strong, nonatomic)NSMutableArray *dataSource;
@end
@implementation FriendListViewController
//添加好友
- (IBAction)addFriend:(UIBarButtonItem *)sender {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"添加好友" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
//设置alerView的样式, 带一个输入框
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
}
#pragma mark - alertView的代理方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
//如果点击的时确定
if (buttonIndex) {
//获取alertView上面输入框的值
UITextField *alerTF = [alertView textFieldAtIndex:0];
NSString *userName = alerTF.text;
//封装成XMPPJID
XMPPJID *friendJID = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];
//添加好友
[[XMPPHelper sharedXMPPHelper].xmppRoster addUser:friendJID withNickname:nil];
}
}
#pragma mark -- tableView代理方法和dataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.dataSource.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
XMPPJID *jid = self.dataSource[indexPath.row];
cell.textLabel.text = jid.user;
return cell;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 初始化数组
self.dataSource = [NSMutableArray array];
//添加当前对象为xmppRoster的代理对象
[[XMPPHelper sharedXMPPHelper].xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
//调整视图的边距, 这个是ios7之后的方法, ios6不能用
self.edgesForExtendedLayout = UIRectEdgeNone;
}
#pragma mark -- xmppRosterdelegate 的代理方法
//好友花名册开始检索好友的代理方法
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender{
}
//检索到好友信息的代理方法
- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item{
//xml解析中获取节点中的属性
NSString *jidStr = [[item attributeForName:@"jid"] stringValue];
//xml解析获取节点中的订阅属性
NSString *subscriptionStatus = [[item attributeForName:@"subscription"] stringValue];
if ([subscriptionStatus isEqualToString:@"both"]) {
//封装成JID对象
XMPPJID *friendJID = [XMPPJID jidWithString:jidStr resource:kResource];
if ([self.dataSource containsObject:friendJID]) {
return;
}
//插入数据源数组
[self.dataSource addObject:friendJID];
//再插单元格
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.dataSource.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
//让tableView滚动
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.dataSource.count - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
//xmppRoster结束检索
- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender{
}
- (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.
ChatTableViewController *chatVC = segue.destinationViewController;
//找到点击的cell
UITableViewCell *cell = sender;
//获取点击cell的下标
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
chatVC.friendJID = self.dataSource[indexPath.row];
}
@end
//
// ChatTableViewController.h
// XMPP_lesson
//
// Created by 王聪 on 15/8/21.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import <UIKit/UIKit.h>
@class XMPPJID;
@interface ChatTableViewController : UITableViewController
//声明当前聊天好友的JID属性
@property (nonatomic, strong)XMPPJID *friendJID;
@end
//
// ChatTableViewController.m
// XMPP_lesson
//
// Created by lanouhn on 15/8/21.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import "ChatTableViewController.h"
#import "XMPPHelper.h"
@interface ChatTableViewController ()<XMPPStreamDelegate>
@property (strong, nonatomic)NSMutableArray *dataSource;
@end
@implementation ChatTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
//添加当前为xmppStream的代理对象
[[XMPPHelper sharedXMPPHelper].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//初始化数组
self.dataSource = [NSMutableArray array];
//调用检索本地聊天记录的方法
[self seacherMessageBetweenFriend];
}
//检索本地聊天记录
- (void)seacherMessageBetweenFriend{
NSManagedObjectContext *context = [XMPPHelper sharedXMPPHelper].managedContext;
//创建查询请求对象
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
//设置检索条件
//1.创建谓词
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@ and streamBareJidStr == %@", self.friendJID.bare, [XMPPHelper sharedXMPPHelper].xmppStream.myJID.bare];
request.predicate = predicate;
//2.设置排序
NSSortDescriptor *description = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];
request.sortDescriptors = @[description];
//执行这个查询请求
NSArray *resultArray = [context executeFetchRequest:request error:nil];
//加入数据源
[self.dataSource addObjectsFromArray:resultArray];
//刷新单元格
[self.tableView reloadData];
}
//发送消息的方法
- (IBAction)sendMessage:(UIBarButtonItem *)sender {
//创建消息对象
XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.friendJID];
//给消息添加内容
[message addBody:@"你懂得http://cl.ttum.pw/htm_data/7/1508/1607032.html"];
//3. *发送消息
[[XMPPHelper sharedXMPPHelper].xmppStream sendElement:message];
}
- (void)showNewMessage:(XMPPMessage *)message{
//插入数据源
[self.dataSource addObject:message];
//插入单元格
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.dataSource.count - 1 inSection:0];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
//tableView
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
#pragma mark -- 与聊天相关的xmppStream的代理回调方法
//收到消息的代理回调
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
//判断消息发送者
XMPPJID *friendJID = message.from;
if ([self.friendJID isEqualToJID:friendJID]) {
[self showNewMessage:message];
}
}
//发送消息成功的代理回调方法
- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message{
[self showNewMessage: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.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cells" forIndexPath:indexPath];
XMPPMessage *message = self.dataSource[indexPath.row];
if ([message isKindOfClass:[XMPPMessage class]]) {
if ([message.from isEqualToJID:self.friendJID]) {
cell.textLabel.textColor = [UIColor redColor];
}else{
cell.textLabel.textColor = [UIColor blackColor];
}
cell.textLabel.text = message.body;
return cell;
}
else{
XMPPMessageArchiving_Message_CoreDataObject *message = self.dataSource[indexPath.row];
//判断消息是不是登录用户发送的(isOutdoing是自己发送的)
if (![message isOutgoing]) {
cell.textLabel.textColor = [UIColor redColor];
}else{
cell.textLabel.textColor = [UIColor blackColor];
}
cell.textLabel.text = message.body;
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) {
// Delete the row from the data source
[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
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#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
//
// XMPPHelper.h
// XMPP_lesson
//
// Created by lanouhn on 15/8/20.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "XMPPFramework.h"
@interface XMPPHelper : NSObject<XMPPStreamDelegate, XMPPRosterDelegate>
//通信介质(管道对象), 在整个xmpp框架中非常重要, 所有的服务都是建立在它之上
@property (nonatomic, strong)XMPPStream *xmppStream;
//声明好友花名册属性 (负责所有与好友相关的操作)
@property (nonatomic, strong) XMPPRoster *xmppRoster;
//声明消息归档对象 (这个对象负责消息的归档)
@property (nonatomic, strong)XMPPMessageArchiving *messageArchiving;
//声明被管理对象上下文, 方便外界查询 (因为我们要查询聊天记录)
@property (strong , nonatomic)NSManagedObjectContext *managedContext;
+ (XMPPHelper *)sharedXMPPHelper;
//1.登陆方法
- (void)loginServerWithUserName:(NSString *)userName password:(NSString *)password;
//2.注册方法
- (void)registerServerWithUserName:(NSString *)userName password:(NSString *)password;
@end
//
// XMPPHelper.m
// XMPP_lesson
//
// Created by 王聪 on 15/8/20.
// Copyright (c) 2015年 Congwang. All rights reserved.
//
#import "XMPPHelper.h"
#import "FriendListViewController.h"
//定义枚举
typedef NS_ENUM(NSInteger, ThePurposeOfLinkServer)
{
LoginPurpose = 0,
RegisterPurpose = 1
};
//延展
@interface XMPPHelper ()
//声明密码属性, 用于验证密码的验证
@property (nonatomic, copy)NSString *password;
//声明枚举属性来区分登陆和注册
@property (nonatomic, assign)ThePurposeOfLinkServer purpose;
@end
@implementation XMPPHelper
//xmpp服务助手
+ (XMPPHelper *)sharedXMPPHelper{
static XMPPHelper *xmppHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
xmppHelper = [[XMPPHelper alloc] init];
});
return xmppHelper;
}
//重写init方法, 在该方法中完成一些xmpp中一些类的基本设置
-(instancetype)init{
self = [super init];
if (self) {
//1.xmppStream的设置
//1.1创建xmppStream对象
self.xmppStream = [[XMPPStream alloc] init];
//指定xmppStream链接的服务器的地址
self.xmppStream.hostName = kHostName;
//指定xmppStream链接服务器的端口号
self.xmppStream.hostPort = kHostPort;
//添加xmppStream的代理对象
//1.指定代理对象
//2.指定GCD队列, 我们选择主线程
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//完成xmppRoster的一些基本配置
//1.创建好友存储对象
XMPPRosterCoreDataStorage *storage = [XMPPRosterCoreDataStorage sharedInstance];
//2.创建好友花名册管理对象
self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:storage dispatchQueue:dispatch_get_main_queue()];
//3.添加代理
[self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
//4.* 打开xmppRoster服务
[self.xmppRoster activate:self.xmppStream];
//创建消息存储对象
XMPPMessageArchivingCoreDataStorage *archivingStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
//给数据管理器属性赋值, 方便外界的访问
self.managedContext = archivingStorage.mainThreadManagedObjectContext;
//创建信息归档对象
self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:archivingStorage dispatchQueue:dispatch_get_main_queue()];
//添加代理, 不用遵守代理协议
[self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
//**启动该服务
[self.messageArchiving activate:self.xmppStream];
}return self;
}
//发起请求方法
- (void)connectToServer{
if (self.xmppStream.isConnected || self.xmppStream.isConnecting) {
//告诉服务器我们xia线了
//1.创建XMPPPresence对象,unavailable就是xia线了
XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
//2.发送xia线状态
[self.xmppStream sendElement:presence];
//断开链接
[self.xmppStream disconnect];
}
//发起链接请求
[self.xmppStream connectWithTimeout:-1 error:nil];
}
/**
* 登陆方法的实现
*/
- (void)loginServerWithUserName:(NSString *)userName password:(NSString *)password{
//给密码属性赋值
self.password = password;
//geimudi meijv fuzhi
self.purpose = LoginPurpose;
// 登陆方法
//1.创建登陆方
XMPPJID *userJID = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];
//2.告诉xmppStream登陆方是谁
self.xmppStream.myJID = userJID;
//发起链接请求
[self connectToServer];
}
/**
* 注册方法的实现
*/
- (void)registerServerWithUserName:(NSString *)userName password:(NSString *)password{
//给密码赋值
self.password = password;
//给目的属性赋值
self.purpose = RegisterPurpose;
//注册第一步
//1.创建注册方法
XMPPJID *registerUser = [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];
//2.谁在注册
self.xmppStream.myJID = registerUser;
//3.发起注册
[self connectToServer];
}
#pragma mark -- xmppStream的代理方法
//管道对象将要链接的时候出发
- (void)xmppStreamWillConnect:(XMPPStream *)sender{
NSLog(@"%s, %d",__FUNCTION__, __LINE__ );
}
//xmppStream基于的socket管道链接上的时候触发
- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket{
NSLog(@"%s, %d",__FUNCTION__, __LINE__ );
}
//管道对象已经链接上出发的方法
- (void)xmppStreamDidConnect:(XMPPStream *)sender{
if (!self.purpose) {
//验证密码
[self.xmppStream authenticateWithPassword:self.password error:nil];
}else{
//注册账号
[self.xmppStream registerWithPassword:self.password error:nil];
}
NSLog(@"%s, %d",__FUNCTION__, __LINE__ );
}
//对该账号进行验证
//验证成功的方法
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
//告诉服务器我们上线了
//1.创建XMPPPresence对象,available就是上线了
XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];
//2.发送上线状态
[self.xmppStream sendElement:presence];
//提示框
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"登陆成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
NSLog(@"成功");
}
//验证失败的方法
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
//提示框
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"登陆失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
NSLog(@"失败");
}
//注册账号的方法
//注册成功的方法
- (void)xmppStreamDidRegister:(XMPPStream *)sender{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"注册成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
}
//注册失败的方法
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"注册失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
}
#pragma mark -- xmppRoster代理方法
//接收到好友的添加请求
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
//获取请求添加好友的JID
XMPPJID *friendJID = presence.from;
//我们同意添加为好友
[self.xmppRoster acceptPresenceSubscriptionRequestFrom:friendJID andAddToRoster:YES];
// //拒绝好友的添加请求
// [self.xmppRoster rejectPresenceSubscriptionRequestFrom:friendJID];
}
@end