上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramework的使用。本篇博客中用到了Spark做测试,当然也少不了Openfire服务器,在这就不详述Openfire的安装过程了(网上的教程还是蛮多的),Openfire的安装仅需要一个数据库的支持,本篇是用的MySql数据库。当然这不是本篇的重点。

废话少说,切入今天的正题。今天要给之前的微信加入登陆,获取好友列表,聊天(发送文字,表情,图片,声音等功能),最近联系人等。在博客的开头还是先来几张图来介绍一下功能,然后再给出核心代码的实现。

  

一、功能模块截图

1.登陆和获取好友列表

登陆的过程就是连接用XMPPFramework连接Openfire的过程,如果用户登陆过,就从UserDefault里获取用户的JID和密码自动连接,如果用户没有登陆过则登陆。获取好友列表也是通过XMPPFramework中的Roster来获取的,运行截图如下:

  

2、内容发送处理

好友点击去就是聊天页面,聊天时如果是发送的图片或者声音,先存储到服务器上存储,服务器会返回存储路径然后再把URL发送给接收方,接收方再下载

(1)如果是发送的文字,把文字转成属性字符串,然后再转成NSData,最后转成字符串放在Message的Body中进行发送,下面是用Spark做接收端做得测试,截图如下:

  

    

(2)发送图片,把图片的存储路径发送给对方,让对方从服务器上下载。截图如下:

  

    

(3)发送声音和图片一样都是发送URL,截图如下:

  

二、代码实现部分

上面的部分是允许的效果截图,从截图上是不难看出功能点的。图就先贴到这吧,下面给出核心代码的实现。

1.使用XMPPFramework前的准备

获取XmppStream和激活要用的组件,在AppDelegate添加代码。以后要用xmppStream时,要通过AppDelegate获取。下面的代码是在AppDelegate.m中进行的相关组件的初始化,代码如下

(1)实例化XMPPStream

    //创建xmppstream
self.xmppStream = [[XMPPStream alloc]init];

(2)创建重连组件,并在xmppStream中激活

   //创建重写连接组件
xmppReconnect= [[XMPPReconnect alloc] init];
//使组件生效
[xmppReconnect activate:self.xmppStream];

(3)创建message部分的内容,接受的消息我们保存在本地数据库中,我们要显示的时候是从数据库中获取的。在初始化消息组件的时候,要指定保存策略,一般可以选的是CoreData还是内存。指定完保存策略后实例化Message是要关联保存策略,之后也是需要在XMPPStream中进行激活的,最后要获取CoreData的上下文。代码如下:

     //创建消息保存策略(规则,规定)
messageStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
//用消息保存策略创建消息保存组件
xmppMessageArchiving = [[XMPPMessageArchiving alloc]initWithMessageArchivingStorage:messageStorage];
//使组件生效
[xmppMessageArchiving activate:self.xmppStream];
//提取消息保存组件的coreData上下文
self.xmppManagedObjectContext = messageStorage.mainThreadManagedObjectContext;

(4),初始化获取好友列表的相关组件并指定保存策略,和上面的代码步骤极为相似。这也能看出来在XMPPFramework中进行组件的初始化步骤是差不多的。下面我们设定自动获取花名册,代码如下:

     xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage];
//自动获取用户列表
xmppRoster.autoFetchRoster = YES;
xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES; [xmppRoster activate:self.xmppStream];
self.xmppRosterManagedObjectContext = xmppRosterStorage.mainThreadManagedObjectContext;

2.登陆模块的实现

登陆时就是用户输入JID和Password,然后连接服务器和验证密码,如果认证成功则跳转到好友列表才Controller,同时把JID和Password存储到UserDefaults中便于下次自动连接。下面的代码就是登陆部分的代码(LoginViewController.m):

(1).通过应用代理获取XMPPStream,并注册回调,代码如下:

 -(void) initXmpp
{
//获取应用的xmppSteam(通过Application中的单例获取)
UIApplication *application = [UIApplication sharedApplication];
id delegate = [application delegate];
self.xmppStream = [delegate xmppStream]; //注册回调
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

(2).创建JID连接服务器

 //连接服务器
-(void) xmppConnect
{
if (![self.userNameTextFiled.text isEqualToString:@""] && self.userNameTextFiled.text != nil)
{
//1.创建JID
XMPPJID *jid = [XMPPJID jidWithUser:self.userNameTextFiled.text domain:MY_DOMAIN resource:@"iPhone"]; //2.把JID添加到xmppSteam中
[self.xmppStream setMyJID:jid]; //连接服务器
NSError *error = nil;
[self.xmppStream connectWithTimeout: error:&error];
if (error)
{
NSLog(@"连接出错:%@",[error localizedDescription]);
} }
else
{
UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"用户名不能为空" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil];
[alter show];
}
}

