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