简体   繁体   中英

Uniquely identifying an iOS user

I'd like to create a user account on the server for new users of an app, but I'd also like to not ask the user to type in anything. Ideally, I'd like this to be automatic, like with Game Center.

But I'm wondering if it's possible. Is there anything I can use to uniquely identify the user? It's highly unlikely that I can find out the user's Apple ID. Also, the device ID uniquely identifies the device, not the user, so it would be useless if the user has more devices...

Is there anything else I can use?

About privacy - I don't want to find out anything behind the user's back. I have absolutely no problem with asking the user for access to their information (and if there is an API that grants me this information, it would be great if the API asks this itself). As Steve Jobs himself said, this is what privacy is all about - forcing apps to ask the user for permission before doing anything with their private data.

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

Generating a UUID like Erik suggested is definitely a solid solution. The only (small) caveat might be that in the event of a full iDevice restore, the keychain will be wiped too. The application will generate a new UUID in this scenario and the iDevice cannot be related to an existing user anymore.

Another approach to uniquely identify an iDevice is to generate a unique id based on the MAC address of the network interface and combine it with the app bundle id. This methods yields a unique id that is application specific and durable. It will not change when the iDevice is wiped, restored or when the app is removed and reinstalled. Because of this, there is no need to store this id in the keychain.

Fortunately there are already some people that have created a little library to achieve this. The code can be found on GitHub:

https://github.com/gekitz/UIDevice-with-UniqueIdentifier-for-iOS-5

Swift3/4+

Using iCloud record ID (CKRecordID). This ID will be unique for iTunes account.

let container = CKContainer.default()
container.fetchUserRecordID() {
    recordID, error in

    if let err = error {
        print(err.localizedDescription)
    }
    else if recID = recordID {
        print("fetched ID \(recID.recordName ?? "NA")")
    }
}

For automatically created records, the ID name string is based on a UUID and is therefore guaranteed to be unique.

Please note that if device doesn't have an iCloud account, or has an iCloud account with restricted access, this returns CKError.Code.notAuthenticated error

An alternative to Erik's suggestion is a third-party library called OpenUDID . You can generate a unique identifier on a per-device basis, and it's dead simple:

#include "OpenUDID.h"
NSString* openUDID = [OpenUDID value];

How about using game center and iCloud together to identify user?

It is less likely a user do not play game and do not sync their data.

You can use third party solutions like SECUREUDID also check out this

It is not an absolute sure solution (and only works when iCloud enabled), but it is very easy to implement: Generate a unique ID once (like UUID) and store it in iCloud key-value-storage.

The only problem is that advanced users know how to delete content from the iCloud.

To prevent the user just editing the unique ID stored in iCloud to someone else ID you can add some kind of secret to.

Today our APP was rejected, becouse of Game Center user identification. It is in App Store Guidelines: https://developer.apple.com/appstore/resources/approval/guidelines.html .

You can not use uniqe Game Center ID in G: format.

The only way now for us is to use iOS6 Facebook integration.

UPDATE: Our new version with automatic FB logon passed review process and is available on App Store ( Slovni Duel ). Uses also inApp subscription linked to FB profile.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM