iOS 唯一用户标识符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7273014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:21:49  来源:igfitidea点击:

iOS unique user identifier

iphoneobjective-cios

提问by Drabuna

I'm writting an application for iphone, which communicates with my server using REST. The main problem is, I need to identify user somehow. Not so long ago, we were allowed to use UDID, but now its not allowed anymore. So what should I use instead? I need some kind of identifier on iphone, so user will delete application, install it again, and he will get same id.

我正在为 iphone 编写一个应用程序,它使用 REST 与我的服务器进行通信。主要问题是,我需要以某种方式识别用户。不久前,我们被允许使用 UDID,但现在不再允许了。那我应该用什么代替呢?我在 iphone 上需要某种标识符,因此用户将删除应用程序,重新安装它,他将获得相同的 ID。

采纳答案by Ole Begemann

Firstly, the UDID is only deprecatedin iOS 5. That doesn't mean it's gone (yet).

首先,UDID在 iOS 5 中被弃用。这并不意味着它已经消失(还)。

Secondly, you should ask yourself if you really need such a thing. What if the user gets a new device and installs your app on that? Same user, but the UDID has changed. Meanwhile, the original user might have sold his old device so now a completely new user installs your app and you think it's a different person based on the UDID.

其次,你应该问问自己是否真的需要这样的东西。如果用户获得一台新设备并在其上安装您的应用会怎样?相同的用户,但 UDID 已更改。同时,原始用户可能已经卖掉了他的旧设备,所以现在一个全新的用户安装了您的应用程序,并且您认为这是基于 UDID 的另一个人。

If you don't need the UDID, use CFUUIDCreate()to create a unique ID and save it to the user defaults on the first launch (use CFUUIDCreateString()to convert the UUID to a string first). It will survive backups and restores and even come along with the original user when they switch to a new device. It's in many ways a better option that the UDID.

如果您不需要 UDID,请使用CFUUIDCreate()来创建唯一 ID 并在首次启动时将其保存到用户默认值中(用于先将CFUUIDCreateString()UUID 转换为字符串)。它将在备份和恢复中幸存下来,甚至在原始用户切换到新设备时也会出现。在很多方面,UDID 都是更好的选择。

If you really need a unique deviceidentifier (it doesn't sound like you do), go for the MAC address as pointed out in Suhail's answer.

如果您确实需要一个唯一的设备标识符(听起来不像您需要的),请按照 Suhail 的回答中指出的方式查找 MAC 地址。

回答by Moomio

I used CFUUIDCreate()to create a UUID:

我曾经CFUUIDCreate()创建过一个 UUID:

+ (NSString *)GetUUID {
  CFUUIDRef theUUID = CFUUIDCreate(NULL);
  CFStringRef string = CFUUIDCreateString(NULL, theUUID);
  CFRelease(theUUID);
  return [(NSString *)string autorelease];
}

Then set the above UUID to my NSString:

然后将上面的 UUID 设置为我的 NSString:

NSString *UUID = [nameofclasswhereGetUUIDclassmethodresides UUID];

I then stored that UUID to the Keychain using SSKeyChain

然后我使用SSKeyChain将该 UUID 存储到钥匙串

To set the UUID with SSKeyChain:

使用 SSKeyChain 设置 UUID:

[SSKeychain setPassword:UUID forService:@"com.yourapp.yourcompany" account:@"user"];

To Retrieve it:

要检索它:

NSString *retrieveuuid = [SSKeychain passwordForService:@"com.yourapp.yourcompany" account:@"user"];

When you set the UUID to the Keychain, it will persist even if the user completely uninstalls the App and then installs it again.

当您将 UUID 设置为 Keychain 时,即使用户完全卸载应用程序然后再次安装它,它也会持续存在。

To make sure ALL devices have the same UUID in the Keychain.

确保所有设备在钥匙串中具有相同的 UUID。

  1. Setup your app to use iCloud.
  2. Save the UUID that is in the Keychain to NSUserDefaults as well.
  3. Pass the UUID in NSUserDefaults to the Cloud with Key-Value Data Store.
  4. On App first run, Check if the Cloud Data is available and set the UUID in the Keychain on the New Device.
  1. 设置您的应用程序以使用 iCloud。
  2. 将钥匙串中的 UUID 也保存到 NSUserDefaults。
  3. 使用键值数据存储将 NSUserDefaults 中的 UUID 传递到云。
  4. 在应用程序首次运行时,检查云数据是否可用并在新设备的钥匙串中设置 UUID。

You now have a Unique Identifier that is persistent and shared/synced with all devices.

您现在拥有一个唯一标识符,该标识符是持久的并与所有设备共享/同步。

回答by Quamber Ali

I was updating my application that was working based only on Unique Identifier which supported iOS 4.3 and above.So,

我正在更新仅基于支持iOS 4.3 及更高版本的唯一标识符的应用程序所以,

1) I was unable to use [UIDevice currentDevice].uniqueIdentifier;as it was no longer available

1)我无法使用,[UIDevice currentDevice].uniqueIdentifier;因为它不再可用

2) I could not use [UIDevice currentDevice].identifierForVendor.UUIDStringbecause it was Available in iOS 6.0 and later only and was unable to use for lower iOS versions.

2) 我无法使用,[UIDevice currentDevice].identifierForVendor.UUIDString因为它仅适用于 iOS 6.0 及更高版本,无法用于较低的 iOS 版本。

3) The mac address was not an option as it wasn't allowed in iOS-7

3) mac 地址不是一个选项,因为它在 iOS-7 中是不允许的

4) OpenUDIDwas deprecatedsome time ago and also had issues with iOS-6.

4) OpenUDID 不久前弃用,并且在 iOS-6 上也有问题。

5) Advertisement identifiers were also not available for iOS-5 and below

5) 广告标识符也不适用于 iOS-5 及以下版本

Finally this was what i did

最后这就是我所做的

a) Added SFHFKeychainUtilsto the project

a)在项目中添加了SFHFKeychainUtils

b) Generated CFUUID key String

b) 生成的 CFUUID 密钥字符串

 CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));

c) Saved it to Key Chain Utils or else it will generate a new Unique Each Time

c) 将其保存到 Key Chain Utils否则每次都会生成一个新的 Unique

Final Code

最终代码

+ (NSString *)GetDeviceID {
    NSString *udidString;
   udidString = [self objectForKey:@"deviceID"];
    if(!udidString)
    {
    CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
    CFRelease(cfuuid);
        [self setObject:udidString forKey:@"deviceID"];
    }
    return udidString;
}

+(void) setObject:(NSString*) object forKey:(NSString*) key
{
    NSString *objectString = object;
    NSError *error = nil;
    [SFHFKeychainUtils storeUsername:key
                         andPassword:objectString
                      forServiceName:@"LIB"
                      updateExisting:YES
                               error:&error];

    if(error)
        NSLog(@"%@", [error localizedDescription]);
}

+(NSString*) objectForKey:(NSString*) key
{
    NSError *error = nil;
    NSString *object = [SFHFKeychainUtils getPasswordForUsername:key
                                                  andServiceName:@"LIB"
                                                           error:&error];
    if(error)
        NSLog(@"%@", [error localizedDescription]);

    return object;
}

enter image description here

在此处输入图片说明

For further Details

欲知详情

回答by mikeho

Some people want to know more about the different options available, and if you do, take a look at the answer from @NSQuamber.java. If you want to know how to use the NSUUID and sync with iCloud, keep reading. This post ended up being more long-winded than I originally wanted, but I hope that it makes it clear for anyone taking these steps!

有些人想了解更多有关可用不同选项的信息,如果您想了解,请查看@NSQuamber.java 的答案。如果您想知道如何使用 NSUUID 并与 iCloud 同步,请继续阅读。这篇文章最终比我最初想要的更冗长,但我希望它能让任何采取这些步骤的人都清楚!

Using NSUUID

使用 NSUUID

I use the NSUUID class to create the UUID:

我使用 NSUUID 类来创建 UUID:

NSUUID *uuid = [NSUUID UUID];

Then to create the string, you only need to call the UUIDStringmethod:

然后要创建字符串,您只需要调用该UUIDString方法:

NSString *uuidString = [uuid UUIDString];

or do it in one line:

或在一行中完成:

NSString *uuidString = [[NSUUID UUID] UUIDString];

IMHO, this is much easier than trying to use CFUUIDCreate and have a method you have to maintain.

恕我直言,这比尝试使用 CFUUIDCreate 并拥有必须维护的方法容易得多。



EDIT: I now use UICKeyChainStore

编辑:我现在使用UICKeyChainStore

To set the UUID with UICKeyChainStore:

使用 UICKeyChainStore 设置 UUID:

UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"];
keychain[@"com.sample.MyApp.user"] = userID;

To retrieve it:

要检索它:

UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"];
NSString *userID = keychain[@"com.sample.MyApp.user"];


I then stored that UUID to the Keychain using SSKeyChain

然后我使用SSKeyChain将该 UUID 存储到钥匙串

To set the UUID with SSKeyChain:

使用 SSKeyChain 设置 UUID:

[SSKeychain setPassword:userID forService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"];

To retrieve it:

要检索它:

NSString *userID = [SSKeychain passwordForService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"];

When you set the UUID to the Keychain, it will persist even if the user completely uninstalls the App and then installs it again.

当您将 UUID 设置为 Keychain 时,即使用户完全卸载应用程序然后再次安装它,它也会持续存在。

Syncing with iCloud

与 iCloud 同步

So it's useful to make sure that all the user's devices use the same UUID. This is to ensure that data is synchronized across all the devices, rather than each device thinking it is a unique user.

因此,确保所有用户的设备都使用相同的 UUID 很有用。这是为了确保数据在所有设备之间同步,而不是每个设备都认为它是唯一的用户。

There were several questions in the comments for my answer on how synchronization would work, so now that I've got it all working, I'll provide more details.

评论中有几个问题是关于同步如何工作的,所以现在我已经完成了所有工作,我将提供更多详细信息。

Configuring iCloud/NSUbiquitousKeyValueStore Use

配置 iCloud/NSUbiquitousKeyValueStore 使用

  1. Click on your project at the top of the Project Navigatorin Xcode.
  2. Select Capabilities.
  3. Turn on iCloud.
  1. 在 Xcode的Project Navigator顶部单击您的项目。
  2. 选择能力
  3. 打开 iCloud。

It should now look something like this: Screenshot of iCloud enabled

它现在应该是这样的: 启用 iCloud 的屏幕截图

Using NSUbiquitousKeyValueStore

使用 NSUbiquitousKeyValueStore

Using iCloud is fairly simple. To write:

使用 iCloud 相当简单。来写:

// create the UUID
NSUUID *userUUID = [[NSUUID UUID];
// convert to string
NSString *userID = [userUUID UUIDString];
// create the key to store the ID
NSString *userKey = @"com.sample.MyApp.user";

// Save to iCloud
[[NSUbiquitousKeyValueStore defaultStore] setString:userID forKey:userKey];

To read:

阅读:

// create the key to store the ID
NSString *userKey = @"com.sample.MyApp.user";

// read from iCloud
NSString *userID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];

Before you can write the NSUbiquitousKeyValueStore documentationstates that you are required to read from iCloud first. To force a read, call the following method:

在您编写NSUbiquitousKeyValueStore 文档之前,您需要先从 iCloud 读取数据。要强制读取,请调用以下方法:

[[NSUbiquitousKeyValueStore defaultStore] synchronize];

To have your app receive notifications of changes in iCloud, add the following notification:

要让您的应用接收 iCloud 更改通知,请添加以下通知:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iCloudStoreDidChange:)
                                             name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                           object:[NSUbiquitousKeyValueStore defaultStore]];

Creating the UUID with iCloud

使用 iCloud 创建 UUID

Combining NSUUID, SSKeychain and NSUbiquityKeyValueStore, here's my method for generating a user ID:

结合 NSUUID、SSKeychain 和 NSUbiquityKeyValueStore,这是我生成用户 ID 的方法:

