Habber - IOS XMPP 客户端 教程 (二)应用XMPP&代理&全局变量

底层始于XMPP

首先构筑底层,底层为上层服务,也就是我们对XMPP框架提供接口编程的应用。

我是在AppDelegate中写的,可是为什么要在AppDelegate中写?
Nice question! Cuz the demo which the author gave was written in AppDelegate.

好吧,开个玩笑,不过按照我的理解,创建在AppDelegate中原因只是app的生命周期内,我们也只需要创建一个单例,一个xmppStream,然后进行接收传递等数据都靠这同一个流。

事实上完全可以在别的地方写,但是引用起来可能差强人意了,因为还是要引用同一个XMPPStream,怎样获取是个问题。

先看一下AppDelegate.h文件:

//
//  AppDelegate.h
//  Habber
//
//  Created by Sunny on 12/15/15.
//  Copyright © 2015 Nine. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <XMPP.h>
#import "Statics.h"
#import "HabberMessageDelegate.h"
#import "HabberChatDelegate.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate, XMPPStreamDelegate> {
    //是否连接状态
    BOOL isOpen;
}

@property (strong, nonatomic) UIWindow *window;

//用于传输xmpp协议数据的封装流。
@property (nonatomic, readonly) XMPPStream *xmppStream;
//密码
@property (nonatomic, strong) NSString *password;

@property (nonatomic, strong) id<HabberChatDelegate> chatDelegate;
@property (nonatomic, strong) id<HabberMessageDelegate> messageDelegate;

//XMPPStream的初始化
- (void)setupStream;

//连接功能
- (BOOL)connect;
- (void)disconnect;

//控制上下线
- (void)goOnline;
- (void)goOffline;

@end

可以看到它的主要功能就是这些了,继承XMPPStreamDelegate与服务器进行交互,并返回状态,剩下的交给代理(HabberChatDelegate、HabberMessageDelegate)去做。

这里isOpen这个在我给的代码中不用写也可以运行,本来想用来控制服务器是否连接状态的,
不过程序中没有用上。但是应该有一个对服务器判断的,减少不必要的开销和消除bug

接下来看看AppDelegate.m中做了什么:

//
//  AppDelegate.m
//  Habber
//
//  Created by Sunny on 12/15/15.
//  Copyright © 2015 Nine. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@property (strong, nonatomic) UIImageView *splashView;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //设置界面显示等,标题栏颜色,状态栏颜色,字体大小等
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
    [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:36.0/255 green:36.0/255 blue:36.0/255 alpha:0.9]];
    [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
    [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
    [[UIBarButtonItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont boldSystemFontOfSize:13], NSFontAttributeName, nil] forState:UIControlStateNormal];

    //程序打开自动连接服务器
    [self connect];

    [NSThread sleepForTimeInterval:1.5];

    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {

}

- (void)applicationWillTerminate:(UIApplication *)application {
    [self disconnect];
}

//XMPPStream初始化
- (void)setupStream {
    _xmppStream = [XMPPStream new];
    //设置线程
//    [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    //像上面这样,放到其它线程中,那么代理和通知修改界面的时候就会出现问题,至于放到主线程中来,反正它里面集成的多线程操作可以应付消息传递了。
    [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

//发送连接服务器请求
- (BOOL)connect {
    [self setupStream];

    //从本地取得用户名密码和服务器地址
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *userId = [defaults stringForKey:USERID];
    NSString *pass = [defaults stringForKey:PASS];
    NSString *server = [defaults stringForKey:SERVER];

    //已经连接就不用再连接了
    if ([_xmppStream isConnected]) {
        return YES;
    }
    //没有用户名密码我也不去连接
    if (userId == nil || pass == nil) {
        return NO;
    }

    //设置用户
    [_xmppStream setMyJID:[XMPPJID jidWithString:userId]];
    //密码
    _password = pass;
    //设置服务器
    [_xmppStream setHostName:server];

    //连接服务器
    NSError *error = nil;
    if (![_xmppStream connectWithTimeout:5.0 error:&error]) {
        return NO;
    }
    [_chatDelegate didConnect];
    return YES;
}

- (void)disconnect {
    [self goOffline];
    [_xmppStream disconnect];
    [_chatDelegate didDisconnect];
}

- (void)acceptFriendRequest {
}

//控制上下线
- (void)goOnline {
    //发送在线状态
    XMPPPresence *presence = [XMPPPresence presence];
    [[self xmppStream] sendElement:presence];
}

- (void)goOffline {
    //发送下线状态
    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
    [[self xmppStream] sendElement:presence];
}

#pragma mark - XMPPStreamDelegate实现
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
    isOpen = YES;
    NSError *error = nil;
    //验证密码
    [[self xmppStream] authenticateWithPassword:_password error:&error];
}

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"authenticateFail" object:nil];
}

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
    [self goOnline];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"hasAuthenticated" object:nil];
}

- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error {
    isOpen = NO;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"connectServerFailed" object:nil];
}

//收到消息后把消息传递给代理
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {
    //封装好的 文字 + 发送人
    NSString *msg = [[message elementForName:@"body"] stringValue];
    NSString *img = [[message elementForName:@"image"] stringValue];
    if (!img) {
        img = @"";
    }
    NSString *voice = [[message elementForName:@"voice"] stringValue];
    NSString *voiceTime = [[[message elementForName:@"voice"] attributeForName:@"voiceTime"] stringValue];
    if (!voice) {
        voice = @"";
        voiceTime = @"";
    }
    NSString *from = [[message attributeForName:@"from"] stringValue];
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:msg forKey:@"msg"];
    [dict setObject:img forKey:@"photo"];
    [dict setObject:voice forKey:@"voice"];
    [dict setObject:voiceTime forKey:@"voiceTime"];
    [dict setObject:from forKey:@"sender"];

    [_messageDelegate newMessageReceived:dict];
}

//收到好友状态
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
    //取得好友状态
    NSString *presenceType = [presence type];
    //我的id
    NSString *userId = [[sender myJID] user];
    //对方状态(用user也就相当于强制类型转换成NSString)
    NSString *presenceFromUser = [[presence from] user];
    //如果在列表中把“我”过滤掉
    if (![presenceFromUser isEqualToString:userId]) {
        if ([presenceType isEqualToString:@"available"]) {
            [_chatDelegate newBuddyOnline:[NSString stringWithFormat:@"%@@%@", presenceFromUser, @"thinkdifferent.local"]];
        }
        if ([presenceType isEqualToString:@"unavailable"]) {
            [_chatDelegate buddyWentOffline:[NSString stringWithFormat:@"%@@%@", presenceFromUser, @"thinkdifferent.local"]];
        }
        //收到好友请求
        if ([presenceType isEqualToString:@"subscribe"]) {
            [_chatDelegate receivedFriendRequest:[NSString stringWithFormat:@"%@@%@", presenceFromUser, @"thinkdifferent.local"]];
        }
    }
}

@end

注释中已经写的很详细了,根据服务器传回的消息进行发送消息通知,而设置的代理则用于传输接收到的传递回来的xml数据解析后封装成的字典。

如果对通知机制与代理机制不太熟悉请恶补一下这方面的内容,真的是非常好用!
不禁要感叹只有当自己去做程序的时候才能明白其真正的作用。

两个代理

HabberChatDelegate

#import <Foundation/Foundation.h>

@protocol HabberChatDelegate <NSObject>

//传递上线人的名字
- (void)newBuddyOnline:(NSString *)buddyName;
//传递下线人的名字
- (void)buddyWentOffline:(NSString *)buddyName;
//发送与服务器断开连接的信息
- (void)didDisconnect;
//发送与服务器连接的信息
- (void)didConnect;
//传送好友申请信息
- (void)receivedFriendRequest:(NSString *)presenceFrom;

@end

HabberMessageDelegate

#import <Foundation/Foundation.h>

@protocol HabberMessageDelegate <NSObject>

//就只负责聊天数据的传送
- (void)newMessageReceived:(NSDictionary *)messageContent;

@end

Model的设置

Statics.h

#import <Foundation/Foundation.h>
//这里有三个,专门用于当做存储userDefaults的键值使用
static NSString *USERID = @"userId";
static NSString *PASS= @"pass";
static NSString *SERVER = @"server";

@interface Statics : NSObject

//获得当前时间
+ (NSString *)getCurrentTime;

@end

Statics.m

#import "Statics.h"

@implementation Statics

+ (NSString *)getCurrentTime {
    NSDate *nowUTC = [NSDate date];

    NSDateFormatter *dateFormatter = [NSDateFormatter new];
    [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];

    return [dateFormatter stringFromDate:nowUTC];
}

@end

其实关于这个静态方法+ (NSString *)getCurrentTime,作用是获得系统时间显示在聊天cell上的,但是由于已经用了UUChatTableView这个框架,这个方法放在这里没什么意义了,仅供参考。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值