iOS XMPP Framework 05 - 登录服务器

在前两节中,我们构建了应用程序的框架并将XMPP Framework添加到了工程中,但这仅仅还是个空的应用程序,这一节我们会建立XMPP链接并登录到Openfire服务器中。

1. 实现XMPPFramework

1.1 添加接口和属性

修改YDAppDelegate.h,添加对Core Data和XMPPFramework.h的引用

#import <CoreData/CoreData.h>
#import "XMPPFramework.h"

添加XMPP相关的属性和接口声明

@interface YDAppDelegate : UIResponder <UIApplicationDelegate,YDLeftMenuViewControllerDelegate,YDSlideMenuContainerViewControllerDelegate>
{
    BOOL allowSelfSignedCertificates;
    BOOL allowSSLHostNameMismatch;
    
    BOOL isXmppConnected;
}

//XMPP
@property (nonatomic, strong, readonly) XMPPStream *xmppStream;
@property (nonatomic, strong, readonly) XMPPReconnect *xmppReconnect;
@property (nonatomic, strong, readonly) XMPPRoster *xmppRoster;
@property (nonatomic, strong, readonly) XMPPRosterCoreDataStorage *xmppRosterStorage;
@property (nonatomic, strong, readonly) XMPPvCardTempModule *xmppvCardTempModule;
@property (nonatomic, strong, readonly) XMPPvCardAvatarModule *xmppvCardAvatarModule;
@property (nonatomic, strong, readonly) XMPPvCardCoreDataStorage *xmppvCardStorage;
@property (nonatomic, strong, readonly) XMPPCapabilities *xmppCapabilities;
@property (nonatomic, strong, readonly) XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
- (NSManagedObjectContext *)managedObjectContext_roster;
- (NSManagedObjectContext *)managedObjectContext_capabilities;
//public methods
- (BOOL)connect;
- (void)disconnect;

1.2 引入Keychain

iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。从ios 3。0开始,跨程序分享keychain变得可行。

如何需要在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。

从apple官网,可以下载KeychainItemWrapper:

GenericKeychain

KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:

/** 初始化一个保存用户帐号的KeychainItemWrapper */  
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Account Number"    
                                                                   accessGroup:@"YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier"];   
  
//保存帐号   
[wrapper setObject:@"<帐号>" forKey:(id)kSecAttrAccount];     
       
//保存密码   
[wrapper setObject:@"<帐号密码>" forKey:(id)kSecValueData];     
       
//从keychain里取出帐号密码   
NSString *password = [wrapper objectForKey:(id)kSecValueData];   

其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会崩溃!

此外,由于我们的工程使用了ARC,但是KeyChainItemWrapper不支持ARC,我们需要修改工程配置,告诉编译器这个类与ARC不兼容。-fno-objc-arc


2. 实现登陆

登陆界面由两个UITextField和两个UIButton组成。用于输入用户名和密码以及进行登陆和取消操作。

首先实现YDSigninViewController.h,声明一个代理协议,用于通知代理证书已被存储事件。

#import <UIKit/UIKit.h>

@protocol YDSignInViewControllerDelegate <NSObject>

-(void)credentialsStored;

@end
@interface YDSignInViewController : UIViewController
@property (nonatomic, strong) id<YDSignInViewControllerDelegate>  delegate;
@end
接下来实现YDSigninViewController.m

#import <QuartzCore/QuartzCore.h>
#import "YDSignInViewController.h"
#import "KeychainItemWrapper.h"
@interface YDSignInViewController ()

@property (nonatomic,strong) UITextField *jidField;
@property (nonatomic,strong) UITextField *passwordField;
@end

@implementation YDSignInViewController



- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton=YES;
    
    
	// Do any additional setup after loading the view.
    self.view.backgroundColor=[UIColor whiteColor];
    self.title = @"Sign in";
    
   
    self.jidField = [[UITextField alloc] initWithFrame:CGRectMake(20.0, 80.0, 280, 25.0)];
	self.jidField.borderStyle = UITextBorderStyleNone;
    self.jidField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
	self.jidField.tag = 1;
	self.jidField.keyboardType = UIKeyboardTypeDefault;
	self.jidField.backgroundColor = [UIColor whiteColor];
	self.jidField.font = [UIFont systemFontOfSize:14.0];
	self.jidField.autocorrectionType = UITextAutocorrectionTypeNo;
	self.jidField.placeholder = @"Enter username";
	self.jidField.autocapitalizationType = UITextAutocapitalizationTypeNone;
	self.jidField.returnKeyType = UIReturnKeyNext;
    self.jidField.borderStyle = UITextBorderStyleRoundedRect;
    self.jidField.layer.borderWidth = 1.0f;
    self.jidField.layer.borderColor = [[UIColor grayColor] CGColor];
    self.jidField.layer.cornerRadius = 5.0f;
    [self.view addSubview:self.jidField ];
    
    self.passwordField = [[UITextField alloc] initWithFrame:CGRectMake(20.0, 120.0, 280, 25.0)];
	self.passwordField.borderStyle = UITextBorderStyleNone;
    self.passwordField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
	self.passwordField.tag = 2;
	self.passwordField.keyboardType = UIKeyboardTypeDefault;
	self.passwordField.backgroundColor = [UIColor whiteColor];
	self.passwordField.font = [UIFont systemFontOfSize:14.0];
	self.passwordField.autocorrectionType = UITextAutocorrectionTypeNo;
	self.passwordField.placeholder = @"Enter the password";
    self.passwordField.secureTextEntry = YES;
	self.passwordField.autocapitalizationType = UITextAutocapitalizationTypeNone;
	self.passwordField.returnKeyType = UIReturnKeyNext;
    self.passwordField.borderStyle = UITextBorderStyleRoundedRect;
    self.passwordField.layer.borderWidth = 1.0f;
    self.passwordField.layer.borderColor = [[UIColor grayColor] CGColor];
    self.passwordField.layer.cornerRadius = 5.0f;
    [self.view addSubview:self.passwordField ];
    
    UIButton* signInButton = [[UIButton alloc] initWithFrame:CGRectMake(20,160,280,25)];
    signInButton.backgroundColor=[UIColor blueColor];
    signInButton.layer.borderWidth = 1.0f;
    signInButton.layer.borderColor = [[UIColor grayColor] CGColor];
    signInButton.layer.cornerRadius = 5.0f;
    [signInButton addTarget:self action:@selector(saveCredentials:) forControlEvents:UIControlEventTouchUpInside];
    UILabel *signInLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,280,25)];
    signInLabel.backgroundColor=[UIColor clearColor];
    [signInLabel setFont:[UIFont systemFontOfSize:16]];
    signInLabel.text=@"Sign in";
    signInLabel.adjustsFontSizeToFitWidth=YES;
    signInLabel.textAlignment=NSTextAlignmentCenter;
    signInLabel.textColor=[UIColor whiteColor];
    [signInButton addSubview:signInLabel];
    [self.view addSubview:signInButton];
    //Cancel
    UIButton* cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(20,200,280,25)];
    cancelButton.backgroundColor=[UIColor blueColor];
    cancelButton.layer.borderWidth = 1.0f;
    cancelButton.layer.borderColor = [[UIColor grayColor] CGColor];
    cancelButton.layer.cornerRadius = 5.0f;
    [cancelButton addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside];
    UILabel *cancelLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,280,25)];
    cancelLabel.backgroundColor=[UIColor clearColor];
    [cancelLabel setFont:[UIFont systemFontOfSize:16]];
    cancelLabel.text=@"Cancel";
    cancelLabel.adjustsFontSizeToFitWidth=YES;
    cancelLabel.textAlignment=NSTextAlignmentCenter;
    cancelLabel.textColor=[UIColor whiteColor];
    [cancelButton addSubview:cancelLabel];
    [self.view addSubview:cancelButton];

    
}
-(IBAction)saveCredentials:(UIButton *)sender
{
    if (([self.jidField.text length] == 0) ||([self.passwordField.text length] == 0) )
        {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Oops"
                            message:@"Both fields are mandatory"
                            delegate:self cancelButtonTitle:@"OK"
                            otherButtonTitles:nil, nil];
        [alert show];
        return;
        }
    else
        {
        KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"YDCHAT" accessGroup:nil];
        NSString *jid = [NSString stringWithFormat:@"%@@%@",self.jidField.text,kXMPPServer];
        [[NSUserDefaults standardUserDefaults] setValue:jid forKey:kXMPPmyJID];
        [[NSUserDefaults standardUserDefaults]  synchronize];
         [keychain setObject:self.passwordField.text forKey:(__bridge id)kSecValueData];
        [self.navigationController popToRootViewControllerAnimated:NO];
        [self.delegate credentialsStored];
        }
    
}
-(IBAction)cancel:(UIButton *)sender
{
     [self.navigationController popToRootViewControllerAnimated:NO];
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

完成了YDSigninViewController后,我们修改YDAppDelegate,实现credentialsStored协议,当用户输入用户名和密码后,调用connect方法连接openfire服务器。

-(void)credentialsStored
{
    if (![self connect])
    {
        DDLogInfo(@"credentialsStored self connect failed");
    }
    
}
连接服务器方法如下,读取储存的用户名和密码调用connectWithTimeout方法连接服务器:

- (BOOL)connect
{
    if (![self.xmppStream isDisconnected]) {
        return YES;
    }
    NSString *myJID = [[NSUserDefaults standardUserDefaults] stringForKey:kXMPPmyJID];
    KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"YDCHAT" accessGroup:nil];
    NSString *myPassword = [keychain objectForKey:(__bridge id)kSecValueData];
    userPassword = myPassword;
    
    //
    // If you don't want to use the Settings view to set the JID,
    // uncomment the section below to hard code a JID and password.
    //
    // myJID = @"user@gmail.com/xmppframework";
    // myPassword = @"";
    
    if (myJID == nil || myPassword == nil) {
        return NO;
    }
    
    [self.xmppStream setMyJID:[XMPPJID jidWithString:myJID]];
	   
    NSError *error = nil;
    if (![self.xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error])
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error connecting"
                                                            message:@"See console for error details."
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
        
        DDLogError(@"Error connecting: %@", error);
        
        return NO;
    }
    
    return YES;
}
认证通过后,xmppStreamDidAuthenticate被调用,在其中调用goOnline方法:

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
    
    [self goOnline];
}
goOnline方法中,向服务器发送上线消息:

