1. 基本步骤:首先导入Xmpp框架,配置环境
-》由于我们使用的是OC的Xmpp框架,再进行Swift开发时需要进行桥接。 具体方法就是创建一个基于c的.h的头文件,然后将我们需要编译OC的语言的头文件包含这个.h文件中;
-》然后导入如下库文件,在头头文件选择路径中
如上图, 设置header Search paths 设置头文件的搜索路径,导入libxml2库文件相对路径,***注意前面是 usr 。以前第一次装的时候写成user找了半个多小时
-> 然后创建界面,随便弄两个框框和按钮自定义一个建议的界面。
应为在应用程序中用户名和密码可能被反复的使用,所以我们需要讲它门设置成一个单利类。
如下代码所示:
import UIKit
class WSBUserinfo: NSObject {
// 登录的用户名密码
var userName : String?
var userPasswd : String?
// 注册的用户名密码
var userRegisterName : String?
var userRegisterPasswd : String?
// 为了区分登录和注册
var isLogin : Bool?
// 单例
class func getShareInstance() -> WSBUserinfo{
struct Singleton {
static var dispatchOne : dispatch_once_t = 0
static var instance : WSBUserinfo? = nil
}
dispatch_once(&Singleton.dispatchOne) { () -> Void in
Singleton.instance = WSBUserinfo()
}
return Singleton.instance!
}
}
//由于登录流程设计到了xmpp流协议实现,步骤较多。这里我们可以单独封装一个工具类。这样我们的登录和注册的主控制器就能得到极大的解脱:
在登录控制器中我们只需要将输入框的用户输入信息赋值到单利用户信息类 UserInfo里
然后调用 单利工具类的 usrLogin()方法及可自动完成所有的登录流程
再通过指定一个协议,实现对登录和注册状态的监听;
注册控制器的逻辑同上,这里就不重复了 。
注意点1:再每次连接请求时先把之前的连接断开,这样能避免一个账号长连接而其它的账号无法连接。
注意点2:再发送登录注册时中间都涉及到了共同的连接请求和连接状态请求,我们需要在单利类中建立一个Bool属性值做分支判定,这样我们只需要在方法内部做简单的调整,从而省去大量的代码步骤。
具体步骤请看下面代码
import UIKit
class WSBLoginViewController: UIViewController,WSBXMPPLoginDelegate {
@IBOutlet weak var userNameField: UITextField!
@IBOutlet weak var userPasswdField: UITextField!
@IBAction func loginBtnClick(sender: AnyObject) {
WSBUserinfo.getShareInstance().isLogin = true
print("用户名:\(userNameField.text!)")d
print("密码是:\(userPasswdField.text!)")
// 把用户名 密码存入工具类 WSBUserinfo 这个单例对象中
WSBUserinfo.getShareInstance().userName = userNameField.text!
WSBUserinfo.getShareInstance().userPasswd = userPasswdField.text!
// 调用XMPP 工具类 完成登录
// 登录之前先给WSBXMPPTool设置代理
WSBXMPPTool.getSharedInstance().loginDelegate = self
WSBXMPPTool.getSharedInstance().userLogin()
}
/** 登录协议中的方法 */
func loginSuccess() {
print("登录成功")
// 切换到主界面
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
UIApplication.sharedApplication().keyWindow?.rootViewController =
storyBoard.instantiateInitialViewController()
}
func loginFailed() {
print("登录失败")
}
func loginNetError() {
print("登录时网络错误")
}
override func viewDidLoad() {
super.viewDidLoad()
}
deinit{
print("deinit\(self)")
}
}
RegisterViewController:
import UIKit
class WSBRegisterViewController: UIViewController,WSBXMPPRegisterDelegate {
@IBOutlet weak var userRegiserName: UITextField!
@IBOutlet weak var userRegisterPasswd: UITextField!
@IBAction func registerBtnClick(sender: AnyObject) {
// 说明这是注册行为
WSBUserinfo.getShareInstance().isLogin = false
WSBUserinfo.getShareInstance().userRegisterName = self.userRegiserName.text!
WSBUserinfo.getShareInstance().userRegisterPasswd = self.userRegisterPasswd.text!
// 调用XMPPTool的 注册方法
WSBXMPPTool.getSharedInstance().registerDelegate = self
WSBXMPPTool.getSharedInstance().userRegister()
}
/** 实现注册协议中的方法 */
func registerSuccess() {
print("注册成功")
self.dismissViewControllerAnimated(true, completion: nil)
}
func registerFailed() {
print("注册失败")
}
func registerNetError() {
print("注册时网络错误")
}
}
登录和注册协议
import Foundation
// 登录协议
protocol WSBXMPPLoginDelegate : NSObjectProtocol {
// 登录成功
func loginSuccess()
// 登录失败
func loginFailed()
// 网络错误
func loginNetError()
}
// 注册协议
protocol WSBXMPPRegisterDelegate : NSObjectProtocol {
// 注册成功
func registerSuccess()
// 注册失败
func registerFailed()
// 注册网络错误
func registerNetError()
}
Swift:
import UIKit
/** 桥接文件 项目--buildSettings --> 搜索 Objective-c Bride 设置即可
这是一个XMPP的工具类 可以完成登录和注册的行
*/
class WSBXMPPTool: NSObject, XMPPStreamDelegate {
var xmppStream : XMPPStream!
// 登录的Delegate
weak var loginDelegate : WSBXMPPLoginDelegate?
// 注册的Deletegate
weak var registerDelegate : WSBXMPPRegisterDelegate?
// 保证xmppStream 使用时非空
override init() {
super.init()
setupXmppStream()
}
class func getSharedInstance() -> WSBXMPPTool {
struct Singleton {
static var predicate : dispatch_once_t = 0
static var instance : WSBXMPPTool? = nil
}
dispatch_once(&Singleton.predicate) { () -> Void in
Singleton.instance = WSBXMPPTool()
}
return Singleton.instance!
}
// 设置流 设置代理
func setupXmppStream(){
xmppStream = XMPPStream()
xmppStream.addDelegate(self, delegateQueue: dispatch_get_main_queue())
}
// 连接服务器
func connectToHost(){
// 先断开上次的 不断开登录之后没法再登录
xmppStream.disconnect();
// 调用设置xmppStream
if self.xmppStream == nil {
self.setupXmppStream()
}
var userName : String? = nil
if WSBUserinfo.getShareInstance().isLogin! {
userName = WSBUserinfo.getShareInstance().userName
}else{
userName = WSBUserinfo.getShareInstance().userRegisterName
}
// 构建jid 设置hostName 和 port
xmppStream.myJID = XMPPJID.jidWithUser(userName, domain: XMPPDOMAIN, resource: "iphone")
xmppStream.hostName = XMPPHOSTNAME
xmppStream.hostPort = XMPPPORT
do {
try xmppStream.connectWithTimeout(XMPPStreamTimeoutNone)
}catch let error as NSError{
print(error.description)
}
}
// 连接成功(代理方法)
func xmppStreamDidConnect(sender: XMPPStream!) {
// 调用发送密码 请求授权
if WSBUserinfo.getShareInstance().isLogin!{
self.sendLoginPasswd()
}else{
// 使用密码注册
self.sendRegisterPasswd()
}
}
// 发送密码 注册
func sendRegisterPasswd(){
do{
try xmppStream.registerWithPassword(WSBUserinfo.getShareInstance().userRegisterPasswd)
}catch let error as NSError{
print(error.description)
}
}
// 发送密码 请求授权
func sendLoginPasswd(){
do{
try xmppStream.authenticateWithPassword(WSBUserinfo.getShareInstance().userPasswd)
}catch let error as NSError{
print(error.description)
}
}
// 授权成功(代理方法)
func xmppStreamDidAuthenticate(sender: XMPPStream!) {
print("授权成功")
// 发送在线消息
self.sendOnLine()
// 调用代理的登录成功的方法
loginDelegate?.loginSuccess()
}
// 发送在线消息
func sendOnLine(){
xmppStream.sendElement(XMPPPresence())
}
// 授权失败
func xmppStream(sender: XMPPStream!, didNotAuthenticate error: DDXMLElement!) {
print(error.description)
// 授权失败 调用代理的登录失败的方法
loginDelegate?.loginFailed()
}
// 连接断开(代理方法)
func xmppStreamDidDisconnect(sender: XMPPStream!, withError error: NSError!) {
// 如果网络有问题断开 则error 中有值
// 如果是主动断开 则error是 nil
if error != nil {
if WSBUserinfo.getShareInstance().isLogin!{
//登录网络有问题 调用代理的网络错误
loginDelegate?.loginNetError()
}else{
// 注册时网络有问题
registerDelegate?.registerNetError()
}
}
}
//注册成功 (代理方法 )
func xmppStreamDidRegister(sender: XMPPStream!) {
// 调用代理的注册成功
registerDelegate?.registerSuccess()
}
// 注册失败 (代理方法 )
func xmppStream(sender: XMPPStream!, didNotRegister error: DDXMLElement!) {
registerDelegate?.registerFailed()
}
// 用户完成登录的方法
func userLogin(){
self.connectToHost()
}
// 用户完成注册的方法
func userRegister(){
self.connectToHost()
}
}
总的来说基本思路和OC的写法基本相同,下面简但介绍下程序的设计思路。
一 总体登录流程:
1.设置xmpp流配置(port id domain)-》2. 请求链接绑定jid-》3.监听链接是否成功-》4.成功则发送密码请求授权-》5.监听授权是成功-》6.授权成功则发送出席状态。
注册流程:
1.设置xmpp流配置(port id domain)-》 2.请求链接绑定jid-》3.监听链接是否成功-》4.成功则发送密码请求注册-》5.监听注册是否成功-》6.注册成功则返回登录界面。
从上面可以看出登录和注册的基本流程大致相同,只不郭仔第4步骤中调用的方法不通而已,为了操作方便我们定义了一个Bool值,在第3部链接成功中进行选择判定 isRegister。
其余对应的方法不通的地方也依次使用Bool选择判定。
二 .协议的使用时机。
我们将此6部只需要封装到一个方法里,并返回一个结果(链接是否成功,授权是否成功) usrLogin:(返回的结果)。 这样我们在主控制器中通过调用xmppTool单利类的方法就能够拿到这个结果。
在上面链接是否成功,是否授权,是否注册成功,一共5处需传递给主控制器。 这里可以使用协议或者代理,当然还有通知.
下面就种方式常用的方式代理和闭包(其实就相当于OC的Block回调,变了个说法而已)
****协议的写法和OC大不相同,值得注意的是,我们定义的协议前面一定要继承系统的NSObjectProtocol(这是因为我们在定义var delegate:Protococl! 的时候,前面不能使用weak,但是不使用weak又会导致设置协议后造成另一个对象强强引用释放不了,所以此处需要继承下系统的协议,具体什么原因楼主目前暂时也还没有弄明白)。
其它的只是变了个写法格式,看代码就可以了,逻辑完全没变。
三. 使用Block代替上面的协议。
3.1 这里我们首先定义一个枚举类型,用于定义回调传值的类型,然后再定义一个闭包类型。
enum XMPPResultType{
case XMPPResultTypeRegisterSuccess,
XMPPResultTypeRegisterFailed,
XMPPResultTypeRegisterNetError
}
typealias RegisterCloserType = (result : XMPPResultType) -> Void
3.2 接着在XmppTool工具类中定义一个闭包类型,用来接收注册或者登陆控制器中传来的闭包函数
weak var loginDelegate : WSBXMPPLoginDelegate?
// 保存传入的闭包
var registerCloser : RegisterCloserType!
3.3 调用闭包方法
/* 用户完成注册的方法 当用闭包时 需要让调用者传入一个闭包来获取注册状态 */
func userRegister(function : RegisterCloserType!){
registerCloser = function
self.connectToHost()
}
3.4在XmppTool工具类中需要使用回调的地方执行次闭包函数,下面以其中一个为例, 网络连接错误,注册成功,失败,这三处都需要调用。
registerCloser!(result: XMPPResultType.XMPPResultTypeRegisterSuccess)
3.5 我们在主控制器中只需要拿出闭包函数的result的状态及可判断是否 后台执行的结果.注意需要使用weakself,否则无法释放。
weak var weakVc = self
WSBXMPPTool.getSharedInstance().userRegister { (result) -> Void in
switch result{
case .XMPPResultTypeRegisterSuccess:
print("注册成功")
weakVc?.dismissViewControllerAnimated(true, completion: nil)
case .XMPPResultTypeRegisterFailed:
print("注册失败")
case .XMPPResultTypeRegisterNetError:
print("网络错误")
} } }