Friday 16 August 2013

Deprecated uniqueIdentifier in iOS7 work around.


This code is a work around for the deprecated method to retrieve the devices Unique ID in iOS7 [[UIDevice currentDevice] uniqueIdentifier] 

We currently have a variety of services which are using the UDID as an authenticator, in iOS7 the uniqueIdentifier method is deprecated meaning that only an app level unique id is the supported method of identification. If an app upgrades to iOS7 then the app will fail to work as it will no longer authenticate with the server and would require re-registration. 

To avoid this problem it has been proposed to install an update to the existing apps before the upgrade to iOS7 this would copy the UDID to the keychain, which could then still be used for authentication post iOS 7 roll out. 

This code is an example of how this could be implemented, instead of using [[UIDevice currentDevice] uniqueIdentifier] the developer would reference the "MNSAppIdentifier.h" header and then call [MNSAppIdentifier uniqueIdentifier] to retrieve the UDID instead. 

How it works: Internally when you first call [MNSAppIdentifier uniqueIdentifier] the system checks to see if the UDID is stored in the keychain, if it is it simply returns it to the user. If the UDID is not in the keychain and the user is using iOS6 or below then the code internally calls [[UIDevice currentDevice] uniqueIdentifier] and then stores this value in the keychain before returning the value to the calling code. If the user is using iOS7 and there is no stored value in the keychain then [[[UIDevice currentDevice] identifierForVendor] UUIDString] is called and the value again stored in the keychain before being returned to the calling code. Subsequent calls to [MNSAppIdentifier uniqueIdentifier] in both cases would return the keychain value. If the user upgrades from iOS6 to iOS7 and assuming the app has been installed and used on iOS6 then the value from the keychain which corresponds to the original device UDID will be returned and the app will continue to function normally.

NSUInteger DeviceSystemMajorVersion();
NSUInteger DeviceSystemMajorVersion() {
    static NSUInteger _deviceSystemMajorVersion = -1;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion]
                                       componentsSeparatedByString:@"."] objectAtIndex:0] intValue];
    });
    return _deviceSystemMajorVersion;
}

#define kIS_LESS_THAN_IOS7 (DeviceSystemMajorVersion() < 7)

#import "MNSAppIdentifier.h"

@implementation MNSAppIdentifier

+(NSString*) uniqueIdentifier {
    
    // check to see if we have the UUID in the keychain
    MNSKeychainItemWrapper * wrapper = [[MNSKeychainItemWrapper alloc] initWithIdentifier:@"GenericUDIDKeychain" accessGroup:nil];
    NSString* udid = [wrapper objectForKey:kSecValueData];
    
    if(udid == nil || [udid length] < 1) {
        udid = [MNSAppIdentifier generateUDID];
        [wrapper setObject:udid forKey:kSecValueData];
    }
    
    
    NSLog(@"Got ID: %@",udid);
    
    return udid;

}

+(NSString*) resetIdentifier {
    
    MNSKeychainItemWrapper * wrapper = [[MNSKeychainItemWrapper alloc] initWithIdentifier:@"GenericUDIDKeychain" accessGroup:nil];
    [wrapper resetKeychainItem];
    
}

+(NSString*) generateUDID {
    
if (kIS_LESS_THAN_IOS7) {
        // get the device UDID
        return [[UIDevice currentDevice] uniqueIdentifier]; // standard udid
} else {
        // we need to generate the UDID from the vendor id this should be shared across team apps
        return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
    
}

@end

Click here to download some example code