环信聊天游客和用户身份的两种状态

最近搞环信聊天,需求是游客身份也可以进行聊天,当用户注册了我们的APP后也需要把游客身份切换过来进行聊天,首先我们的环信注册,登录全都放前段处理了,下面就按照我们的需求逻辑来如何切换游客

1.APP用户的注册,也就注册环信,APP的登录返回的有用户ID,这个时候并没有让他登录环信,只是保存了返回的ID,下面就是用ID来判断该用户是否注册过环信的依据

下面用图来表示:

下面就上代码了,第一步从图中第一步来说判断userID是否存在

这个地方是在点击聊天按钮开始判断的

-(void)releaseInfo:(UIButton*)sender{

NSString*Hxusername=[userdic objectForKey:@"useid"];//获取保存的userID

NSString*phonestr=  [[NSUserDefaults standardUserDefaults]objectForKey:@"phonenum"];

NSString*chatid=[[phonestr md5String]substringFromIndex:16];//这个是获取客服的欢信ID

//单例里面处理用户是否登录,以及游客随机分配uuid来注册环信IM号

DataManager*datamage= [DataManager shareDataManager];

//判断用户ID是否存在,也就证明是否注册过环信

if (Hxusername.length>0) {

if ([datamage loginKefuSDK])//判断用户是否登录

{//单聊

ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];

}

}else{

//游客身份的判断

if ([datamage customelogin]) {

//单聊

ChattingViewController *chatController = [[ChattingViewController alloc] initWithConversationChatter:chatid conversationType:EMConversationTypeChat];[self.navigationController pushViewController:chatController animated:YES];

}

}

}上面这是按钮方法里面的数据下面来说,DataManager*datamage= [DataManager shareDataManager];这个单利的方法


DataManager.h

@interface DataManager : NSObject

-(BOOL)customelogin;//判断游客之前是否有登录

-(void)requestchattphone;//获取美容院客服聊天的对象电话

@end

DataManager.m

@implementation DataManager

+(instancetype)shareDataManager{

static DataManager *manager;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

manager = [[DataManager alloc] init];

});

return manager;

}

//userID存在的时候 登录IM

- (BOOL)loginKefuSDK {

NSDictionary*userdic=[[NSUserDefaults standardUserDefaults]objectForKey:@"userMessage"];//接受用户是否登录

NSString*loguser=[NSString stringWithFormat:@"%@",[userdic objectForKey:@"useid"] ];

EMClient *client = [EMClient sharedClient];

//用户已经登录

if (client.isLoggedIn) {

if ([loguser isEqualToString:client.currentUsername])//当前登录用户的ID和即将要登录人的ID是否一样

{

return YES;

}else

{

EMError *error = [[EMClient sharedClient] logout:YES];

if (!error) {

NSLog(@"退出成功");

}

}

}//这里APP用户登录环信的密码统统是123456

EMError *error = [[EMClient sharedClient] loginWithUsername:loguser password:@"123456"];

if (!error) { //IM登录成功

return YES;

} else { //登录失败

NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);

return NO;

}

return NO;

}


//游客身份的登录方法

-(BOOL)customelogin

{

EMClient *client = [EMClient sharedClient];

//用户已经登录

if (client.isLoggedIn) {

return YES;

}//该用户没有注册,来用改设备UUID来给用户注册环信,并登录环信

if (![self registerIMuser]) {

return NO;

}

EMError *error = [[EMClient sharedClient] loginWithUsername:self.Hxusername password:@"123456"];

if (!error) { //IM登录成功

return YES;

} else { //登录失败

NSLog(@"登录失败 error code :%d,error description:%@",error.code,error.errorDescription);

return NO;

}

return NO;

}

- (BOOL)registerIMuser { //举个栗子。注册建议在服务端创建环信id与自己app的账号一一对应,\

而不要放到APP中,可以在登录自己APP时从返回的结果中获取环信账号再登录环信服务器

EMError *error = nil;

NSString *newUser = [self getrandomUsername];

self.Hxusername = newUser;

error = [[EMClient sharedClient] registerWithUsername:newUser password:@"123456"];

if (error &&  error.code != EMErrorUserAlreadyExist) {

NSLog(@"注册失败;error code:%d,error description :%@",error.code,error.errorDescription);

return NO;

}return YES;

}


//创建一个随机的用户名,这里是设备UUID来代替的