- (NSUUID *)createUserID {
    NSString *userKey = @"com.sample.MyApp.user";
    NSString *KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";
    NSString *userID = [SSKeychain passwordForService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    if (userID) {
        return [[NSUUID UUID] initWithUUIDString:userID];
    }

    // check iCloud
    userID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];
    if (!userID) {
        // none in iCloud, create one
        NSUUID *newUUID = [NSUUID UUID];
        userID = [newUUID UUIDString];
        // save to iCloud
        [[NSUbiquitousKeyValueStore defaultStore] setString:userID forKey:userKey];
    }

    // store the user ID locally
    [SSKeychain setPassword:userID forService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    return [[NSUUID UUID] initWithUUIDString:userID];
}

How to ensure that your User ID is in sync

如何确保您的用户 ID 同步

Because writing to iCloud requires a download of any data in iCloud first, I put the synchronize call at the top of the (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsmethod. I also added the notification registration there as well. That allows me to detect any changes from iCloud and handle them appropriately.

因为写入 iCloud 需要先下载 iCloud 中的任何数据,所以我将同步调用放在(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法的顶部。我还在那里添加了通知注册。这使我能够检测来自 iCloud 的任何更改并适当处理它们。

Here's a sample:

这是一个示例:

NSString *const USER_KEY = @"com.sample.MyApp.user";
NSString *const KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";

- (void)iCloudStoreDidChange:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NSNumber *changeReason = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey];
    NSArray *keysChanged = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey];
    if (changeReason) {
        switch ([changeReason intValue]) {
            default:
            case NSUbiquitousKeyValueStoreServerChange:
            case NSUbiquitousKeyValueStoreInitialSyncChange:
                // check changed keys
                for (NSString *keyChanged in keysChanged) {
                    NSString *iCloudID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:keyChanged];
                    if (![keyChanged isEqualToString:USER_KEY]) {
                        NSLog(@"Unknown key changed [%@:%@]", keyChanged, iCloudID);
                        continue;
                    }

                    // get the local key
                    NSString *localID = [SSKeychain passwordForService:keyChanged account:KEYCHAIN_ACCOUNT_IDENTIFIER];
                    if (!iCloudID) {
                        // no value from iCloud
                        continue;
                    }
                    // local ID not created yet
                    if (!localID) {
                        // save the iCloud value locally
                        [SSKeychain setPassword:iCloudID forService:keyChanged account:KEYCHAIN_ACCOUNT_IDENTIFIER];
                        continue; // continue because there is no user information on the server, so no migration
                    }

                    if ([iCloudID isEqualToString:localID]) {
                        // IDs match, so continue
                        continue;
                    }

                    [self handleMigration:keyChanged from:localID to:iCloudID];
                }

                break;
            case NSUbiquitousKeyValueStoreAccountChange:
                // need to delete all data and download new data from server
                break;
        }
    }
}

When the application is launched or when it comes back to the foreground, I force a synchronization with iCloud and verify the integrity of the UUIDs.

当应用程序启动或返回前台时,我强制与 iCloud 同步并验证 UUID 的完整性。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self configureSecKeyWrapper];
    // synchronize data from iCloud first. If the User ID already exists, then we can initialize with it
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];
    [self checkUseriCloudSync];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // synchronize changes from iCloud
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];
    [self checkUseriCloudSync];
}

- (BOOL)checkUseriCloudSync {
    NSString *userKey = @"com.sample.MyApp.user";
    NSString *KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";
    NSString *localID = [SSKeychain passwordForService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    NSString *iCloudID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];

    if (!iCloudID) {
        // iCloud does not have the key saved, so we write the key to iCloud
        [[NSUbiquitousKeyValueStore defaultStore] setString:localID forKey:userKey];
        return YES;
    }

    if (!localID || [iCloudID isEqualToString:localID]) {
        return YES;
    }

    // both IDs exist, so we keep the one from iCloud since the functionality requires synchronization
    // before setting, so that means that it was the earliest one
    [self handleMigration:userKey from:localID to:iCloudID];
    return NO;
}

If which UUID came first matters

如果哪个 UUID 先出现很重要

In my use case of my UserID, I assumed that the value in iCloud is the one to keep, since it would be the first UUID pushed to iCloud, regardless of which device generated the UUID first. Most of you would probably take the same path, since you won't really care which UUID it resolves to, as long as it resolves to a single one. For those of you who actually care about which came first, I suggest you store both the UUID and the timestamp generation ([[NSDate date] timeIntervalSince1970]) so that you can check to see which one is older:

