0

我有一个问题接近尾声。

我的工作是基于这样的信念/经验,即多次播种 iCloud 是一个坏主意,如果用户可以做错事,他可能迟早会这样做。

我想做的事:

A. 当用户将应用偏好“启用 iCloud”从“否”更改为“是”时,显示 AlertView 询问(是或否)用户是否希望使用现有的非 iCloud 数据播种云。

B. 确保应用程序仅在 iCloud 帐户上为 iCloud 播种一次,避免在第一次播种完成后放置 AlertView。

我的方法:

  1. 按照 Apple 的有关正确使用 NSUbiquitousKeyValueStore 的文档,我在 - (void)application: dFLWOptions 中使用以下方法:

    - (void)updateKVStoreItems:(NSNotification*)notification {
        // Get the list of keys that changed.
        NSDictionary* userInfo = [notification userInfo];
        NSNumber* reasonForChange = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
        NSInteger reason = -1;
            // If a reason could not be determined, do not update anything.
        if (!reasonForChange)
            return;
            // Update only for changes from the server.
        reason = [reasonForChange integerValue];
        if ((reason == NSUbiquitousKeyValueStoreServerChange) ||
             (reason == NSUbiquitousKeyValueStoreInitialSyncChange)) { // 0 || 1
                // If something is changing externally, get the changes
                // and update the corresponding keys locally.
            NSArray* changedKeys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
            NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
            NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
                // This loop assumes you are using the same key names in both
                // the user defaults database and the iCloud key-value store
            for (NSString* key in changedKeys) {//Only one key: @"iCloudSeeded" a BOOL
                BOOL bValue = [store boolForKey:key];
                id value = [store objectForKey:@"iCloudSeeded"];
                [userDefaults setObject:value forKey:key];
            }
        }
    

    }

  2. 在应用程序顶部附近包含以下代码:dFLWO:

    NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                    selector:@selector(updateKVStoreItems:)
                                            name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                                                 object:store]; // add appDelegate as observer
    
  3. 加载 iCloud Store 后,仅在从未完成播种的情况下使用非 iCloud 数据播种

    - (BOOL)loadiCloudStore {
        if (_iCloudStore) {return YES;} // Don’t load iCloud store if it’s already loaded
    
        NSDictionary *options =
        @{
        NSMigratePersistentStoresAutomaticallyOption:@YES
        ,NSInferMappingModelAutomaticallyOption:@YES
        ,NSPersistentStoreUbiquitousContentNameKey:@"MainStore"
        };
        NSError *error=nil;
        _iCloudStore = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                configuration:nil URL:[self iCloudStoreURL] options:options error:&error];
        if (_iCloudStore) {
            NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
            BOOL iCloudSeeded =
                [store boolForKey:@"iCloudSeeded"];//If the key was not found, this method returns NO.
            if(!iCloudSeeded) // CONTROL IS HERE
                [self confirmMergeWithiCloud]; // Accept one USER confirmation for seeding in AlertView ONCE world wide
            return YES; // iCloud store loaded.
        }
        NSLog(@"** FAILED to configure the iCloud Store : %@ **", error);
        return NO;
    }
    
  4. 播种完成后,请执行以下操作以防止任何重复播种:

    if (alertView == self.seedAlertView) {
            if (buttonIndex == alertView.firstOtherButtonIndex) {
                [self seediCloud];
                NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
                [store setBool:YES  forKey:@"iCloudSeeded"]; // NEVER AGAIN 
                    //[store synchronize];
            }
        }
    }
    
  5. 在上述过程之前,请务必使用以下方法完全重置 iCloud:

    [NSPersistentStoreCoordinator
          removeUbiquitousContentAndPersistentStoreAtURL:[_iCloudStore URL]
          options:options
          error:&error])
    

    恕我直言,这是我的问题的一个非常整洁的解决方案,但我无法完成它。

    我的问题:

    我如何响应上面 updateKVStoreItems: 的第一个通知?这是一个包含错误信息的通知。我说该值为 TRUE,但我从未将其设置为 TRUE。如何为 NSUbiquitousKeyValueStore 中的键设置默认值?

    我发现第一个通知是有道理的:NSUbiquitousKeyValueStoreInitialSyncChange 当那个注释进来时,bValue 是 YES。这是我的问题。就好像 iCloud/iOS 假定任何新的 BOOL 为 TRUE。我最初需要将此值设置为 NO,以便我可以继续遵循 Apple Docs 并将 NSUserDefault 设置为 NO。然后稍后在播种完成后,最终设置值: YES 为键:@“iCloudSeed”

    我发现我无法从 Apple 中理解以下内容的含义:

    NSUbiquitousKeyValueStoreInitialSyncChange
    Your attempt to write to key-value storage was discarded because an initial download from iCloud has not yet happened. 
    That is, before you can first write key-value data, the system must ensure that your app’s local, on-disk cache matches the truth in iCloud.
    Initial downloads happen the first time a device is connected to an iCloud account, and when a user switches their primary iCloud account.
    

    我不太明白下面数字 2 的含义,这是我在网上找到的:

     NSUbiquitousKeyValueStoreInitialSyncChange – slightly more complicated, only happens under these circumstances:
    1. You start the app and call synchronize
    2. Before iOS has chance to pull down the latest values from iCloud you make some changes.
    3. iOS gets the changes from iCloud.
    

    如果这个问题是 NSUserDefaults 而不是 NSUbiquitousKeyValueStore,我相信我需要去 registerDefaults。

    我快到了,请问我该怎么做!感谢阅读,马克