(3).连接成后需要认证密码,代码如下:

 //连接后的回调
-(void)xmppStreamDidConnect:(XMPPStream *)sender
{
if (![self.passwordTextFiled.text isEqualToString:@""] && self.passwordTextFiled.text != nil)
{
//连接成功后认证用户名和密码
NSError *error = nil;
[self.xmppStream authenticateWithPassword:self.passwordTextFiled.text error:&error];
if (error)
{
NSLog(@"认证错误:%@",[error localizedDescription]);
}
}
else
{
UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"密码不能为空" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil];
[alter show];
}
}

(4)密码认证成功后的回调

 //认证成功后的回调
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
NSLog(@"登陆成功"); //密码进入userDefault
NSUserDefaults *userDefult = [NSUserDefaults standardUserDefaults];
[userDefult setObject:self.userNameTextFiled.text forKey:@"username"];
[userDefult setObject:self.passwordTextFiled.text forKey:@"password"]; //设置在线状态
XMPPPresence * pre = [XMPPPresence presence];
[self.xmppStream sendElement:pre]; UIStoryboard *storybard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UIViewController *viewController = [storybard instantiateViewControllerWithIdentifier:@"mainController"];
[self presentViewController:viewController animated:YES completion:^{
}];
}

(5)密码认证失败后的回调

 //认证失败的回调
-(void)xmppStream:sender didNotAuthenticate:(DDXMLElement *)error
{
NSLog(@"认证失败");
}

(6),二次登陆自动连接代码:

    // 如果已登录就直接填充密码登陆
NSUserDefaults *userDefult = [NSUserDefaults standardUserDefaults]; NSString *userName = [userDefult objectForKey:@"username"];
NSString *password = [userDefult objectForKey:@"password"];
NSLog(@"%@,%@",userName,password);
if (userName != nil && password != nil && ![userName isEqualToString:@""] && ![password isEqualToString:@""])
{
self.userNameTextFiled.text = userName;
self.passwordTextFiled.text = password;
[self xmppConnect];
}

    

3.获取好友列表的XMPPFramework的代码实现

在获取用户列表的代码中就会用到我们之前注册的Roster的内容,因为我们在实例化Roster的时候指定的保存策略是用CoreData进行保存的,并且是自动获取好友列表。所以在获取好友列表的TableViewController中我们只需要通过CoreData来获取好友列表即可。下面将给出获取好友列表的核心代码:

(1),获取Roster对应的上下文,用于获取存储在Roster相应实体中的数据

     //获取Roster的上下文
UIApplication *application = [UIApplication sharedApplication];
id delegate = [application delegate];
self.xmppRosterManagedObjectContext = [delegate xmppRosterManagedObjectContext];

      

(2).获取FetchRequst对象,并指定CoreData实体类,之后添加排序规则,代码如下:

     //从CoreData中获取数据
//通过实体获取FetchRequest实体
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([XMPPUserCoreDataStorageObject class])];
//添加排序规则
NSSortDescriptor * sortD = [NSSortDescriptor sortDescriptorWithKey:@"jidStr" ascending:YES];
[request setSortDescriptors:@[sortD]];

  

(3).获取FetchedResultController并注册回调,用于自动刷新TableView,代码如下:

     //获取FRC
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.xmppRosterManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;

(4)获取存储的内容

     //获取内容
NSError * error;
;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(@"%s %@",__FUNCTION__,[error localizedDescription]);
}

  

至于如何在TableView上显示FetchedResultController获取的数据,请参考之前的博客:iOS开发之表视图爱上CoreData

最近联系人的代码和历史表情的代码类似,请参考之前的博客:iOS开发之微信聊天工具栏的封装

聊页面的实现请参考之前的博客:iOS开发之微信聊天页面实现

  

