I’m going to show you in this post how to write a workaround for this problem. Many of the existing Apps in the App Store are dependent on this property (uniqueIdentifier) for uniquely identifying a particular device… Apple stopped supporting a unique identifier for iOS (>=5.0).
Solution 1:
Instead of:
[UIDevice uniqueIdentifier];
Use:
[UIDevice uniqueDeviceIdentifier];
But wait… where does this property it come from? The following source code solves the problem. It generates a unique identifier based on the mac address of the device in combination with the bundle identifier.
Advertisement:
Create NSString+MD5Addition.h & NSString+MD5Addition.m
NSString+MD5Addition.h
#import <Foundation/Foundation.h> @interface NSString(MD5Addition) - (NSString *) stringFromMD5; @end
NSString+MD5Addition.m
// // NSString+MD5Addition.m // UIDeviceAddition // // Created by Georg Kitz on 20.08.11. // Copyright 2011 Aurora Apps. All rights reserved. // #import "NSString+MD5Addition.h" #import <CommonCrypto/CommonDigest.h> @implementation NSString(MD5Addition) - (NSString *) stringFromMD5{ if(self == nil || [self length] == 0) return nil; const char *value = [self 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]]; } return [outputString autorelease]; } @end
Create UIDevice+IdentifierAddition.h & UIDevice+IdentifierAddition.m
UIDevice+IdentifierAddition.h
#import <Foundation/Foundation.h> @interface UIDevice (IdentifierAddition) /* * @method uniqueDeviceIdentifier * @description use this method when you need a unique identifier in one app. * It generates a hash from the MAC-address in combination with the bundle identifier * of your app. */ - (NSString *) uniqueDeviceIdentifier; /* * @method uniqueGlobalDeviceIdentifier * @description use this method when you need a unique global identifier to track a device * with multiple apps. as example a advertising network will use this method to track the device * from different apps. * It generates a hash from the MAC-address only. */ - (NSString *) uniqueGlobalDeviceIdentifier; @end
UIDevice+IdentifierAddition.m
#import "UIDevice+IdentifierAddition.h" #import "NSString+MD5Addition.h" #include <sys/socket.h> // Per msqr #include <sys/sysctl.h> #include <net/if.h> #include <net/if_dl.h> @interface UIDevice(Private) - (NSString *) macaddress; @end @implementation UIDevice (IdentifierAddition) //////////////////////////////////////////////////////////////////////////////// #pragma mark - #pragma mark Private Methods // Return the local MAC addy // Courtesy of FreeBSD hackers email list // Accidentally munged during previous update. Fixed thanks to erica sadun & mlamb. - (NSString *) macaddress{ int mib[6]; size_t len; char *buf; unsigned char *ptr; struct if_msghdr *ifm; struct sockaddr_dl *sdl; mib[0] = CTL_NET; mib[1] = AF_ROUTE; mib[2] = 0; mib[3] = AF_LINK; mib[4] = NET_RT_IFLIST; if ((mib[5] = if_nametoindex("en0")) == 0) { printf("Error: if_nametoindex error\n"); return NULL; } if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { printf("Error: sysctl, take 1\n"); return NULL; } if ((buf = malloc(len)) == NULL) { printf("Could not allocate memory. error!\n"); return NULL; } if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { printf("Error: sysctl, take 2"); free(buf); return NULL; } ifm = (struct if_msghdr *)buf; sdl = (struct sockaddr_dl *)(ifm + 1); ptr = (unsigned char *)LLADDR(sdl); NSString *outstring = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)]; free(buf); return outstring; } //////////////////////////////////////////////////////////////////////////////// #pragma mark - #pragma mark Public Methods - (NSString *) uniqueDeviceIdentifier{ NSString *macaddress = [[UIDevice currentDevice] macaddress]; NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; NSString *stringToHash = [NSString stringWithFormat:@"%@%@",macaddress,bundleIdentifier]; NSString *uniqueIdentifier = [stringToHash stringFromMD5]; return uniqueIdentifier; } - (NSString *) uniqueGlobalDeviceIdentifier{ NSString *macaddress = [[UIDevice currentDevice] macaddress]; NSString *uniqueIdentifier = [macaddress stringFromMD5]; return uniqueIdentifier; } @end
You can copy & paste the code above and import it to your code, or download it directly here:
Further explanation of code:
As you can see, there are two methods declared in UIDevice+IdentifierAddition.h =>
- uniqueDeviceIdentifier
- uniqueGlobalDeviceIdentifier
The first one you should use if you need an unique ID throughout one single App since it generates a hash from the MAC-address in combination with the bundle identifier of your App.
The second one you should use if you need an unique ID throughout multiple Apps from which you must track a specific device (for example an advertising network would prefer to use this one here). This method generates a hash from the MAC-address only
You can grab more information here:
https://github.com/gekitz/UIDevice-with-UniqueIdentifier-for-iOS-5/tree/master/Classes
Thanks to Georg Kitz (gekitz) for this amazing piece of code! 🙂
Solution 2:
I won’t go deep into this solution since I have never used it yet. But it is for sure a mention worth here, a very interesting project: It’s called “OpenUDID” and was initiated by Yann Lechelle (ylechelle).
You can find the project page on github: https://github.com/ylechelle/OpenUDID/
Gabe
Hi, instead of taking a risk that Apple might started restricting the access to the Mac address I implemented a subClass of UIDevice and overrode the uniqueIdentifier method to return a new string that was generated like this:
+ (NSString *)_createUUID {
CFUUIDRef uid = CFUUIDCreate(NULL);
CFStringRef ref = CFUUIDCreateString(NULL, uid);
CFRelease(uid);
NSString *uuid = (NSString*)ref;
CFRelease(ref);
return uuid;
}
I forgot to mention that it should only be called once, and stored on the keychain, as each time the method is called, it generates a new unique ID.
Beautiful method, I like it…
and like you mentioned, it should be called once and stored on the keychain. But what if the user erases his phone or a thunder strikes his mother while holding his phone? 😀
The good thing about the uniqueIdentifier was, that you could for example save game state, user data, etc. organized through the uniqueIdentifier. If the user erases your app and then downloads it again, the App could get all the information back to the phone.
But you could also think of a possibility using your method, too.
Just some thoughts about it 🙂
Thanks for your post!
My understanding about why Apple decided to remove access to uniqueIdentifier is to prevent app tracking as it was be abused by a lot of companies. They introduced 2 types of new identifier, but for our purposes, we chose not to use either of them. The choice of using CFUUIDCreate come from an Apple engineer – no idea who exactly.
If an app is deleted, the value stored in the keyChain remains. If it is re-installed, it will have the same ID. If the device is reset, it will be deleted.
This is absolutely true!!!
Hey, you’re the goto eexprt. Thanks for hanging out here.
Is this working with the iOS7 beta?
I seem to get the same number back for different users.
Works GREAT under iOS5 and iOS6.
Hello Kurt Baum
WeBe Bluetooth Mouse works GREAT on iOS7 Beta 🙂
I seem to get the same number back for different users in database
Hi I used solution 1 and is gives me “uniqueDeviceIdentifier” same for two devices. What is wrong there ?
Can you please tell me how to solve this ?