将唯一ID实现为UUID并将其保存在Keychain中

时间:2013-12-02 20:45:11

标签: ios objective-c uuid

我需要在我的应用程序中使用唯一ID,我知道,根据我的研究,我们不能再使用UDID,使用UUID作为设备唯一ID并将其保存在钥匙串中将确保即使用户重新安装我的应用程序,唯一ID也保持不变。

我在stackoverflow上找到了类似问题的答案之一下面的代码但是,我需要知道如何访问keychainUtils和IDManager? 提前感谢您的帮助。

+ (NSString *) getUniqueUUID {
    NSError * error;
    NSString * uuid = [KeychainUtils getPasswordForUsername:USER_NAME   andServiceName:SERVICE_NAME error:&error];
    if (error) {
        NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
        return nil;
    }
    if (!uuid) {
        DLog(@"No UUID found. Creating a new one.");
        uuid = [IDManager GetUUID];
        uuid = [Util md5String:uuid];
        [KeychainUtils storeUsername:kBuyassUser andPassword:uuid forServiceName:kIdOgBetilngService updateExisting:YES error:&error];
        if (error) {
            NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
            return nil;
        }
    }
    return uuid;
}

3 个答案:

答案 0 :(得分:4)

我想提供这个解决方案。它不需要第三方代码。如果它绝对需要,它会读取或写入KeyChain,因此到目前为止提供的解决方案效率更高。此外,UUID将写入钥匙串访问组,以便您可以在应用程序之间共享此UUID。只需在方法开头更改kKeyChainVendorIDAccessGroup即可。

+(NSUUID *)persistentIdentifierForVendor
{
    static NSString * const kKeyChainVendorID = @"co.cwbrn.PersistentIdentifier";
    static NSString * const kKeyChainVendorIDAccessGroup = @"<AppIdentifier>.<keychain-access-group-identifier>";

    // First, check NSUserDefaults so that we're not hitting the KeyChain every single time
    NSString *uuidString = [[NSUserDefaults standardUserDefaults] stringForKey:kKeyChainVendorIDGroup];
    BOOL vendorIDMissingFromUserDefaults = (uuidString == nil || uuidString.length == 0);

    if (vendorIDMissingFromUserDefaults) {
        // Check to see if a UUID is stored in the KeyChain
        NSDictionary *query = @{
                                (__bridge id)kSecClass:             (__bridge id)kSecClassGenericPassword,
                                (__bridge id)kSecAttrAccount:       kKeyChainVendorID,
                                (__bridge id)kSecAttrService:       kKeyChainVendorID,
                                (__bridge id)kSecAttrAccessGroup:   kKeyChainVendorIDAccessGroup,
                                (__bridge id)kSecMatchLimit:        (__bridge id)kSecMatchLimitOne,
                                (__bridge id)kSecReturnAttributes:  (__bridge id)kCFBooleanTrue
                               };
        CFTypeRef attributesRef = NULL;
        OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)query, &attributesRef);
        if (result == noErr) {
            // There is a UUID, so try to retrieve it
            NSDictionary *attributes = (__bridge_transfer NSDictionary *)attributesRef;
            NSMutableDictionary *valueQuery = [NSMutableDictionary dictionaryWithDictionary:attributes];

            [valueQuery setObject:(__bridge id)kSecClassGenericPassword  forKey:(__bridge id)kSecClass];
            [valueQuery setObject:(__bridge id)kCFBooleanTrue            forKey:(__bridge id)kSecReturnData];

            CFTypeRef passwordDataRef = NULL;
            OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)valueQuery, &passwordDataRef);
            if (result == noErr) {
                NSData *passwordData = (__bridge_transfer NSData *)passwordDataRef;
                uuidString = [[NSString alloc] initWithBytes:[passwordData bytes]
                                                      length:[passwordData length]
                                                    encoding:NSUTF8StringEncoding];
            }
        }
    }

    // Failed to read the UUID from the KeyChain, so create a new UUID and store it
    if (uuidString == nil || uuidString.length == 0) {
        // Generate the new UIID
        CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
        uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
        CFRelease(uuidRef);

        // Now store it in the KeyChain
        NSDictionary *query = @{    (__bridge id)kSecClass:             (__bridge id)kSecClassGenericPassword,
                                    (__bridge id)kSecAttrAccount:       kKeyChainVendorID,
                                    (__bridge id)kSecAttrService:       kKeyChainVendorID,
                                    (__bridge id)kSecAttrAccessGroup:   kKeyChainVendorIDAccessGroup,
                                    (__bridge id)kSecAttrLabel:         @"",
                                    (__bridge id)kSecAttrDescription:   @"",
                                    (__bridge id)kSecAttrAccessible:    (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
                                    (__bridge id)kSecValueData:         [uuidString dataUsingEncoding:NSUTF8StringEncoding]
                                };

        OSStatus result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
        if (result != noErr) {
            NSLog(@"ERROR: Couldn't add to the Keychain. Result = %ld; Query = %@", result, query);
            return nil;
        }
    }

    // Save UUID to NSUserDefaults so that we can avoid the KeyChain next time
    if (vendorIDMissingFromUserDefaults) {
        [[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:kKeyChainVendorIDGroup];
    }

    return [[NSUUID alloc] initWithUUIDString:uuidString];
}

答案 1 :(得分:0)

这是我用于唯一设备ID并使用符合ARC兼容实用程序的USHFKeychainUtils将其保存在钥匙串中的内容。这是一个完整的代码,我希望能为你节省时间!!

+ (NSString *) getUniqueUUID {
NSError * error;
NSString * uuid = [SFHFKeychainUtils getPasswordForUsername:USER_NAME andServiceName:SERVICE_NAME error:&error];
if (error) {
    NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
    return nil;
}
if (!uuid) {
    NSLog(@"No UUID found. Creating a new one.");
    uuid = [self GetUUID];
    uuid = [self md5String:uuid];
    [SFHFKeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
    if (error) {
        NSLog(@"Error geting unique UUID for this device! %@", [error localizedDescription]);
        return nil;
    }
}
return uuid;
}


+ (NSString *)md5String:(NSString *)plainText

{
    if(plainText == nil || [plainText length] == 0)
        return nil;

    const char *value = [plainText UTF8String];
    unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(value, strlen(value), outputBuffer);

    NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
        [outputString appendFormat:@"%02x",outputBuffer[count]];
    }
    NSString * retString = [NSString stringWithString:outputString];
    return retString;
}


+ (NSString *)GetUUID
{
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return (__bridge NSString *)string;
}

答案 2 :(得分:-1)

我使用更简单的生成器,不需要任何外部库支持。

    CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
    CFStringRef uuidStringRef = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
    CFRelease(uuidRef);

    /* save it to defaults, or whatever you want to do with it */
    [userDefaults setValue:(__bridge NSString *)uuidStringRef forKey:UNIQUE_DEVICE_ID];
    [userDefaults synchronize];

    CFRelease(uuidStringRef);