Uniquely identifying an iOS user Uniquely identifying an iOS user ios ios

Uniquely identifying an iOS user


The correct solution is to use the iCloud Key-Value Store, where you can store a unique user ID without requiring any kind of authentication or user information such as an email address.

The end result is a random UUID (nothing that actually IDENTIFIES the user), that is different for each user, but will persist across multiple devices registered to the same iCloud account.

We define a field in our iCloud KV Store, let's call it userID. When the app is launched, we first check the userID. If it's there, then we're all set with our user's unique ID. If not, then this is the first time we're running for this user. We generate a random UUID and store it in the KV Store under userID. That's all there is to it.

Our experience shows that this UUID is unique per iTunes account. If your end-users are using family sharing, those accounts will be assigned different UUIDs (which may or may not be desirable but there's nothing you can do about it). Any number of devices launching under the same iTunes account will see the same UUID.

This approach is totally legit and should be approved by Apple with no issues.

Obviously you must enable iCloud Key-Value store on Xcode under Capabilities, just turn on the iCloud Switch.

Here's a simple class that implements this concept in Objective-C:

@implementation EEUserID+ (NSUUID *) getUUID{    NSUUID *uuid = nil;    NSString *uuidString = [[NSUbiquitousKeyValueStore defaultStore] stringForKey: @"EEUserID"];    if (uuidString == nil)    {        // This is our first launch for this iTunes account, so we generate random UUID and store it in iCloud:        uuid = [NSUUID UUID];        [[NSUbiquitousKeyValueStore defaultStore] setString: uuid.UUIDString forKey: @"EEUserID"];        [[NSUbiquitousKeyValueStore defaultStore] synchronize];    }    else    {        uuid = [[NSUUID alloc] initWithUUIDString: uuidString];    }        return uuid;}+ (NSString *) getUUIDString{    NSUUID *uuid = [self getUUID];    if (uuid != nil)        return uuid.UUIDString;    else        return nil;}+ (void) load{    // get changes that might have happened while this    // instance of your app wasn't running    [[NSUbiquitousKeyValueStore defaultStore] synchronize];}@end

And for the header file:

#import <Foundation/Foundation.h>@interface EEUserID : NSObject+ (NSUUID *) getUUID;+ (NSString *) getUUIDString;@end

To use, all you have to do is invoke:

NSString *uniqueIDForiTunesAccount = [EEUserID getUUIDString];

Enjoy.


Generate a UUID with this:

NSString *UUID() {    CFUUIDRef cfuuid = CFUUIDCreate(NULL);     NSString *uuid =  (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfuuid);     CFRelease(cfuuid);    return uuid;}

Nothing here is deprecated or frowned on by Apple -- in fact, it is the way they suggest you do it. Store the generated UUID in the keychain and it will be there -- even if the user uninstalls and reinstalls your app. The UUID is unique for the device and the time it was generated.

You can then use various schemes to have the user group their devices together -- iCloud, or some sort of key that you deliver from the server.

Good luck!

Addition:

Here's how I store it in the keychain, using the uuid as a username and generating a random password:

uuid = UUID();[keychainItemWrapper setObject:uuid forKey:(__bridge_transfer id)kSecAttrAccount];NSString *pass_token = randomString(10);[keychainItemWrapper setObject:pass_token forKey:(__bridge_transfer id)kSecValueData];

Note that all of this can be done without any input from the user.

Update:

MCSMKeychainItem has a great solution to UUID generation and storage with [MCSMApplicationUUIDKeychainItem applicationUUID]. The library also has [MCSMGenericKeychainItem genericKeychainItemWithService:service username:username password:password]. Together, these functions take care of everything mentioned above. Easy to install with CocoaPods too.


Radu

Excellent question. We have resolved the issue you describe by integrating Urban Airship (urbanairship.com) into our apps. Urban Airship offers a grab bag of functionality to support in-app purchases, receipt validation, subscription recovery, content delivery, and Apple Push Notification.

One of the great things about Urban Airship is it's ability to identify a "user", not a device, but a "user" by email address. It's not really an advertised "feature"... more of a by-product of its intended functionality.

Here is what we have found and how we can leverage Urban Airship to solve the problem you have.

When a user installs your app that has Urban Airship integrated to it, UA somehow generates a UDID-like number, which at the moment, simply identifies the device.

However, if you leverage the subscription recovery components of Urban Airship you can have the user enter an email address. Once the user enters their email address on the first device... that generated ID becomes their main method of user identification and is associated with that email address. When they enter their email address on subsequent devices, Urban Airship will trigger an email validation process. Once the user completes the validation process it updates the ID on the new device to be the same as the ID of the first device and so on. The best part of it is... it's all auto-magic! You simply integrate the components and have the user enter their email address. You should be able to have it all up and working within an hour.

It also offers functionality to allow the user to change the email address associated with all of their devices.

We have actually implemented and it works VERY well!

NOTE: As of JULY 1st 2013 Urban Airship is deprecating the subscription & recovery functionality