如何在应用程序的 iCloud 生命周期中仅显示一次请求种子 iCloud 的 AlertView?

Posted

技术标签:

【中文标题】如何在应用程序的 iCloud 生命周期中仅显示一次请求种子 iCloud 的 AlertView?【英文标题】:How to display an AlertView requesting permission to seed iCloud once and only once in app's iCloud lifetime? 【发布时间】:2014-09-01 04:05:52 【问题描述】:

我有一个问题接近尾声。

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

我想做什么:

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

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

我的方法:

    按照 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];
            
        
    

    在应用程序顶部附近添加以下代码:dFLWO:

    NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                    selector:@selector(updateKVStoreItems:)
                                            name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                                                 object:store]; // add appDelegate as observer
    

    加载 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;
    
    

    播种完成后,请执行以下操作以防止任何重复播种:

    if (alertView == self.seedAlertView) 
            if (buttonIndex == alertView.firstOtherButtonIndex) 
                [self seediCloud];
                NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
                [store setBool:YES  forKey:@"iCloudSeeded"]; // NEVER AGAIN 
                    //[store synchronize];
            
        
    
    

    请务必在上述过程之前使用以下方法完全重置 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。

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

【问题讨论】:

【参考方案1】:

代码正在寻找两者

    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;
            
        
    

添加 2014 年 9 月 3 日 很抱歉,但我仍然无法使用 BOOL,我切换到 NSString,现在 一切都很好。

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

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

    在常量.h 中:

    #define SEEDED_ICLOUD_MSG @"Have Seeded iCloud"
    #define ICLOUD_SEEDED_KEY @"iCloudSeeded"
    

    在调用函数以非 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.
    

    如果用户选择 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];
            
        
    
    

    此后在所有设备上,一直以来,代码

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

产生:msg == SEEDED_ICLOUD_MESSAGE

【讨论】:

以上是关于如何在应用程序的 iCloud 生命周期中仅显示一次请求种子 iCloud 的 AlertView?的主要内容,如果未能解决你的问题,请参考以下文章

uni-app 生命周期

如何在我的应用程序中仅打印一次警报视图?

微信小程序:渲染流程、生命周期和触发顺序

如何在 Vue 的生命周期中显示来自 Vuex 存储的数据

微信小程序生命周期(onLoad,onHide,onShow,onReady,onUnload)

微信小程序生命周期