iOS уникальный идентификатор пользователя

Я пишу приложение для iphone, которое общается с моим сервером, используя REST. Основная проблема в том, что мне нужно как-то идентифицировать пользователя. Не так давно нам было разрешено использовать UDID, но теперь это запрещено. Так что я должен использовать вместо этого? Мне нужен какой-то идентификатор на iphone, поэтому пользователь удалит приложение, установит его снова и получит тот же идентификатор.

7 ответов

Решение

Во-первых, UDID устарел только в iOS 5. Это не значит, что он исчез (пока).

Во-вторых, вы должны спросить себя, действительно ли вам это нужно. Что если пользователь получит новое устройство и установит на него ваше приложение? Тот же пользователь, но UDID изменился. Между тем, оригинальный пользователь, возможно, продал свое старое устройство, так что теперь совершенно новый пользователь устанавливает ваше приложение, и вы думаете, что это другой человек, основанный на UDID.

Если вам не нужен UDID, используйте CFUUIDCreate() создать уникальный идентификатор и сохранить его для пользователя по умолчанию при первом запуске (используйте CFUUIDCreateString() сначала преобразовать UUID в строку). Он будет выживать при резервном копировании и восстановлении, и даже придет вместе с первоначальным пользователем, когда они переключатся на новое устройство. Во многих отношениях это лучший вариант, чем UDID.

Если вам действительно нужен уникальный идентификатор устройства (он не похож на ваш), выберите MAC-адрес, как указано в ответе Сухаила.

Я использовал CFUUIDCreate() создать UUID:

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

Затем установите указанный выше UUID для моей строки NSString:

NSString *UUID = [nameofclasswhereGetUUIDclassmethodresides UUID];

Затем я сохранил этот UUID в связке ключей, используя SSKeyChain

Чтобы установить UUID с SSKeyChain:

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

Чтобы получить это:

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

Когда вы устанавливаете UUID в связку ключей, он будет сохраняться, даже если пользователь полностью удалит приложение, а затем снова установит его.

Чтобы убедиться, что все устройства имеют одинаковый UUID в связке ключей.

  1. Настройте ваше приложение для использования iCloud.
  2. Сохраните UUID, который находится в Цепочке для ключей, к NSUserDefaults также.
  3. Передайте UUID в NSUserDefaults в облако с хранилищем данных значения ключа.
  4. При первом запуске приложения проверьте, доступны ли облачные данные, и установите UUID в связке ключей на новом устройстве.

Теперь у вас есть уникальный идентификатор, который является постоянным и совместно используется / синхронизируется со всеми устройствами.

Я обновлял свое приложение, которое работало только на основе уникального идентификатора, который поддерживал iOS 4.3 и выше. Так,

1) я не смог использовать [UIDevice currentDevice].uniqueIdentifier; как это было больше не доступно

2) Я не мог использовать [UIDevice currentDevice].identifierForVendor.UUIDString потому что он был доступен только в iOS 6.0 и более поздних версиях и не мог использоваться для более низких версий iOS.

3) Mac-адрес не был опцией, поскольку он не был разрешен в iOS-7

4) OpenUDID устарел некоторое время назад, а также имел проблемы с iOS-6.

5) Рекламные идентификаторы также не были доступны для iOS-5 и ниже

Наконец это было то, что я сделал

а) Добавлен SFHFKeychainUtils в проект

б) Сгенерированный CFUUID ключ String

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

c) Сохранено в Key Chain Utils, иначе будет каждый раз генерироваться новый Уникальный

Окончательный код

+ (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;
}

Для дальнейших деталей

Некоторые люди хотят узнать больше о различных доступных опциях, и если вы это сделаете, взгляните на ответ из @NSQuamber.java. Если вы хотите знать, как использовать NSUUID и синхронизироваться с iCloud, продолжайте читать. Этот пост оказался более многословным, чем я изначально хотел, но я надеюсь, что он станет понятным для любого, кто предпримет эти шаги!

Использование NSUUID

Я использую класс NSUUID для создания UUID:

NSUUID *uuid = [NSUUID UUID];

Затем, чтобы создать строку, вам нужно всего лишь вызвать UUIDString метод:

NSString *uuidString = [uuid UUIDString];

или сделать это в одну строку:

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

ИМХО, это намного проще, чем пытаться использовать CFUUIDCreate и иметь метод, который вы должны поддерживать.


РЕДАКТИРОВАТЬ: я сейчас использую UICKeyChainStore