在我的 UserID 用例中,我假设 iCloud 中的值是要保留的值,因为它将是推送到 iCloud 的第一个 UUID,无论哪个设备首先生成 UUID。大多数人可能会采取相同的路径,因为您不会真正关心它解析为哪个 UUID,只要它解析为单个 UUID。对于那些真正关心哪个先出现的人,我建议您同时存储 UUID 和时间戳生成 ( [[NSDate date] timeIntervalSince1970]),以便您可以查看哪个更旧:

// using dates
NSDate *uuid1Timestamp = [NSDate dateWithTimeIntervalSince1970:timestamp1];
NSDate *uuid2Timestamp = [NSDate dateWithTimeIntervalSince1970:timestamp2];
NSTimeInterval timeDifference = [uuid1 timeIntervalSinceDate:uuid2Timestamp];

// or just subtract
double timeDifference = timestamp1 - timestamp2;

回答by Suhail Patel

There is a nice alternative on Github which generates a Unique Identifier based on a combination of Mac Address and the Bundle Identifier which works pretty well: UIDevice-with-UniqueIdentifier-for-iOS-5

Github 上有一个不错的替代方案,它基于 Mac 地址和捆绑标识符的组合生成唯一标识符,效果很好:UIDevice-with-UniqueIdentifier-for-iOS-5

回答by ThE uSeFuL

In iOS7 Apple has introduced a read only property called "identifierForVendor" in the UIDevice class. If you decide to use it you should make note of the following,

在 iOS7 中,Apple 在 UIDevice 类中引入了一个名为“identifierForVendor”的只读属性。如果您决定使用它,则应注意以下事项,

  • This value could be nil if it is accessed before the user unlocks the device
  • The value changes when the user deletes all of that vendor's apps from the device and subsequently reinstalls one or more of them.
  • The value can also change when installing test builds using Xcode or when installing an app on a device using ad-hoc distribution.
  • 如果在用户解锁设备之前访问该值,则该值可能为零
  • 当用户从设备中删除该供应商的所有应用程序并随后重新安装其中一个或多个应用程序时,该值会发生变化。
  • 使用 Xcode 安装测试版本或使用临时分发在设备上安装应用程序时,该值也可能会更改。

If you need an identifier for advertising purposes, use the advertisingIdentifierproperty of ASIdentifierManager. However make note that point one discussed above is still true for this as well.

如果您需要用于广告目的的标识符,请使用ASIdentifierManager的AdvertisingIdentifier属性。但是请注意,上面讨论的第一点仍然适用于此。

Source: https://developer.apple.com/library/ios/documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html#//apple_ref/occ/instp/UIDevice/identifierForVendor

来源:https: //developer.apple.com/library/ios/documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html#//apple_ref/occ/instp/UIDevice/identifierForVendor

回答by marciokoko

This is a hot topic indeed. I have an app that I have to migrate because it used the UDID to name an XML file to be stored on a server. Then the device with the app would connect to the server and download its specific udid.xml and parse it to work.

这确实是一个热门话题。我有一个必须迁移的应用程序,因为它使用 UDID 命名要存储在服务器上的 XML 文件。然后带有应用程序的设备将连接到服务器并下载其特定的 udid.xml 并解析它以使其工作。

Ive been thinking that indeed if the user moves to a new device, the app will break. So I really should use something else. The thing is, I don't use a database for the data. The data is simply stored in an xml format, one xml file per device stored on the cloud.

我一直在想,如果用户转移到新设备上,应用程序确实会崩溃。所以我真的应该用别的东西。问题是,我不使用数据库来存储数据。数据仅以 xml 格式存储,每个设备一个 xml 文件存储在云中。

Im thinking the best thing would be to have the user fill out the data on the web, have php create a token on the fly which will not be stored in a database but rather sent to the user. The user can then input the token on the target device and retrieve the xml in question.

我认为最好的办法是让用户在网上填写数据,让 php 动态创建一个令牌,该令牌不会存储在数据库中,而是发送给用户。然后,用户可以在目标设备上输入令牌并检索有问题的 xml。

That would be my solution to the problem. Not sure how to implement the whole 'creating unique tokens' thing though.

那将是我解决问题的方法。不知道如何实现整个“创建独特的令牌”的事情。