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 в связке ключей.
- Настройте ваше приложение для использования iCloud.
- Сохраните UUID, который находится в Цепочке для ключей, к NSUserDefaults также.
- Передайте UUID в NSUserDefaults в облако с хранилищем данных значения ключа.
- При первом запуске приложения проверьте, доступны ли облачные данные, и установите 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
- Нажмите на свой проект наверху Навигатора проекта в XCode.
- Выберите Возможности.
- Включите 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, обсужденный выше, все еще верен и для этого.
Это действительно горячая тема. У меня есть приложение, которое я должен перенести, потому что оно использует UDID для именования XML-файла, который будет храниться на сервере. Затем устройство с приложением подключится к серверу, загрузит его определенный файл udid.xml и проанализирует его для работы.
Я думал, что действительно, если пользователь перейдет на новое устройство, приложение сломается. Так что я действительно должен использовать что-то еще. Дело в том, что я не использую базу данных для данных. Данные просто хранятся в формате XML, один файл XML на устройство хранится в облаке.
Я думаю, что лучше всего было бы, чтобы пользователь заполнил данные в Интернете, чтобы php создал токен на лету, который не будет храниться в базе данных, а скорее будет отправлен пользователю. Затем пользователь может ввести токен на целевом устройстве и получить соответствующий XML-файл.
Это было бы моим решением проблемы. Не уверен, как реализовать всю идею создания уникальных токенов.