iOS unique user identifier
我正在为iPhone编写一个应用程序,它使用REST与我的服务器通信。主要的问题是,我需要以某种方式识别用户。不久前,我们被允许使用udid,但现在它不再被允许了。那么我应该用什么来代替呢?我在iPhone上需要某种标识符,所以用户将删除应用程序,重新安装它,并获得相同的ID。
我用
1 2 3 4 5 6 | + (NSString *)GetUUID { CFUUIDRef theUUID = CFUUIDCreate(NULL); CFStringRef string = CFUUIDCreateString(NULL, theUUID); CFRelease(theUUID); return [(NSString *)string autorelease]; } |
然后将上述uuid设置为my nsstring:
1 | NSString *UUID = [nameofclasswhereGetUUIDclassmethodresides UUID]; |
然后我使用sskeychain将该uuid存储到keychain
要使用sskeychain设置uuid,请执行以下操作:
1 | [SSKeychain setPassword:UUID forService:@"com.yourapp.yourcompany" account:@"user"]; |
检索它:
1 | NSString *retrieveuuid = [SSKeychain passwordForService:@"com.yourapp.yourcompany" account:@"user"]; |
当您将uuid设置为keychain时,即使用户完全卸载应用程序,然后再次安装它,它也将保持不变。
以确保所有设备在密钥链中具有相同的UUID。
您现在有了一个唯一的标识符,它是持久的,并且与所有设备共享/同步。
首先,UDID只在iOS 5中被弃用。这并不意味着它已经消失了。
其次,你应该问问自己是否真的需要这样的东西。如果用户得到一个新设备并在上面安装了你的应用程序怎么办?同一个用户,但UDID已更改。同时,原来的用户可能已经卖掉了他的旧设备,所以现在一个全新的用户安装了你的应用程序,你认为这是一个基于udid的不同的人。
如果不需要udid,可以使用
如果你真的需要一个唯一的设备标识符(听起来不像你需要的),按照Suhail的回答中指出的MAC地址。
我更新的应用程序只基于支持iOS 4.3及更高版本的唯一标识符。所以,
1)我无法使用
2)我不能使用
3)Mac地址不是一个选项,因为在iOS-7中不允许使用它。
4)openudid在一段时间前就被弃用了,iOS-6也有问题。
5)iOS-5及以下版本也没有广告标识。
最后这就是我所做的
a)向项目中添加sfhfkeychainuils
b)生成的cfuuid密钥字符串
1 2 | CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault); udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid)); |
c)保存到钥匙链实用程序,否则每次都会生成一个新的唯一的
最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | + (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我使用nsuid类创建uuid:
1 | NSUUID *uuid = [NSUUID UUID]; |
然后,要创建字符串,只需调用
1 | NSString *uuidString = [uuid UUIDString]; |
或者用一行来做:
1 | NSString *uuidString = [[NSUUID UUID] UUIDString]; |
imho,这比尝试使用cfuuidcreate并拥有必须维护的方法要容易得多。
编辑:我现在使用uickeychainstore
要使用uickeychainstore设置uuid,请执行以下操作:
1 2 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"]; keychain[@"com.sample.MyApp.user"] = userID; |
检索它:
1 2 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"]; NSString *userID = keychain[@"com.sample.MyApp.user"]; |
然后我使用sskeychain将该uuid存储到keychain
要使用sskeychain设置uuid,请执行以下操作:
1 | [SSKeychain setPassword:userID forService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"]; |
检索它:
1 | NSString *userID = [SSKeychain passwordForService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"]; |
当您将uuid设置为keychain时,即使用户完全卸载应用程序,然后再次安装它,它也将保持不变。
与iCloud同步因此,确保所有用户的设备都使用相同的UUID是很有用的。这是为了确保数据在所有设备之间同步,而不是每个设备都认为它是唯一的用户。
在我的答案的注释中有几个关于同步如何工作的问题,所以现在我已经完成了所有的工作,我将提供更多的细节。
配置iCloud/nsubiQuitousKeyValueStore使用现在应该是这样的:
使用nsubiquitosKeyValueStore使用iCloud相当简单。写:
1 2 3 4 5 6 7 8 9 | // 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]; |
阅读:
1 2 3 4 5 | // create the key to store the ID NSString *userKey = @"com.sample.MyApp.user"; // read from iCloud NSString *userID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey]; |
在编写nsubiquitousKeyValueStore文档之前,必须先从iCloud中读取文档。要强制读取,请调用以下方法:
1 | [[NSUbiquitousKeyValueStore defaultStore] synchronize]; |
要让应用程序接收iCloud中的更改通知,请添加以下通知:
1 2 3 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iCloudStoreDidChange:) name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:[NSUbiquitousKeyValueStore defaultStore]]; |
使用iCloud创建UUID
结合nsuid、sskeychain和nsubiquitykeyValueStore,下面是生成用户ID的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | - (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]; } |
如何确保用户ID同步
因为写入icloud需要先下载icloud中的任何数据,所以我将synchronize调用放在
这是一个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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的完整性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | - (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; } |
如果哪个乌伊德先来重要的话
在我的用户ID用例中,我假设icloud中的值是要保留的值,因为它将是推送到icloud的第一个uuid,而不管哪个设备首先生成uuid。你们大多数人可能会走同样的路,因为你们不会真正关心它解析为哪个UUID,只要它解析为单个UUID。对于那些真正关心谁先来的人,我建议您同时存储UUID和时间戳生成(
1 2 3 4 5 6 7 | // 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地址和运行良好的包标识符的组合生成唯一标识符:uidevice-with-unique identifier-for-ios-5
在iOS7中,Apple在uidevice类中引入了一个名为"IdentifierForVendor"的只读属性。如果你决定使用它,你应该注意以下几点:
- 如果在用户解锁设备之前访问该值,则该值可能为零。
- 当用户从设备中删除该供应商的所有应用程序,然后重新安装其中一个或多个应用程序时,该值将更改。
- 当使用Xcode安装测试版本或在使用特殊分发的设备上安装应用程序时,该值也会更改。
如果出于广告目的需要标识符,请使用AsIdentifierManager的AdvertisingIdentifier属性。但是,请注意,上面讨论的第一点同样适用于此。
来源:https://developer.apple.com/library/ios/documentation/uikit/reference/uidevice-class/reference/uidevice.html//apple-ref/occ/instp/uidevice/identifierforvendor
这的确是一个热门话题。我有一个应用程序,我必须迁移,因为它使用udid命名一个XML文件来存储在服务器上。然后,带有该应用程序的设备将连接到服务器并下载其特定的udid.xml并解析它以使其工作。
我一直在想,如果用户移动到一个新的设备上,应用程序就会崩溃。所以我真的应该用点别的。问题是,我不使用数据库来存储数据。数据只是以XML格式存储,每个设备在云端存储一个XML文件。
我认为最好的办法是让用户填写Web上的数据,让PHP动态创建一个令牌,它不会存储在数据库中,而是发送给用户。然后,用户可以在目标设备上输入令牌并检索相关的XML。
那就是我解决这个问题的办法。但不知道如何实现整个"创建唯一令牌"的功能。