有些人想了解更多关于可用的不同选项的信息,如果您想了解,请查看来自@NSQuamber.java 的答案。如果您想知道如何使用 NSUUID 并与 iCloud 同步,请继续阅读。这篇文章最终比我最初想要的更冗长,但我希望它能让任何采取这些步骤的人都清楚!
使用 NSUUID
我使用 NSUUID 类来创建 UUID:
NSUUID *uuid = [NSUUID UUID];
那么创建字符串,只需要调用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 设置为 Keychain 时,即使用户完全卸载应用程序然后再次安装它也会保留。
与 iCloud 同步
因此,确保所有用户的设备使用相同的 UUID 很有用。这是为了确保数据在所有设备之间同步,而不是每个设备都认为自己是唯一的用户。
cmets 中有几个问题是关于同步如何工作的答案,所以现在我已经完成了所有工作,我将提供更多详细信息。
配置 iCloud/NSUbiquitousKeyValueStore 使用
- 在 Xcode 的 Project Navigator 顶部单击您的项目。
- 选择能力。
- 打开 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 documentation 声明之前,您需要先从 iCloud 读取。要强制读取,请调用以下方法:
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
要让您的应用接收 iCloud 更改通知,请添加以下通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iCloudStoreDidChange:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:[NSUbiquitousKeyValueStore defaultStore]];
使用 iCloud 创建 UUID
结合 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 方法的顶部。我还在那里添加了通知注册。这使我能够检测到来自 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 中的值是要保留的值,因为它将是第一个推送到 iCloud 的 UUID,无论哪个设备首先生成了 UUID。你们中的大多数人可能会走同样的路,因为你不会真正关心它解析为哪个 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;