4

1 回答 1

0

代码正在寻找两者

    A. NSUbiquitousKeyValueStoreInitialSyncChange and 
    B. NSUbiquitousKeyValueStoreServerChange

我无法弄清楚如何处理通知。我知道我不需要做任何事情。我的应用程序只需要读写,以解决我在问题标题中提出的问题。

该应用程序通过以下方式获取当前值:

    NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
    BOOL iCloudSeeded = [store boolForKey:@"iCloudSeeded"];

该应用程序在 NSUbiquitousKeyValueStore 中设置值:

    NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
    [store setBool:YES  forKey:@"iCloudSeeded"];

我相信我说的是正确的:写作是在记忆中完成的。此后不久,系统将数据放入磁盘。从那里获取并放入 iCloud,并可供在同一 iCloud 帐户上运行同一应用程序的其他设备使用。在我描述的应用程序中,不需要添加观察者,也不需要做任何其他事情。这可能是 NSUbiquitousKeyValueStore 的“不寻常”使用。

如果您来这里是为了寻找更“常见”的用途,比如当用户在 textview 中键入内容,然后它会出现在运行同一应用程序的其他设备的视图中,请查看我在以下位置遇到的一个简单演示:

    https://github.com/cgreening/CMGCloudSyncTest

更好的功能(仅监控)通知处理程序如下:

    - (void)updateKVStoreItems:(NSNotification*)notification {
        NSNumber *reason = notification.userInfo[NSUbiquitousKeyValueStoreChangeReasonKey];
        if(!reason) return;
            // get the reason code
        NSInteger reasonCode = [notification.userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] intValue];
        BOOL bValue;
        NSUbiquitousKeyValueStore *store;

        switch(reasonCode) {
            case NSUbiquitousKeyValueStoreServerChange:{ // code 0, monitoring only
                store = [NSUbiquitousKeyValueStore defaultStore];
                bValue = [store boolForKey:@"iCloudSeeded"];
                id value = [store objectForKey:@"iCloudSeeded"];
                DLog(@"New value for iCloudSeeded=%d\nNo Action need be take.",bValue);
                    // For monitoring set in UserDefaults
                [[NSUserDefaults standardUserDefaults] setObject:value forKey:@"iCloudSeeded"];
                break;
            }
            case NSUbiquitousKeyValueStoreAccountChange: {// ignore, log
                NSLog(@"NSUbiquitousKeyValueStoreAccountChange");
                break;
            }
            case NSUbiquitousKeyValueStoreInitialSyncChange:{ // ignore, log
                NSLog(@"NSUbiquitousKeyValueStoreInitialSyncChange");
                break;
            }
            case NSUbiquitousKeyValueStoreQuotaViolationChange:{ // ignore, log
                NSLog(@"Run out of space!");
                break;
            }
        }
    }

添加 9/3/14 很抱歉,但我在使用 BOOL 时仍然遇到问题,我切换到 NSString,现在一切都很好。

确保在应用程序生命周期内最多使用一次用于播种 ICOUD 的“合并”按钮的方法

  1. 在 KV_STORE 中使用 NSString 而不是 BOOL。无需添加观察者,学习除外

  2. 在常量.h 中:

    #define SEEDED_ICLOUD_MSG @"Have Seeded iCloud"
    #define ICLOUD_SEEDED_KEY @"iCloudSeeded"
    
  3. 在调用函数以使用非 iCloud 数据播种 iCloud 之前:

    NSUbiquitousKeyValueStore* kvStore = [NSUbiquitousKeyValueStore defaultStore];
    NSString* strMergeDataWithiCloudDone =
                [kvStore stringForKey:ICLOUD_SEEDED_KEY];
    NSComparisonResult *result = [strMergeDataWithiCloudDone compare:SEEDED_ICLOUD_MSG];
    if(result != NSOrderedSame)
        //put up UIAlert asking user if seeding is desired.
    
  4. 如果用户选择 YES :在合并完成后设置 Key 的值。

    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        if (alertView == self.seedAlertView) {
            if (buttonIndex == alertView.firstOtherButtonIndex) {
                [self seediCloudwithNoniCloudData];
                NSUbiquitousKeyValueStore* kvStoretore = [NSUbiquitousKeyValueStore defaultStore];
                [store setObject:SEEDED_ICLOUD_MSG forKey:ICLOUD_SEEDED_KEY];
            }
        }
    }
    
  5. 此后在所有设备上,一直以来,代码

    NSUbiquitousKeyValueStore* kvStoretore = [NSUbiquitousKeyValueStore defaultStore]; NSString* msg = [kvStore stringForKey:ICLOUD_SEEDED_KEY];

产生:味精 == SEEDED_ICLOUD_MESSAGE

于 2014-09-01T18:44:13.503 回答