Is it possible to organize a user identification system based on UDID?

This class is meant specifically: https://developer.apple.com/library/ios/documentation/AdSupport/Reference/ASIdentifierManager_Ref/index.html

The description says that its main purpose is to display ads. You can paint in more detail what it gives, whether the UDID will change after you restart the phone, reinstall the application, etc.

    2 answers 2

    Two identifier options are available:

    IDFA (advertisingIdentifier) ​​- the user can reset it in the settings at any time.

    IDFV (identifierForVendor) - if the last application is removed (and often it is the only one) from one publisher, then the identifier is deleted and a new one is created during a new installation.

    But there is a detour that is not prohibited by Apple (I personally verified it, the application in the AppStore): we generate the identifier and save it in KeyChain. When an application is deleted, the key in KeyChain remains and remains the same when reinstalled. Theoretically, the user can remove it from KeyChain, but this is good if one in a thousand guesses.

    Here is the ready code:

    Using:

    NSString *udid = [DeviceUID uid]; // дальше отправляем udid на сервер и там регистрируем 

    DeviceUID.h:

     #import <Foundation/Foundation.h> @interface DeviceUID : NSObject + (NSString *)uid; @end 

    DeviceUID.m:

     #import "DeviceUID.h" @import UIKit; @interface DeviceUID () @property(nonatomic, strong, readonly) NSString *uidKey; @property(nonatomic, strong, readonly) NSString *uid; @end @implementation DeviceUID @synthesize uid = _uid; #pragma mark - Public methods + (NSString *)uid { return [[[DeviceUID alloc] initWithKey:@"deviceUID"] uid]; } #pragma mark - Instance methods - (id)initWithKey:(NSString *)key { self = [super init]; if (self) { _uidKey = key; _uid = nil; } return self; } /*! Returns the Device UID. The UID is obtained in a chain of fallbacks: - Keychain - NSUserDefaults - Apple IFV (Identifier for Vendor) - Generate a random UUID if everything else is unavailable At last, the UID is persisted if needed to. */ - (NSString *)uid { if (!_uid) _uid = [[self class] valueForKeychainKey:_uidKey service:_uidKey]; if (!_uid) _uid = [[self class] valueForUserDefaultsKey:_uidKey]; if (!_uid) _uid = [[self class] appleIFV]; if (!_uid) _uid = [[self class] appleIFA]; if (!_uid) _uid = [[self class] randomUUID]; [self save]; return _uid; } /*! Persist UID to NSUserDefaults and Keychain, if not yet saved */ - (void)save { if (![DeviceUID valueForUserDefaultsKey:_uidKey]) { [DeviceUID setValue:_uid forUserDefaultsKey:_uidKey]; } if (![DeviceUID valueForKeychainKey:_uidKey service:_uidKey]) { [DeviceUID setValue:_uid forKeychainKey:_uidKey inService:_uidKey]; } } #pragma mark - Keychain methods /*! Create as generic NSDictionary to be used to query and update Keychain items. * param1 * param2 */ + (NSMutableDictionary *)keychainItemForKey:(NSString *)key service:(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; return keychainItem; } /*! Sets * param1 * param2 */ + (OSStatus)setValue:(NSString *)value forKeychainKey:(NSString *)key inService:(NSString *)service { NSMutableDictionary *keychainItem = [[self class] keychainItemForKey:key service:service]; keychainItem[(__bridge id)kSecValueData] = [value dataUsingEncoding:NSUTF8StringEncoding]; return SecItemAdd((__bridge CFDictionaryRef)keychainItem, NULL); } + (NSString *)valueForKeychainKey:(NSString *)key service:(NSString *)service { OSStatus status; NSMutableDictionary *keychainItem = [[self class] keychainItemForKey:key service:service]; keychainItem[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue; keychainItem[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; CFDictionaryRef result = nil; status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, (CFTypeRef *)&result); if (status != noErr) { return nil; } NSDictionary *resultDict = (__bridge_transfer NSDictionary *)result; NSData *data = resultDict[(__bridge id)kSecValueData]; if (!data) { return nil; } return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } #pragma mark - NSUserDefaults methods + (BOOL)setValue:(NSString *)value forUserDefaultsKey:(NSString *)key { [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; return [[NSUserDefaults standardUserDefaults] synchronize]; } + (NSString *)valueForUserDefaultsKey:(NSString *)key { return [[NSUserDefaults standardUserDefaults] objectForKey:key]; } #pragma mark - UID Generation methods + (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; } + (NSString *)appleIFV { if(NSClassFromString(@"UIDevice") && [UIDevice instancesRespondToSelector:@selector(identifierForVendor)]) { // only available in iOS >= 6.0 return [[UIDevice currentDevice].identifierForVendor UUIDString]; } return nil; } + (NSString *)randomUUID { if(NSClassFromString(@"NSUUID")) { 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; } @end 

    This code is on GitHub - DeviceUID , but be careful, there is an error - it is said about it in the comments below.

    In the code that I posted here, it has already been fixed.

    • Thank! Do not know, but how can a user delete it from KeyChain? and in what cases can retire still, not at the initiative of the user? except for device recovery. - Artur Mirrov 9:01 pm
    • Open Settings -> Safari -> Passwords and find the corresponding entry there. At the initiative of the user otherwise not delete. And this should be done when the application is removed from the device, since in parallel the copy is stored in UserDefaults. - Igor

    advertisingIdentifier - changes when erasing the phone

    identifierForVendor - may change even when reinstalling the application

    In general, there is no full-fledged replacement for the iron " CFUUIDCreateString(NULL, uuid); " no, but it is prohibited to use by Apple politicians

    • identifierforvendor remains the same for all applications from one vendor, but it can be reset in the settings - Max Mikheyenko
    • The fact that the same in the framework of the vendor - here you are right, it was conceived as a cross-app identifier. About reset - I will add: OS automatically deletes the current identifierForVendor when the user deletes the last application from the given vendor. If the user later installs one or more applications from this manufacturer, a new identifier will be generated. - iosp