Чтобы установить UUID с UICKeyChainStore:

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

Чтобы получить его:

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

Затем я сохранил этот UUID в связке ключей, используя SSKeyChain

Чтобы установить UUID с SSKeyChain:

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

Чтобы получить его:

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

Когда вы устанавливаете UUID в связку ключей, он будет сохраняться, даже если пользователь полностью удалит приложение, а затем снова установит его.

Синхронизация с iCloud

Поэтому полезно убедиться, что все устройства пользователя используют один и тот же UUID. Это делается для того, чтобы данные синхронизировались на всех устройствах, а не на каждом устройстве, считая его уникальным пользователем.

В моих комментариях было несколько вопросов о том, как будет работать синхронизация, поэтому теперь, когда у меня все работает, я предоставлю больше деталей.

Настройка использования iCloud/NSUbiquitousKeyValueStore

  1. Нажмите на свой проект наверху Навигатора проекта в XCode.
  2. Выберите Возможности.
  3. Включите iCloud.

Теперь это должно выглядеть примерно так:Скриншот iCloud включен

Использование NSUbiquitousKeyValueStore

Использовать 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];

Читать:

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

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

Прежде чем вы сможете написать документацию NSUbiquitousKeyValueStore, говорится, что вы должны сначала прочитать из iCloud. Чтобы вызвать чтение, вызовите следующий метод:

[[NSUbiquitousKeyValueStore defaultStore] synchronize];

Чтобы ваше приложение получало уведомления об изменениях в iCloud, добавьте следующее уведомление:

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

Создание UUID с iCloud

Комбинируя NSUUID, SSKeychain и NSUbiquityKeyValueStore, вот мой метод генерации идентификатора пользователя:

- (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];
}

Как обеспечить синхронизацию вашего идентификатора пользователя

Поскольку запись в iCloud требует сначала загрузки любых данных в iCloud, я поместил вызов синхронизации в верхней части (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions метод. Я также добавил там регистрацию уведомлений. Это позволяет мне обнаруживать любые изменения в iCloud и обрабатывать их соответствующим образом.

Вот пример:

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;
        }
    }
}

Когда приложение запускается или возвращается на передний план, я запускаю синхронизацию с 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;
}

Если какой UUID пришел первым, имеет значение

В моем случае использования моего UserID я предполагал, что значение в iCloud - это то, что нужно сохранить, поскольку это будет первый UUID, отправленный в iCloud, независимо от того, какое устройство сгенерировало 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;

На Github есть хорошая альтернатива, которая генерирует уникальный идентификатор на основе комбинации Mac-адреса и идентификатора пакета, который работает довольно хорошо: https://github.com/gekitz/UIDevice-with-UniqueIdentifier-for-iOS-5

В iOS7 Apple представила свойство "только для чтения" под названием "identifierForVendor" в классе UIDevice. Если вы решите использовать его, вы должны отметить следующее,

  • Это значение может быть равно нулю, если оно доступно до того, как пользователь разблокирует устройство.
  • Значение изменяется, когда пользователь удаляет все приложения этого поставщика с устройства, а затем переустанавливает одно или несколько из них.
  • Это значение также может изменяться при установке тестовых сборок с использованием XCode или при установке приложения на устройство с использованием специального распространения.

Если вам нужен идентификатор для рекламных целей, используйте свойство advertisingIdentifier ASIdentifierManager. Однако обратите внимание, что пункт 1, обсужденный выше, все еще верен и для этого.

Источник: https://developer.apple.com/library/ios/documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html

Это действительно горячая тема. У меня есть приложение, которое я должен перенести, потому что оно использует UDID для именования XML-файла, который будет храниться на сервере. Затем устройство с приложением подключится к серверу, загрузит его определенный файл udid.xml и проанализирует его для работы.

Я думал, что действительно, если пользователь перейдет на новое устройство, приложение сломается. Так что я действительно должен использовать что-то еще. Дело в том, что я не использую базу данных для данных. Данные просто хранятся в формате XML, один файл XML на устройство хранится в облаке.

Я думаю, что лучше всего было бы, чтобы пользователь заполнил данные в Интернете, чтобы php создал токен на лету, который не будет храниться в базе данных, а скорее будет отправлен пользователю. Затем пользователь может ввести токен на целевом устройстве и получить соответствующий XML-файл.

Это было бы моим решением проблемы. Не уверен, как реализовать всю идею создания уникальных токенов.

Другие вопросы по тегам