Я пишу приложение для iphone, которое связывается с моим сервером с помощью REST. Основная проблема заключается в том, что мне нужно как-то идентифицировать пользователя. Не так давно нам разрешили использовать UDID, но теперь его больше не разрешают. Итак, что я должен использовать вместо этого? Мне нужен какой-то идентификатор на iphone, поэтому пользователь удалит приложение, установит его снова, и он получит тот же идентификатор.
Уникальный идентификатор пользователя iOS
Ответ 1
Во-первых, UDID только устарел в iOS 5. Это еще не значит, что он ушел (пока).
Во-вторых, вы должны спросить себя, действительно ли вам нужна такая вещь. Что делать, если пользователь получает новое устройство и устанавливает ваше приложение? Тот же пользователь, но UDID изменился. Между тем, первоначальный пользователь, возможно, продал свое старое устройство, поэтому теперь совершенно новый пользователь устанавливает ваше приложение, и вы думаете, что это другой человек, основанный на UDID.
Если вам не нужен UDID, используйте CFUUIDCreate()
для создания уникального идентификатора и безопасного его по умолчанию для пользователя при первом запуске (используйте CFUUIDCreateString()
, чтобы сначала преобразовать UUID в строку). Он выдержит резервные копии и восстановит их и даже придет вместе с оригинальным пользователем при переключении на новое устройство. Это во многом лучший вариант, который UDID.
Если вам действительно нужен уникальный идентификатор устройства (он не похож на вас), перейдите к MAC-адресу, указанному в ответе Suhail.
Ответ 2
Я использовал 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, который находится в Keychain, и NSUserDefaults.
- Передайте UUID в NSUserDefaults в облако с хранилищем данных с ключом.
- В первом запуске приложения проверьте, доступны ли облачные данные и установите UUID в цепочке ключей на новом устройстве.
Теперь у вас есть уникальный идентификатор, который является постоянным и общим/синхронизированным со всеми устройствами.
Ответ 3
Я обновлял приложение, которое работало только с уникальным идентификатором, который поддерживал iOS 4.3 и выше. Итак,
1) Мне не удалось использовать [UIDevice currentDevice].uniqueIdentifier;
, поскольку он больше не был доступен
2) Я не мог использовать [UIDevice currentDevice].identifierForVendor.UUIDString
, потому что он был доступен только в iOS 6.0 и более поздних версиях и не смог использовать его для более низких версий iOS.
3) Макинтош-адрес не был вариантом, поскольку он не был разрешен в iOS-7
4) OpenUDID было deprecated некоторое время назад, а также возникли проблемы с iOS- 6.
5) Идентификаторы объявлений также недоступны для iOS-5 и ниже
Наконец, это то, что я сделал
a) Добавлен SFHFKeychainUtils в проект
b) Сгенерированная клавиша CFUUID Строка
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;
}
Ответ 4
Некоторые люди хотят узнать больше о различных доступных вариантах, и если да, взгляните на ответ из @NSQuamber.java. Если вы хотите знать, как использовать NSUUID и синхронизировать с iCloud, продолжайте читать. Этот пост оказался более длинным, чем я изначально хотел, но я надеюсь, что он даст понять, кто сделает эти шаги!
Использование NSUUID
Я использую класс NSUUID для создания UUID:
NSUUID *uuid = [NSUUID UUID];
Затем, чтобы создать строку, вам нужно вызвать метод UUIDString
:
NSString *uuidString = [uuid UUIDString];
или сделать это в одной строке:
NSString *uuidString = [[NSUUID UUID] UUIDString];
IMHO, это намного проще, чем пытаться использовать CFUUIDCreate и иметь метод, который вы должны поддерживать.
EDIT: теперь я использую 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;
Ответ 5
В Github есть хорошая альтернатива, которая генерирует уникальный идентификатор на основе сочетания MAC-адреса и идентификатора Bundle, который работает очень хорошо: UIDevice-with-UniqueIdentifier-for-iOS-5
Ответ 6
В iOS7 Apple представила свойство read only, называемое идентификатором forVendor, в классе UIDevice. Если вы решите использовать его, вы должны обратить внимание на следующее:
- Это значение может быть нулевым, если к нему обращаются до того, как пользователь разблокирует устройство.
- Значение изменяется, когда пользователь удаляет все приложения этих поставщиков с устройства и затем переустанавливает один или несколько из них.
- Значение может также изменяться при установке тестовых сборок с использованием Xcode или при установке приложения на устройстве с использованием ad-hoc-распространения.
Если вам нужен идентификатор для рекламных целей, используйте advertisingIdentifier свойство ASIdentifierManager. Однако обратите внимание, что обсуждаемая выше точка остается верной и для этого.
Ответ 7
Это действительно горячая тема. У меня есть приложение, которое я должен выполнить, потому что он использовал UDID для обозначения файла XML, который будет храниться на сервере. Затем устройство с приложением подключится к серверу и загрузит его конкретный udid.xml и проанализирует его работу.
Я думал, что действительно, если пользователь перейдет на новое устройство, приложение сломается. Поэтому я действительно должен использовать что-то еще. Дело в том, что я не использую базу данных для данных. Данные просто хранятся в формате xml, один xml файл на устройство, хранящееся в облаке.
Я думаю, что лучше всего было бы, чтобы пользователь заполнил данные в Интернете, php создаст токен "на лету", который не будет храниться в базе данных, а скорее отправлен пользователю. Затем пользователь может ввести токен на целевое устройство и извлечь соответствующий XML-запрос.
Это было бы моим решением проблемы. Не уверен, как реализовать все "создание уникальных токенов".