iOS獲取UUID,並使用keychain存儲

(絕對有用)iOS獲取UUID,並使用keychain存儲

 
 
UDID被棄用,使用UUID來做爲設備的惟一標識。獲取到UUID後,若是用NSUserDefaults存儲,當程序被卸載後重裝時,再得到的UUID和以前就不一樣了。使用keychain存儲能夠保證程序卸載重裝時,UUID不變。但當刷機或者升級系統後,UUID仍是會改變的。但這還是目前爲止最佳的解決辦法了,若是有更好的解決辦法,歡迎留言。
(我整理的解決辦法的參考來源:http://blog.k-res.net/archives/1081.html)
 

首先對於以前寫的文章本身沒有用真機測試,以及這段時間你們加個人QQ或者私信我也好,有的甚至找到了個人旺旺上,可是我沒有給你們及時回覆很抱歉。html

 

進入正題,我以後又試了下本身寫的方法,發現用模擬器能夠,可是真機不能夠。app

真機卸載後和以前獲取的uuid不一樣,究其緣由就是以前生成的uuid並無被keychain成功存儲。學習

 

因此問題在keychain.我以前給你們的KeychainItemWrapper類是蘋果官方的,可是有些朋友反映運行時會崩潰。測試

除此以外,Code Signing Entitlements的建立方法也不夠嚴謹。ui

 

因此,請你們從新跟我走一遍吧。url

 

 

1.新建一個工程,看一下本身的Bundle Id.這個Bundle Id 要和你用真機測試時的證書上面的Bundle Id相匹配。spa

(絕對有用)iOS獲取UUID,並使用keychain存儲

好比個人是 house.xianrou.xianrou.net

 

2.Target - Capabilities - Keychain Sharing - ONcode

 

(絕對有用)iOS獲取UUID,並使用keychain存儲

 

 
(絕對有用)iOS獲取UUID,並使用keychain存儲

這步主要目的是打開Keychain Sharing,將它由灰色狀態的OFF改成藍色狀態的ON。orm

打開以後的變化以下:

 

(絕對有用)iOS獲取UUID,並使用keychain存儲

 

 

(絕對有用)iOS獲取UUID,並使用keychain存儲

左側的目錄會自動生成Entitlements文件,不須要本身建立了。

 

也就是說,Bundle Identifier、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一個元素,它們要保持上圖所示的一致性。

設置好了之後能夠運行下程序,沒問題能夠進行下一步。

 

3.傳說中的uuid類和keychain類來啦

既然蘋果的keychain方法會崩潰並且有些複雜,咱們只保存一個uuid的話能夠用下面的簡單方法:

(這也是我本身百度的keychain拷貝別人的,而後改改)

UUID.h

#import  尖括號(Foundation/Foundation.h)

 

@interface UUID : NSObject

 

+(NSString *)getUUID;

 

 

@end

 

UUID.m

 

#import "UUID.h"

#import "KeyChainStore.h"

 

 

@implementation UUID

 

+(NSString *)getUUID

{

    NSString * strUUID = (NSString *)[KeyChainStore load:@"com.company.app.usernamepassword"];

    

    //首次執行該方法時,uuid爲空

    if ([strUUID isEqualToString:@""] || !strUUID)

    {

        //生成一個uuid的方法

        CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);

        

        strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));

   

        //將該uuid保存到keychain

        [KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID];

        

    }

    return strUUID;

}

 

@end

 

KeyChainStore.h

 

#import 尖括號(Foundation/Foundation.h)

 

@interface KeyChainStore : NSObject

 

+ (void)save:(NSString *)service data:(id)data;

+ (id)load:(NSString *)service;

+ (void)deleteKeyData:(NSString *)service;

 

@end

 

KeyChainStore.m

#import "KeyChainStore.h"

 

 

@implementation KeyChainStore

 

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {

    return [NSMutableDictionary dictionaryWithObjectsAndKeys:

            (id)kSecClassGenericPassword,(id)kSecClass,

            service, (id)kSecAttrService,

            service, (id)kSecAttrAccount,

            (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,

            nil];

}

 

+ (void)save:(NSString *)service data:(id)data {

    //Get search dictionary

    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

    //Delete old item before add new item

    SecItemDelete((CFDictionaryRef)keychainQuery);

    //Add new object to search dictionary(Attention:the data format)

    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];

    //Add item to keychain with the search dictionary

    SecItemAdd((CFDictionaryRef)keychainQuery, NULL);

}

 

+ (id)load:(NSString *)service {

    id ret = nil;

    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

    //Configure the search setting

    //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue

    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];

    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];

    CFDataRef keyData = NULL;

    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {

        @try {

            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];

        } @catch (NSException *e) {

            NSLog(@"Unarchive of %@ failed: %@", service, e);

        } @finally {

        }

    }

    if (keyData)

        CFRelease(keyData);

    return ret;

}

 

+ (void)deleteKeyData:(NSString *)service {

    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];

    SecItemDelete((CFDictionaryRef)keychainQuery);

}

 

 

@end

 
將這兩個類添加到工程中
 
4.新建一個pch文件,而後pch文件的內容以下:

 

#ifndef PrefixHeader_pch

#define PrefixHeader_pch

 

#define  KEY_USERNAME_PASSWORD @"com.company.app.usernamepassword"

#define  KEY_USERNAME @"com.company.app.username"

#define  KEY_PASSWORD @"com.company.app.password"

 

#endif

 
pch文件的建立方法可參考:http://blog.csdn.net/huang2009303513/article/details/40375235
有可能會在填Prefix Header 即pch文件的路徑那裏報錯,最近又學習到一種更好的方式$(SRCROOT)/$(PROJECT_NAME)/PrefixHeader.pch,其中$(PROJECT_NAME)是相對工程名,比上面的方法更便捷.

 

5.在viewcontroller.m裏面執行以下代碼

  NSString * uuid= [UUID getUUID];

  NSLog(@"uuid=%@",uuid);

  獲得的uuid相似於這種
   uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960
  
而後卸載掉,再從新運行,看先後獲得的uuid是否是同樣吧!

本文來源於:森女鍾溪妍的博客

相關文章
相關標籤/搜索