環信聊天游客和用戶身份的兩種狀態

最近搞環信聊天,需求是游客身份也可以進行聊天,當用戶注冊了我們的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閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容