獲取UUID及keychain存儲

UDID被棄用,使用UUID來作爲設備的唯一標識。獲取到UUID後,如果用NSUserDefaults存儲,當程序被卸載後重裝時,再獲得的UUID和之前就不同了。使用keychain存儲可以保證程序卸載重裝時,UUID不變。但當刷機或者升級系統後,UUID還是會改變的。但這仍是目前爲止最佳的解決辦法了,如果有更好的解決辦法,歡迎留言。

給大家兩個類:

UUID.h中的代碼:

import 尖括號(Foundation/Foundation.h)

@interface UUID : NSObject

+(NSString *)getUUID;

@end

UUID.m中的代碼:

import “UUID.h”

import 尖括號(Foundation/Foundation.h)

import “KeychainItemWrapper.h”

@implementation UUID

+(NSString *)getUUID
{
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]

                                initWithIdentifier:@"UUID"

                                                                             accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];


NSString *strUUID = [keychainItem objectForKey:(id)CFBridgingRelease(kSecValueData)];

//首次執行該方法時,uuid爲空
if ([strUUID isEqualToString:@""])

{
    CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);

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

    [keychainItem setObject:strUUID forKey:(id)CFBridgingRelease(kSecValueData)];

}
return strUUID;

}

@end

第二個類是蘋果官方的一個Demo裏封裝的,Demo的下載地址:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9

KeychainItemWrapper.h中的代碼:

import 尖括號(UIKit/UIKit.h)

@interface KeychainItemWrapper : NSObject
{
NSMutableDictionary *keychainItemData;
NSMutableDictionary *genericPasswordQuery;
}

@property (nonatomic, retain) NSMutableDictionary *keychainItemData;
@property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery;

  • (id)initWithIdentifier: (NSString )identifier accessGroup:(NSString ) accessGroup;
  • (void)setObject:(id)inObject forKey:(id)key;
  • (id)objectForKey:(id)key;
  • (void)resetKeychainItem;

@end

KeychainItemWrapper.m中的代碼:

import “KeychainItemWrapper.h”

import 尖括號(Security/Security.h)

@interface KeychainItemWrapper (PrivateMethods)

  • (NSMutableDictionary )secItemFormatToDictionary:(NSDictionary )dictionaryToConvert;
  • (NSMutableDictionary )dictionaryToSecItemFormat:(NSDictionary )dictionaryToConvert;

  • (void)writeToKeychain;

@end

@implementation KeychainItemWrapper

@synthesize keychainItemData, genericPasswordQuery;

  • (id)initWithIdentifier: (NSString )identifier accessGroup:(NSString ) accessGroup;
    {
    if (self = [super init])
    {
    genericPasswordQuery = [[NSMutableDictionary alloc] init];

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)kSecClass];
[genericPasswordQuery setObject:identifier forKey:(id)CFBridgingRelease(kSecAttrGeneric)];

if (accessGroup != nil)
{

if TARGET_IPHONE_SIMULATOR

else

[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

endif

}

    [genericPasswordQuery setObject:(id)CFBridgingRelease(kSecMatchLimitOne) forKey:(id)kSecMatchLimit];
    [genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];

    NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];

    NSMutableDictionary *outDictionary = nil;

    if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)
    {

        [self resetKeychainItem];

[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];
if (accessGroup != nil)
{

if TARGET_IPHONE_SIMULATOR

else

[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

endif

}
}
else
{
self.keychainItemData = [self secItemFormatToDictionary:outDictionary];
}

[outDictionary release];
}

return self;
}

  • (void)dealloc
    {
    [keychainItemData release];
    [genericPasswordQuery release];

[super dealloc];
}

  • (void)setObject:(id)inObject forKey:(id)key
    {
    if (inObject == nil) return;
    id currentObject = [keychainItemData objectForKey:key];
    if (![currentObject isEqual:inObject])
    {
    [keychainItemData setObject:inObject forKey:key];
    [self writeToKeychain];
    }
    }

  • (id)objectForKey:(id)key
    {
    return [keychainItemData objectForKey:key];
    }

  • (void)resetKeychainItem
    {
    OSStatus junk = noErr;
    if (!keychainItemData)
    {
    self.keychainItemData = [[NSMutableDictionary alloc] init];
    }
    else if (keychainItemData)
    {
    NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData];
    junk = SecItemDelete((CFDictionaryRef)tempDictionary);
    NSAssert( junk == noErr || junk == errSecItemNotFound, @”Problem deleting current dictionary.” );
    }

    [keychainItemData setObject:@”” forKey:(id)kSecAttrAccount];
    [keychainItemData setObject:@”” forKey:(id)kSecAttrLabel];
    [keychainItemData setObject:@”” forKey:(id)kSecAttrDescription];
    [keychainItemData setObject:@”” forKey:(id)kSecValueData];
    }

  • (NSMutableDictionary )dictionaryToSecItemFormat:(NSDictionary )dictionaryToConvert
    {
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];
    [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];

    return returnDictionary;
    }

  • (NSMutableDictionary )secItemFormatToDictionary:(NSDictionary )dictionaryToConvert
    {
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
    [returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    NSData *passwordData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr)
    {
    [returnDictionary removeObjectForKey:(id)kSecReturnData];
    NSString *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length]
    encoding:NSUTF8StringEncoding] autorelease];
    [returnDictionary setObject:password forKey:(id)kSecValueData];
    }
    else
    {
    NSAssert(NO, @”Serious error, no matching item found in the keychain.\n”);
    }

    [passwordData release];

return returnDictionary;
}

  • (void)writeToKeychain
    {
    NSDictionary *attributes = NULL;
    NSMutableDictionary *updateItem = NULL;
    OSStatus result;

    if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)
    {
    updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
    [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];
    NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
    [tempCheck removeObjectForKey:(id)kSecClass];

if TARGET_IPHONE_SIMULATOR

[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];

endif

    result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);

NSAssert( result == noErr, @”Couldn’t update the Keychain Item.” );
}
else
{
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @”Couldn’t add the Keychain Item.” );
}
}

@end

PS: 1. KeychainItemWrapper類在官方Demo裏面也有,如果我複製出錯了,大家可以在上面的鏈接上下載官方Demo.
2.使用時要添加Security.framework
3. 尤爲注意的是,UUID類下面這句代碼中group的設置方法。
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]

                                     initWithIdentifier:@"UUID"

                                     accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];
(1)在項目相同的目錄下創建KeychainAccessGroups.plist文件。
     (絕對有用)iOS獲取UUID,並使用keychain存儲

該文件的結構中最頂層的節點必須是一個名爲“keychain-access-groups”的Array,並且該Array中每一項都是一個描述分組的NSString。YOUR_BUNDLE_SEED.com.yourcompany.userinfo就是要設置的組名。
(2)在項目相同的目錄下創建KeychainAccessGroups.plist文件。在Target-Build Settings-Code Signing欄下的Code Signing Entitlements右側添加KeychainAccessGroups.plist,如下圖。
(絕對有用)iOS獲取UUID,並使用keychain存儲

到此,工作就完成了。
首次安裝程序時,打印出一個uuid,當把程序卸載後,再用getUUID獲得
uuid,打印出來的結果和之前相同。證明達到目的。

測試代碼:

NSString * uuid= [UUID getUUID];
NSLog(@”uuid=%@”,uuid);
測試結果:
uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章