// It's easy to create XML elments to send and to read received XML elements.
// You have the entire NSXMLElement and NSXMLNode API's.
//
// In addition to this, the NSXMLElement+XMPP category provides some very handy methods for working with XMPP.
//
// On the iPhone, Apple chose not to include the full NSXML suite.
// No problem - we use the KissXML library as a drop in replacement.
//
// For more information on working with XML elements, see the Wiki article:
// https://github.com/robbiehanson/XMPPFramework/wiki/WorkingWithElements

- (void)goOnline
{
    XMPPPresence *presence = [XMPPPresence presence]; // type="available" is implicit
    
    NSString *domain = [self.xmppStream.myJID domain];
    
    //Google set their presence priority to 24, so we do the same to be compatible.
    
    if([domain isEqualToString:@"gmail.com"]
       || [domain isEqualToString:@"gtalk.com"]
       || [domain isEqualToString:@"talk.google.com"])
    {
        NSXMLElement *priority = [NSXMLElement elementWithName:@"priority" stringValue:@"24"];
        [presence addChild:priority];
    }
    
    [[self xmppStream] sendElement:presence];
    [self.rootViewController updateStatus:@"Online"];
}

程序执行效果如下:左侧是模拟器上的显示,我们已经在线了,又侧是adium上的效果。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值