zoukankan      html  css  js  c++  java
  • XMPP之ios即时通讯客户端开发-配置XMPP基本信息之工程代码(五)

    登录功能完成以后包含以下代码文件:

     AppDelegate.h 

     AppDelegate.m

    LoginViewController.h

    LoginViewController.m

    LoginUser.h

    LoginUser.m

    以下看代码:

    //
    //  AppDelegate.h
    //  XMPP即时通讯
    //
    //  Created by Mac on 15/7/15.
    //  Copyright (c) 2015年 聂小波. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    #import "XMPPFramework.h"
    
    #define xmppDelegate (AppDelegate *)[[UIApplication sharedApplication] delegate]
    
    typedef void(^CompletionBlock)();
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow *window;
    
    #pragma mark - XMPP属性及方法
    /**
     *  全局的XMPPStream,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPStream *xmppStream;
    /**
     *  全局的xmppvCard模块,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPvCardTempModule *xmppvCardModule;
    /**
     *  全局的XMPPvCardAvatar模块,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPvCardAvatarModule *xmppvCardAvatarModule;
    /**
     *  全局的xmppRoster模块,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPRoster *xmppRoster;
    /**
     *  全局的XMPPRosterCoreDataStorage模块,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPRosterCoreDataStorage *xmppRosterStorage;
    
    /**
     *  消息存档(归档)模块,只读属性
     */
    @property (strong, nonatomic, readonly) XMPPMessageArchiving *xmppMessageArchiving;
    @property (strong, nonatomic, readonly) XMPPMessageArchivingCoreDataStorage *xmppMessageArchivingCoreDataStorage;
    
    /**
     *  传输文件socket数组
     */
    @property (strong, nonatomic) NSMutableArray *socketList;
    
    /**
     *  是否注册用户标示
     */
    @property (assign, nonatomic) BOOL isRegisterUser;
    
    /**
     *  连接到服务器
     *
     *  注释:用户信息保存在系统偏好中
     *
     *  @param completion 连接正确的块代码
     *  @param faild      连接错误的块代码
     */
    - (void)connectWithCompletion:(CompletionBlock)completion failed:(CompletionBlock)faild;
    
    /**
     *  注销用户登录
     */
    - (void)logout;
    
    @end
    //
    //  AppDelegate.m
    //  XMPP即时通讯
    //
    //  Created by Mac on 15/7/15.
    //  Copyright (c) 2015年 聂小波. All rights reserved.
    //
    
    #import "AppDelegate.h"
    #import "LoginUser.h"
    #import "NSString+Helper.h"
    #import "NSData+XMPP.h"
    
    #define kNotificationUserLogonState @"NotificationUserLogon"
    
    @interface AppDelegate() <XMPPStreamDelegate, XMPPRosterDelegate, TURNSocketDelegate>
    {
        CompletionBlock             _completionBlock;       // 成功的块代码
        CompletionBlock             _faildBlock;            // 失败的块代码
        
        XMPPReconnect               *_xmppReconnect;        // XMPP重新连接XMPPStream
        XMPPvCardCoreDataStorage    *_xmppvCardStorage;     // 电子名片的数据存储模块
        
        XMPPCapabilities            *_xmppCapabilities;     // 实体扩展模块
        XMPPCapabilitiesCoreDataStorage *_xmppCapabilitiesCoreDataStorage; // 数据存储模块
    }
    
    // 设置XMPPStream
    - (void)setupStream;
    // 销毁XMPPStream并注销已注册的扩展模块
    - (void)teardownStream;
    // 通知服务器器用户上线
    - (void)goOnline;
    // 通知服务器器用户下线
    - (void)goOffline;
    // 连接到服务器
    - (void)connect;
    // 与服务器断开连接
    - (void)disconnect;
    
    @end
    
    @implementation AppDelegate
    
    #pragma mark 根据用户登录状态加载对应的Storyboard显示
    - (void)showStoryboardWithLogonState:(BOOL)isUserLogon
    {
        UIStoryboard *storyboard = nil;
        
        if (isUserLogon) {
            // 显示Main.storyboard
            storyboard = [UIStoryboard storyboardWithName:@"LoginViewController" bundle:nil];
        } else {
            // 显示Login.sotryboard
            storyboard = [UIStoryboard storyboardWithName:@"LoginViewController" bundle:nil];
        }
        
        // 在主线程队列负责切换Storyboard,而不影响后台代理的数据处理
        dispatch_async(dispatch_get_main_queue(), ^{
            // 如果在项目属性中,没有指定主界面(启动的Storyboard,self.window不会被实例化)
            // 把Storyboard的初始视图控制器设置为window的rootViewController
            [self.window setRootViewController:storyboard.instantiateInitialViewController];
            
            if (!self.window.isKeyWindow) {
                [self.window makeKeyAndVisible];
            }
        });
    }
    
    #pragma mark - AppDelegate方法
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // 1. 实例化window
    //    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        
        // 2. 设置XMPPStream
        [self setupStream];
        
        // 3. 实例化socket数组
        _socketList = [NSMutableArray array];
        
        return YES;
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application
    {
        [self disconnect];
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        // 应用程序被激活后,直接连接,使用系统偏好中的保存的用户记录登录
        // 从而实现自动登录的效果!
        [self connect];
    }
    
    - (void)dealloc
    {
        // 释放XMPP相关对象及扩展模块
        [self teardownStream];
    }
    
    #pragma mark - XMPP相关方法
    // 设置XMPPStream
    - (void)setupStream
    {
        // 0. 方法被调用时,要求_xmppStream必须为nil,否则通过断言提示程序员,并终止程序运行!
    //    NSAssert(_xmppStream == nil, @"XMPPStream被多次实例化!");
        
        // 1. 实例化XMPPSteam
        _xmppStream = [[XMPPStream alloc] init];
    //    _xmppStream.hostName = @"127.0.0.1";
    //////    mysql://localhost:3306/openfire
    //    _xmppStream.hostPort = 5222;
    //    
    //////    NSString *myJID=@"admin";
    //    _xmppStream.myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"admin@127.0.0.1"]];
        
        
        // 让XMPP在真机运行时支持后台,在模拟器上是不支持后台服务运行的
    #if !TARGET_IPHONE_SIMULATOR
        {
            // 允许XMPPStream在真机运行时,支持后台网络通讯!
            [_xmppStream setEnableBackgroundingOnSocket:YES];
        }
    #endif
        
        // 2. 扩展模块
        // 2.1 重新连接模块
        _xmppReconnect = [[XMPPReconnect alloc] init];
        // 2.2 电子名片模块
        _xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
        _xmppvCardModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:_xmppvCardStorage];
        _xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_xmppvCardModule];
        
        // 2.4 花名册模块
        _xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
        _xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:_xmppRosterStorage];
        // 设置自动接收好友订阅请求
        [_xmppRoster setAutoAcceptKnownPresenceSubscriptionRequests:YES];
        // 自动从服务器更新好友记录,例如:好友自己更改了名片
        [_xmppRoster setAutoFetchRoster:YES];
        
        // 2.5 实体扩展模块
        _xmppCapabilitiesCoreDataStorage = [[XMPPCapabilitiesCoreDataStorage alloc] init];
        _xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:_xmppCapabilitiesCoreDataStorage];
        
        // 2.6 消息归档模块
        _xmppMessageArchivingCoreDataStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
        _xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_xmppMessageArchivingCoreDataStorage];
        
        // 3. 将重新连接模块添加到XMPPStream
        [_xmppReconnect activate:_xmppStream];
        [_xmppvCardModule activate:_xmppStream];
        [_xmppvCardAvatarModule activate:_xmppStream];
        [_xmppRoster activate:_xmppStream];
        [_xmppCapabilities activate:_xmppStream];
        [_xmppMessageArchiving activate:_xmppStream];
        
        // 4. 添加代理
        // 由于所有网络请求都是做基于网络的数据处理,这些数据处理工作与界面UI无关。
        // 因此可以让代理方法在其他线城中运行,从而提高程序的运行性能,避免出现应用程序阻塞的情况
        [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
        [_xmppRoster addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    }
    
    // 销毁XMPPStream并注销已注册的扩展模块
    - (void)teardownStream
    {
        // 1. 删除代理
        [_xmppStream removeDelegate:self];
        [_xmppRoster removeDelegate:self];
        
        // 2. 取消激活在setupStream方法中激活的扩展模块
        [_xmppReconnect deactivate];
        [_xmppvCardModule deactivate];
        [_xmppvCardAvatarModule deactivate];
        [_xmppRoster deactivate];
        [_xmppCapabilities deactivate];
        [_xmppMessageArchiving deactivate];
        
        // 3. 断开XMPPStream的连接
        [_xmppStream disconnect];
        
        // 4. 内存清理
        _xmppStream = nil;
        _xmppReconnect = nil;
        _xmppvCardModule = nil;
        _xmppvCardAvatarModule = nil;
        _xmppvCardStorage = nil;
        _xmppRoster = nil;
        _xmppRosterStorage = nil;
        _xmppCapabilities = nil;
        _xmppCapabilitiesCoreDataStorage = nil;
        _xmppMessageArchiving = nil;
        _xmppMessageArchivingCoreDataStorage = nil;
    }
    
    // 通知服务器器用户上线
    - (void)goOnline
    {
        // 1. 实例化一个”展现“,上线的报告,默认类型为:available
        XMPPPresence *presence = [XMPPPresence presence];
        // 2. 发送Presence给服务器
        // 服务器知道“我”上线后,只需要通知我的好友,而无需通知我,因此,此方法没有回调
        [_xmppStream sendElement:presence];
    }
    
    // 通知服务器器用户下线
    - (void)goOffline
    {
        // 1. 实例化一个”展现“,下线的报告
        XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
        // 2. 发送Presence给服务器,通知服务器客户端下线
        [_xmppStream sendElement:presence];
    }
    
    // 连接到服务器
    - (void)connect
    {
        
        // 1. 如果XMPPStream当前已经连接,直接返回
        if ([_xmppStream isConnected]) {
            return;
        }
        //    在C语言中if判断真假:非零即真,如果_xmppStream==nil下面这段代码,与上面的代码结果不同。
        //    if (![_xmppStream isDisconnected]) {
        //        return;
        //    }
        
        // 2. 指定用户名、主机(服务器),连接时不需要password
        NSString *hostName = [[LoginUser sharedLoginUser] hostName];
        NSString *userName = [[LoginUser sharedLoginUser] myJIDName];
        
        // 如果没有主机名或用户名(通常第一次运行时会出现),直接显示登录窗口
    //    if ([hostName isEmptyString] || [userName isEmptyString]) {
    //        [self showStoryboardWithLogonState:NO];
    //        
    //        return;
    //    }
        
        // 3. 设置XMPPStream的JID和主机
        [_xmppStream setMyJID:[XMPPJID jidWithString:userName]];
        [_xmppStream setHostName:hostName];
        
        // 4. 开始连接
        NSError *error = nil;
        [_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error];
        
        // 提示:如果没有指定JID和hostName,才会出错,其他都不出错。
        if (error) {
            NSLog(@"连接请求发送出错 - %@", error.localizedDescription);
        } else {
            NSLog(@"连接请求发送成功!");
        }
    }
    
    #pragma mark 连接到服务器
    - (void)connectWithCompletion:(CompletionBlock)completion failed:(CompletionBlock)faild
    {
        // 1. 记录块代码
        _completionBlock = completion;
        _faildBlock = faild;
        
        // 2. 如果已经存在连接,先断开连接,然后再次连接
        if ([_xmppStream isConnected]) {
            [_xmppStream disconnect];
        }
        
        // 3. 连接到服务器
        [self connect];
    }
    
    // 与服务器断开连接
    - (void)disconnect
    {
        // 1. 通知服务器下线
        [self goOffline];
        // 2. XMPPStream断开连接
        [_xmppStream disconnect];
    }
    
    - (void)logout
    {
        // 1. 通知服务器下线,并断开连接
        [self disconnect];
        
        // 2. 显示用户登录Storyboard
        [self showStoryboardWithLogonState:NO];
    }
    
    #pragma mark - 代理方法
    #pragma mark 连接完成(如果服务器地址不对,就不会调用此方法)
    - (void)xmppStreamDidConnect:(XMPPStream *)sender
    {
        // 从系统偏好读取用户密码
        NSString *password = [[LoginUser sharedLoginUser] password];
        
        if (_isRegisterUser) {
            // 用户注册,发送注册请求
            [_xmppStream registerWithPassword:password error:nil];
        } else {
            // 用户登录,发送身份验证请求
            [_xmppStream authenticateWithPassword:password error:nil];
        }
    }
    
    #pragma mark 注册成功
    - (void)xmppStreamDidRegister:(XMPPStream *)sender
    {
        _isRegisterUser = NO;
        
        // 注册成功,直接发送验证身份请求,从而触发后续的操作
        [_xmppStream authenticateWithPassword:[LoginUser sharedLoginUser].password error:nil];
    }
    
    #pragma mark 注册失败(用户名已经存在)
    - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error
    {
        _isRegisterUser = NO;
        if (_faildBlock != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                _faildBlock();
            });
        }
    }
    
    #pragma mark 身份验证通过
    - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
    {
        if (_completionBlock != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                _completionBlock();
            });
        }
        
        // 通知服务器用户上线
        [self goOnline];
        
        // 显示主Storyboard
        [self showStoryboardWithLogonState:YES];
    }
    
    #pragma mark 密码错误,身份验证失败
    - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
    {
        if (_faildBlock != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                _faildBlock();
            });
        }
        
        // 显示用户登录Storyboard
        [self showStoryboardWithLogonState:NO];
    }
    
    #pragma mark 用户展现变化
    - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
    {
        NSLog(@"接收到用户展现数据 - %@", presence);
        
        // 1. 判断接收到的presence类型是否为subscribe
        if ([presence.type isEqualToString:@"subscribe"]) {
            // 2. 取出presence中的from的jid
            XMPPJID *from = [presence from];
            
            // 3. 接受来自from添加好友的订阅请求
            [_xmppRoster acceptPresenceSubscriptionRequestFrom:from andAddToRoster:YES];
        }
    }
    
    #pragma mark 判断IQ是否为SI请求
    - (BOOL)isSIRequest:(XMPPIQ *)iq
    {
        NSXMLElement *si = [iq elementForName:@"si" xmlns:@"http://jabber.org/protocol/si"];
        NSString *uuid = [[si attributeForName:@"id"]stringValue];
        
        if(si &&uuid ){
            return YES;
        }
        
        return NO;
    }
    
    #pragma mark 接收请求
    - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
    {
        NSLog(@"接收到请求 - %@", iq);
        
        // 0. 判断IQ是否为SI请求
        if ([self isSIRequest:iq]) {
            TURNSocket *socket = [[TURNSocket alloc] initWithStream:_xmppStream toJID:iq.to];
            
            [_socketList addObject:socket];
            
            [socket startWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        } else if ([TURNSocket isNewStartTURNRequest:iq]) {
            // 1. 判断iq的类型是否为新的文件传输请求
            // 1) 实例化socket
            TURNSocket *socket = [[TURNSocket alloc] initWithStream:sender incomingTURNRequest:iq];
            
            // 2) 使用一个数组成员记录住所有传输文件使用的socket
            [_socketList addObject:socket];
            
            // 3)添加代理
            [socket startWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        }
        
        return YES;
    }
    
    #pragma mark 接收消息
    - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
    {
        NSLog(@"接收到用户消息 - %@", message);
        
        // 1. 针对图像数据单独处理,取出数据
        NSString *imageStr = [[message elementForName:@"imageData"] stringValue];
        
        if (imageStr) {
            // 2. 解码成图像
            NSData *data = [[NSData alloc] initWithBase64EncodedString:imageStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
            
            // 3. 保存图像
            UIImage *image = [UIImage imageWithData:data];
            // 4. 将图像保存到相册
            // 1) target 通常用self
            // 2) 保存完图像调用的方法
            // 3) 上下文信息
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
        }
    }
    
    #pragma mark - XMPPRoster代理
    - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
    {
        NSLog(@"接收到其他用户的请求 %@", presence);
    }
    
    #pragma mark - TURNSocket代理
    - (void)turnSocket:(TURNSocket *)sender didSucceed:(GCDAsyncSocket *)socket
    {
        NSLog(@"成功");
        
        // 保存或者发送文件
        // 写数据方法,向其他客户端发送文件
        //    socket writeData:<#(NSData *)#> withTimeout:<#(NSTimeInterval)#> tag:<#(long)#>
        // 读数据方法,接收来自其他客户端的文件
        //    socket readDataToData:<#(NSData *)#> withTimeout:<#(NSTimeInterval)#> tag:<#(long)#>
        
        // 读写操作完成之后断开网络连接
        [socket disconnectAfterReadingAndWriting];
        
        [_socketList removeObject:sender];
    }
    
    - (void)turnSocketDidFail:(TURNSocket *)sender
    {
        NSLog(@"失败");
        
        [_socketList removeObject:sender];
    }
    
    @end
    //  LoginViewController.h
    //  02.用户登录&注册
    //
    
    
    #import <UIKit/UIKit.h>
    
    @interface LoginViewController : UIViewController
    
    @end
    //
    //  LoginViewController.m
    //  02.用户登录&注册
    //
    
    
    #import "LoginViewController.h"
    #import "NSString+Helper.h"
    #import "AppDelegate.h"
    #import "LoginUser.h"
    
    @interface LoginViewController () <UITextFieldDelegate>
    
    @property (weak, nonatomic) IBOutlet UITextField *userNameText;
    @property (weak, nonatomic) IBOutlet UITextField *passwordText;
    @property (weak, nonatomic) IBOutlet UITextField *hostNameText;
    
    @property (weak, nonatomic) IBOutlet UIButton *registerButton;
    @property (weak, nonatomic) IBOutlet UIButton *loginButton;
    
    @end
    
    @implementation LoginViewController
    
    #pragma mark - AppDelegate 的助手方法
    - (AppDelegate *)appDelegate
    {
        return [[UIApplication sharedApplication] delegate];
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        // 1. 拉伸按钮背景图片
        // 1) 登录按钮
        UIImage *loginImage = [UIImage imageNamed:@"LoginGreenBigBtn"];
      loginImage = [loginImage stretchableImageWithLeftCapWidth:loginImage.size.width * 0.5 topCapHeight:loginImage.size.height * 0.5];
        [_loginButton setBackgroundImage:loginImage forState:UIControlStateNormal];
      
        // 2) 注册按钮
        UIImage *registerImage = [UIImage imageNamed:@"LoginwhiteBtn"];
        registerImage = [registerImage stretchableImageWithLeftCapWidth:registerImage.size.width * 0.5 topCapHeight:registerImage.size.height * 0.5];
        [_registerButton setBackgroundImage:registerImage forState:UIControlStateNormal];
        
        // 2. 设置界面文本的初始值
        _userNameText.text = [[LoginUser sharedLoginUser] userName];
        _passwordText.text = [[LoginUser sharedLoginUser] password];
        _hostNameText.text = [[LoginUser sharedLoginUser] hostName];
        
        // 3. 设置文本焦点
        if ([_userNameText.text isEmptyString]) {
            [_userNameText becomeFirstResponder];
        } else {
            [_passwordText becomeFirstResponder];
        }
    }
    
    #pragma mark UITextField代理方法
    - (BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        if (textField == _userNameText) {
            [_passwordText becomeFirstResponder];
        } else if (textField == _passwordText && [_hostNameText.text isEmptyString]) {
            [_hostNameText becomeFirstResponder];
        } else {
            [self userLoginAndRegister:nil];
        }
        
        return YES;
    }
    
    #pragma mark - Actions
    #pragma mark 用户登录
    - (IBAction)userLoginAndRegister:(UIButton *)button
    {
        // 1. 检查用户输入是否完整,在商业软件中,处理用户输入时
        // 通常会截断字符串前后的空格(密码除外),从而可以最大程度地降低用户输入错误
        NSString *userName = [_userNameText.text trimString];
        // 用些用户会使用空格做密码,因此密码不能去除空白字符
        NSString *password = _passwordText.text;
        NSString *hostName = [_hostNameText.text trimString];
        
        if ([userName isEmptyString] ||
            [password isEmptyString] ||
            [hostName isEmptyString]) {
            
            UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"登录信息不完整" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            
            [alter show];
            
            return;
        }
        
        // 2. 将用户登录信息写入系统偏好
        [[LoginUser sharedLoginUser] setUserName:userName];
        [[LoginUser sharedLoginUser] setPassword:password];
        [[LoginUser sharedLoginUser] setHostName:hostName];
        
        // 3. 让AppDelegate开始连接
        // 告诉AppDelegate,当前是注册用户
        NSString *errorMessage = nil;
        
        if (button.tag == 1) {
            [self appDelegate].isRegisterUser = YES;
            errorMessage = @"注册用户失败!";
        } else {
            errorMessage = @"用户登录失败!";
        }
        
        [[self appDelegate] connectWithCompletion:nil failed:^{
            UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"提示" message:errorMessage delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            
            [alter show];
            
            if (button.tag == 1) {
                // 注册用户失败通常是因为用户名重复
                [_userNameText becomeFirstResponder];
            } else {
                // 登录失败通常是密码输入错误
                [_passwordText setText:@""];
                [_passwordText becomeFirstResponder];
            }
        }];
    }
    
    @end
    //  LoginUser.h
    
    
    #import <Foundation/Foundation.h>
    #import "Singleton.h"
    
    @interface LoginUser : NSObject
    single_interface(LoginUser)
    
    @property (strong, nonatomic) NSString *userName;
    @property (strong, nonatomic) NSString *password;
    @property (strong, nonatomic) NSString *hostName;
    
    @property (strong, nonatomic, readonly) NSString *myJIDName;
    
    @end
    //
    //  LoginUser.m
    
    
    #import "LoginUser.h"
    #import "NSString+Helper.h"
    
    #define kXMPPUserNameKey    @"admin"
    #define kXMPPPasswordKey    @"admin"
    #define kXMPPHostNameKey    @"127.0.0.1"
    
    @implementation LoginUser
    single_implementation(LoginUser)
    
    #pragma mark - 私有方法
    - (NSString *)loadStringFromDefaultsWithKey:(NSString *)key
    {
        NSString *str = [[NSUserDefaults standardUserDefaults] stringForKey:key];
        
        return (str) ? str : @"";
    }
    
    #pragma mark - getter & setter
    - (NSString *)userName
    {
        return [self loadStringFromDefaultsWithKey:kXMPPUserNameKey];
    }
    
    - (void)setUserName:(NSString *)userName
    {
        [userName saveToNSDefaultsWithKey:kXMPPUserNameKey];
    }
    
    - (NSString *)password
    {
        return [self loadStringFromDefaultsWithKey:kXMPPPasswordKey];
    }
    
    - (void)setPassword:(NSString *)password
    {
        [password saveToNSDefaultsWithKey:kXMPPPasswordKey];
    }
    
    - (NSString *)hostName
    {
        return [self loadStringFromDefaultsWithKey:kXMPPHostNameKey];
    }
    
    - (void)setHostName:(NSString *)hostName
    {
        [hostName saveToNSDefaultsWithKey:kXMPPHostNameKey];
    }
    
    - (NSString *)myJIDName
    {
        return [NSString stringWithFormat:@"%@@%@", self.userName, self.hostName];
    }
    
    @end
  • 相关阅读:
    logback 打印mybatis sql mybatis 日志打印sql语句和返回结果
    SpringBoot使用logback输出日志并打印sql信息 --经典---
    Logback设置SQL参数打印
    springboot2.X整合mybatis
    异常解决:java.lang.IllegalStateException: Failed to introspect Class
    springboot 启动报错 java.lang.IllegalStateException: Failed to introspect annotated methods on class org
    数据库相关中间件介绍
    QWaitCondition 的正确使用方法(通过 mutex 把有严格时序要求的代码保护起来,同时把 wakeAll() 也用同一个 mutex 保护起来)
    对生产者和消费者问题的另一个解决办法是使用QWaitCondition(封装好了wakeOne,wakeAll,而且与QReadWriteLock对接,几乎是万能的办法)
    Linux定时器的使用(三种方法)
  • 原文地址:https://www.cnblogs.com/niexiaobo/p/4651845.html
Copyright © 2011-2022 走看看