iOS独特的用户标识符
我正在写一个iphone应用程序,它使用REST与我的服务器通信。 主要的问题是,我需要以某种方式识别用户。 不久之前,我们被允许使用UDID,但现在不允许它了。 那我应该用什么呢? 我需要在iPhone上的某种标识符,所以用户将删除应用程序,再次安装,他会得到相同的ID。
首先,UDID 只在iOS 5中被弃用 。这并不意味着它已经消失了。
其次,你应该问自己,你是否真的需要这样的事情。 如果用户得到一个新的设备,并安装你的应用程序呢? 同一个用户,但UDID已经改变。 与此同时,原来的用户可能已经卖掉了他的旧设备,所以现在一个全新的用户安装了你的应用程序,你认为这是一个基于UDID的人。
如果您不需要UDID,请使用CFUUIDCreate()
创build一个唯一的ID,并在第一次启动时将其安全设置为用户默认值(首先使用CFUUIDCreateString()
将UUID转换为string)。 它将保留备份和恢复,甚至在切换到新设备时与原始用户一起出现。 UDID在很多方面都是更好的select。
如果你真的需要一个独特的设备标识符(听起来不像你这样做),请按照Suhail的回答指出MAC地址。
我使用CFUUIDCreate()
来创build一个UUID:
+ (NSString *)GetUUID { CFUUIDRef theUUID = CFUUIDCreate(NULL); CFStringRef string = CFUUIDCreateString(NULL, theUUID); CFRelease(theUUID); return [(NSString *)string autorelease]; }
然后将上面的UUID设置为我的NSString:
NSString *UUID = [nameofclasswhereGetUUIDclassmethodresides UUID];
然后我使用SSKeyChain将该UUID存储到钥匙串
用SSKeyChain设置UUID:
[SSKeychain setPassword:UUID forService:@"com.yourapp.yourcompany" account:@"user"];
检索它:
NSString *retrieveuuid = [SSKeychain passwordForService:@"com.yourapp.yourcompany" account:@"user"];
当您将UUID设置为钥匙串时,即使用户完全卸载该应用程序然后再次安装该应用程序,该UUID也会保留。
确保所有设备在钥匙串中具有相同的UUID。
- 设置您的应用程序使用iCloud。
- 将钥匙串中的UUID保存到NSUserDefaults中。
- 通过键值数据存储将NSUserDefaults中的UUID传递给云。
- 在App第一次运行时,检查Cloud Data是否可用,并在新设备的Keychain中设置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及以下版本
最后这就是我所做的
a)将SFHFKeychainUtils添加到项目中
b)生成的CFUUID键string
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault); udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
c)将它保存到Key Chain Utils中,否则它会生成一个新的Unique Time
最后的代码
+ (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同步,请继续阅读。 这个post最终比我原来想要的更啰嗦,但是我希望这对任何人来说都是明确的!
使用NSUUID
我使用NSUUID类来创buildUUID:
NSUUID *uuid = [NSUUID UUID];
然后创buildstring,你只需要调用UUIDString
方法:
NSString *uuidString = [uuid UUIDString];
或者在一行中做:
NSString *uuidString = [[NSUUID UUID] UUIDString];
恕我直言,这比试图使用CFUUIDCreate更容易,并有一个你必须维护的方法。
编辑:我现在使用UICKeyChainStore
用UICKeyChainStore设置UUID:
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"];
然后我使用SSKeyChain将该UUID存储到钥匙串
用SSKeyChain设置UUID:
[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设置为钥匙串时,即使用户完全卸载该应用程序然后再次安装该应用程序,该UUID也会保留。
与iCloud同步
所以确保所有用户的设备使用相同的UUID是很有用的。 这是为了确保数据在所有设备间同步,而不是每个设备都认为它是唯一的用户。
评论中有几个问题,我的答案是关于同步如何工作,所以现在我已经完成了所有工作,我将提供更多的细节。
configurationiCloud / NSUbiquitousKeyValueStore使用
- 点击Xcode中Project Navigator顶部的项目 。
- selectfunction 。
- 打开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]];
使用iCloud创buildUUID
结合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]; }
如何确保您的用户ID同步
因为写入iCloud需要首先下载iCloud中的任何数据,所以我把同步调用放在(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
的顶部(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进行同步,并validationUUID的完整性。
- (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中的值是要保留的值,因为它是第一个推送到iCloud的UUID,而不pipe哪个设备首先生成UUID。 你们大多数人可能会走相同的路,因为你不会真正关心它解决哪个UUID,只要它解决了一个单一的。 对于那些真正关心哪个先出现的人,我build议你同时存储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上有一个很好的select,它基于MAC地址和Bundle标识符的组合,生成一个唯一标识符,这个标识符运行得非常好: UIDevice-with-UniqueIdentifier-for-iOS-5
在iOS7中,Apple在UIDevice类中引入了一个名为“identifierForVendor”的只读属性。 如果你决定使用它,你应该记下以下内容,
- 如果在用户解锁设备之前访问该值,则该值可以为零
- 当用户从设备上删除所有供应商的应用程序并随后重新安装其中一个或多个应用程序时,该值会更改。
- 在使用Xcode安装testing版本时,或者在使用ad-hoc分发的设备上安装应用程序时,该值也可能会发生变化。
如果您需要用于广告目的的标识符,请使用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并parsing它的工作。
我一直在想,如果用户移动到一个新的设备,应用程序将中断。 所以我真的应该使用别的东西。 事情是,我不使用数据库的数据。 数据只是以xml格式存储,每个设备上存储一个xml文件。
我认为最好的事情是让用户填写networking上的数据,让PHP创build一个令牌,不会被存储在数据库中,而是发送给用户。 然后用户可以在目标设备上input令牌并检索相关的XML。
这将是我解决这个问题的方法。 不知道如何实现整个“创造独特的令牌”的东西。