今天的XMPPFramework就先到这儿吧,内容也挺多的了,其实XMPPFramework中的组件使用方法都差不多,首先第初始化内存,然后进行相关配置,在后就是在XMPPStream中激活,最后就是如何使用了。

gitHub分享地址:https://github.com/lizelu/WeChat

iOS开发之使用XMPPFramework实现即时通信(二)的更多相关文章

  1. iOS开发之使用XMPPFramework实现即时通信(三)

    你看今天是(三)对吧,前面肯定有(一)和(二),在发表完iOS开发之使用XMPPFramework实现即时通信(一)和iOS开发之使用XMPPFramework实现即时通信(二)后有好多的小伙伴加我Q ...

  2. iOS开发之使用XMPPFramework实现即时通信

    iOS开发之使用XMPPFramework实现即时通信   关于XMPP的理论介绍在本篇博客中就不做赘述了,如何在我们之前的微信中加入XMPP协议来实现通信呢?下面将会介绍一下XMPP的基本的知识,让 ...

  3. iOS开发之使用XMPPFramework实现即时通信(一)

    关于XMPP的理论介绍在本篇博客中就不做赘述了,如何在我们之前的微信中加入XMPP协议来实现通信呢?下面将会介绍一下XMPP的基本的知识,让我们的微信可以实现互联通信.要做的准备工作是要有服务器支持X ...

  4. iOS开发多线程篇—线程间的通信

    iOS开发多线程篇—线程间的通信 一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任 ...

  5. iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)

    本文链接:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 这里接着前文<iOS ...

  6. iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)

    转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-two.html 一. 概况 本文接着 iOS 开 ...

  7. iOS开发之线程间的MachPort通信与子线程中的Notification转发

    如题,今天的博客我们就来记录一下iOS开发中使用MachPort来实现线程间的通信,然后使用该知识点来转发子线程中所发出的Notification.简单的说,MachPort的工作方式其实是将NSMa ...

  8. iOS开发多线程篇—线程间的通信(转)

    这里转载 给自己一个备份 一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任务后,转 ...

  9. iOS开发网络篇—网络编程基础(二)

    下面叙述的是关于几个必须要知道的iOS网络编程入门级别的要点:       1.客户端如何找到连接的服务器    客户端通过URL找到想要连接的服务器   2.什么是URL     URL的全称是Un ...

随机推荐

  1. HashMap实现缓存

    package com.cache; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.a ...

  2. 在RNN中使用Dropout

    dropout在前向神经网络中效果很好,但是不能直接用于RNN,因为RNN中的循环会放大噪声,扰乱它自己的学习.那么如何让它适用于RNN,就是只将它应用于一些特定的RNN连接上.   LSTM的长期记 ...

  3. Android系列:res之shape制作

    大家好,pls call me francis. nice to me you. 本文将介绍使用在Android中使用shape标签绘制drawable资源图片. 下面的代码是shap标签的基本使用情 ...

  4. 获得Map的选择集

    ISelection selection = m_hookHelper.FocusMap.FeatureSelection;            IEnumFeatureSetup iEnumFea ...

  5. 初识Python

    Python 简介 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有 ...

  6. shell脚本删除指定mobileprovision

    由于某种原因,xcode帮我按照了几千个开发和上线证书,需要删除这部分证书: #dir="/Users/Ethan/Library/MobileDevice/Provisioning Pro ...

  7. opecv获取图像轮廓

    获取轮廓 #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/ ...

  8. ASP.NET Core Linux下为 dotnet 创建守护进程(必备知识)

    前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 asp.net core 应用程序,本篇主要是怎么样为我们在 Linux 或者 macOs 中部署的 dotnet 程序创建一个守护进程 ...

  9. 体验phonegap3.0

    网上有各种各样的phonegap环境搭建资料,鉴于学习和整理的考虑,我还是把我搭建的过程整理出来 这篇文章中将涉及到的内容 PhoneGap环境需要的组件 Node环境 JDK Android SDK ...

  10. Linux2 在Linux(CentOS)上配置SSH免登陆

    前言:      本文主要是我在安装hadoop之前,需要先配置SSH免登陆.通过网上搜索,发现不少类似的资料,但多少都有些小问题,所以结合自己的实践,记录在此,作为参考.如果能帮助到其他人,自然是更 ...