- (NSString *)getrandomUsername {

//第一种方法:

/*NSString *username = nil;

UIDevice *device = [UIDevice currentDevice];//创建设备对象

NSString *deviceUID = [[NSString alloc] initWithString:[[device identifierForVendor] UUIDString]];

if ([deviceUID length] == 0) {

CFUUIDRef uuid = CFUUIDCreate(NULL);

if (uuid)

{

deviceUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);

CFRelease(uuid);

}

username = [deviceUID stringByReplacingOccurrencesOfString:@"-" withString:@""];

username = [username stringByAppendingString:[NSString stringWithFormat:@"%u",arc4random()%100000]];

return username;*/

//第二种方法

//加上build ID是为了保证设备的唯一性,如果这里的buildID换了,设备的uuid也会变,这里的解决办法也就是放倒了钥匙串里面,不会因卸载程序,程序升级设备的标识会改变

NSString *SERVICE_NAME = NAVI_TEST_BUNDLE_ID;//最好用程序的bundle id

NSString * str =  [SFHFKeychainUtils getPasswordForUsername:@"UUID" andServiceName:SERVICE_NAME error:nil];  // 从keychain获取数据

if ([str length]<=0)

{

str  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];  // 保存UUID作为手机唯一标识符[SFHFKeychainUtils storeUsername:@"UUID"   andPassword:str    forServiceName:SERVICE_NAME updateExisting:1  error:nil];  // 往keychain添加数据

}

str = [str stringByReplacingOccurrencesOfString:@"-" withString:@""];

return str;

}在这里用到了一个类来处理的UUID不变(APP卸载后不会改变)


SFHFKeychainUtils.h

#import@interface SFHFKeychainUtils : NSObject

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;

+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

@end

SFHFKeychainUtils.m

#import "SFHFKeychainUtils.h"

static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";

#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR

@interface SFHFKeychainUtils (PrivateMethods)

+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;

@end

#endif

@implementation SFHFKeychainUtils

#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { if (!username || !serviceName) { *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; return nil; }      SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];      if (*error || !item) { return nil; }

// from Advanced Mac OS X Programming, ch. 16

UInt32 length;

char *password;

SecKeychainAttribute attributes[8];

SecKeychainAttributeList list;

attributes[0].tag = kSecAccountItemAttr;

attributes[1].tag = kSecDescriptionItemAttr;

attributes[2].tag = kSecLabelItemAttr;

attributes[3].tag = kSecModDateItemAttr;

list.count = 4;

list.attr = attributes;

OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);

if (status != noErr)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

return nil;

}

NSString *passwordString = nil;

if (password != NULL)

{ char passwordBuffer[1024];

if (length > 1023) {length = 1023;

}

strncpy(passwordBuffer, password, length);

passwordBuffer[length] = '\0';

passwordString = [NSString stringWithCString:passwordBuffer];

}SecKeychainItemFreeContent(&list, password);CFRelease(item);return passwordString;

}

+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {

if (!username || !password || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

return;

}

OSStatus status = noErr;

SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];

if (*error && [*error code] != noErr) {

return;

}

*error = nil;

if (item) {

status = SecKeychainItemModifyAttributesAndData(item,

NULL,

strlen([password UTF8String]),

[password UTF8String]);

CFRelease(item);

}

else {

status = SecKeychainAddGenericPassword(NULL,

strlen([serviceName UTF8String]),

[serviceName UTF8String],

strlen([username UTF8String]),

[username UTF8String],

strlen([password UTF8String]),

[password UTF8String],

NULL);

}

if (status != noErr) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

}

+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];

return;

}

*error = nil;

SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];

if (*error && [*error code] != noErr) {

return;

}

OSStatus status;

if (item) {

status = SecKeychainItemDelete(item);

CFRelease(item);

}

if (status != noErr) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

}

+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

return nil;

}

*error = nil;

SecKeychainItemRef item;

OSStatus status = SecKeychainFindGenericPassword(NULL,

strlen([serviceName UTF8String]),

[serviceName UTF8String],

strlen([username UTF8String]),

[username UTF8String],

NULL,

NULL,

&item);

if (status != noErr) {

if (status != errSecItemNotFound) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return nil;

}

return item;

}

#else

