使用Keychain解决iOS的UDID问题

iOS 5之前可以通过uniqueIdentifier来获取设备的UDID.由于隐私问题,Apple已经禁止使用该方法来获取UDID了.

“Do not use the uniqueIdentifier
property. To create a unique identifier specific to your app, you can call the CFUUIDCreate
function to create a UUID, and write it to the defaults database using the NSUserDefaults
class.”

Apple建议使用CFUUIDCreate方法来创建UUID,然后保存在NSUserDefaults.

此外Apple还提供了两种方法来获取:

  • IFA/IDFA (Identifier for Advertisers),局限就是应用必须是显示了广告,并且用户可以重置.
  • IFV/IDFV (Identifier for Vendor): 应用删除了,UUID也会重置.

解决方案:

使用Keychain,将UUID当做密码信息来存储.

大致流程:

1, 通过AdSupport获取UUID(原因AdSupport可以跨应用)

+ (NSString *)appleIFA {
    NSString *ifa = nil;
    Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
    if (ASIdentifierManagerClass) { // a dynamic way of checking if AdSupport.framework is available
        SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
        id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector);
        SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
        NSUUID *advertisingIdentifier = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector);
        ifa = [advertisingIdentifier UUIDString];
    }
    return ifa;
}

2, 如果不支持AdSupport,那就使用IFV/IDFV (Identifier for Vendor)

+ (NSString *)appleIFV {
    if(NSClassFromString(@"UIDevice") && [UIDevice instancesRespondToSelector:@selector(identifierForVendor)]) {
        // only available in iOS >= 6.0
        return [[UIDevice currentDevice].identifierForVendor UUIDString];
    }
    return nil;
}

3, 如果以上的都不支持,使用CFUUIDRef手动创建UUID

+ (NSString *)randomUUID {
    if(NSClassFromString(@"NSUUID")) { // only available in iOS >= 6.0
        return [[NSUUID UUID] UUIDString];
    }
    CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
    CFStringRef cfuuid = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
    CFRelease(uuidRef);
    NSString *uuid = [((__bridge NSString *) cfuuid) copy];
    CFRelease(cfuuid);
    return uuid;
}

最后,添加到Keychain

+ (void)setValue:(NSString *)value forKey:(NSString *)key inService:(NSString *)service {
    NSMutableDictionary *keychainItem = [[NSMutableDictionary alloc] init];
    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
    keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAlways;
    keychainItem[(__bridge id)kSecAttrAccount] = key;
    keychainItem[(__bridge id)kSecAttrService] = service;
    keychainItem[(__bridge id)kSecValueData] = [value dataUsingEncoding:NSUTF8StringEncoding];
    SecItemAdd((__bridge CFDictionaryRef)keychainItem, NULL);
}

如果更新了provisioning profile的话, Keychain data会丢失.所以我们应该将UUID在NSUserDefault备份.

[[NSUserDefaults standardUserDefaults] setObject:@”123456-1234-1234-12345678” forKey:@"deviceUID"];
[[NSUserDefaults standardUserDefaults] synchronize];

99%参考Persistent, Cross-Install Device Identifier on iOS: Using Keychain:https://blog.onliquid.com/persistent-device-unique-identifier-ios-keychain

源码:https://gist.github.com/miguelcma/e8f291e54b025815ca46

源码有一个死循环bug,看留言可以发现问题

你可能感兴趣的:(使用Keychain解决iOS的UDID问题)