+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {

if (!username || !serviceName) {

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return nil;

}

if (error != nil) {

*error = nil;

}

// Set up a query dictionary with the base query attributes: item type (generic), username, and service

NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil];

NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, nil];

NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys];

// First do a query for attributes, in case we already have a Keychain item with no password data set.

// One likely way such an incorrect item could have come about is due to the previous (incorrect)

// version of this code (which set the password as a generic attribute instead of password data).

NSMutableDictionary *attributeQuery = [query mutableCopy];

[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnAttributes];

CFTypeRef attrResult = NULL;

OSStatus status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) attributeQuery, &attrResult);

//NSDictionary *attributeResult = (__bridge_transfer NSDictionary *)attrResult;

if (status != noErr) {

// No existing item found--simply return nil for the password

if (error != nil && status != errSecItemNotFound) {

//Only return an error if a real exception happened--not simply for "not found."

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

return nil;

}

// We have an existing item, now query for the password data associated with it.

NSMutableDictionary *passwordQuery = [query mutableCopy];

[passwordQuery setObject: (id) kCFBooleanTrue forKey: (__bridge_transfer id) kSecReturnData];

CFTypeRef resData = NULL;

status = SecItemCopyMatching((__bridge_retained CFDictionaryRef) passwordQuery, (CFTypeRef *) &resData);

NSData *resultData = (__bridge_transfer NSData *)resData;

if (status != noErr) {

if (status == errSecItemNotFound) {

// We found attributes for the item previously, but no password now, so return a special error.

// Users of this API will probably want to detect this error and prompt the user to

// re-enter their credentials.  When you attempt to store the re-entered credentials

// using storeUsername:andPassword:forServiceName:updateExisting:error

// the old, incorrect entry will be deleted and a new one with a properly encrypted

// password will be added.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];

}

}

else {

// Something else went wrong. Simply return the normal Keychain API error code.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

}

}

return nil;

}

NSString *password = nil;

if (resultData) {

password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];

}

else {

// There is an existing item, but we weren't able to get password data for it for some reason,

// Possibly as a result of an item being incorrectly entered by the previous code.

// Set the -1999 error so the code above us can prompt the user again.

if (error != nil) {

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];

}

}

return password;

}

+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error

{

if (!username || !password || !serviceName)

{

if (error != nil)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return NO;

}

// See if we already have a password entered for these credentials.

NSError *getError = nil;

NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];

if ([getError code] == -1999)

{

// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.

// Delete the existing item before moving on entering a correct one.

getError = nil;

[self deleteItemForUsername: username andServiceName: serviceName error: &getError];

if ([getError code] != noErr)

{

if (error != nil)

{

*error = getError;

}

return NO;

}

}

else if ([getError code] != noErr)

{

if (error != nil)

{

*error = getError;

}

return NO;

}

if (error != nil)

{

*error = nil;

}

OSStatus status = noErr;

if (existingPassword)

{

// We have an existing, properly entered item with a password.

// Update the existing item.

if (![existingPassword isEqualToString:password] && updateExisting)

{

//Only update if we're allowed to update existing.  If not, simply do nothing.

NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,

kSecAttrService,

kSecAttrLabel,

kSecAttrAccount,

nil];

NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,

serviceName,

serviceName,

username,

nil];

NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];

status = SecItemUpdate((__bridge_retained CFDictionaryRef) query, (__bridge_retained CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (__bridge_transfer NSString *) kSecValueData]);

}

}

else

{

// No existing entry (or an existing, improperly entered, and therefore now

// deleted, entry).  Create a new entry.

NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,

kSecAttrService,

kSecAttrLabel,

kSecAttrAccount,

kSecValueData,

nil];

NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,

serviceName,

serviceName,

username,

[password dataUsingEncoding: NSUTF8StringEncoding],

nil];

NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];

status = SecItemAdd((__bridge_retained CFDictionaryRef) query, NULL);

}

if (error != nil && status != noErr)

{

// Something went wrong with adding the new item. Return the Keychain error code.

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

return NO;

}

return YES;

}

+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error

{

if (!username || !serviceName)

{

if (error != nil)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];

}

return NO;

}

if (error != nil)

{

*error = nil;

}

NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil];

NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil];

NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];

OSStatus status = SecItemDelete((__bridge_retained CFDictionaryRef) query);

if (error != nil && status != noErr)

{

*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];

return NO;

}

return YES;

}

#endif

@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容