iOS,CloudKit - 我的应用程序启动时是否需要进行提取?

时间:2017-03-04 18:23:07

标签: ios cocoa-touch cloudkit

我正在设置iCloud更改通知的注册。

假设有一个新设备被添加到icloud帐户,我只是想知道该设备将如何获取私人数据库记录。

我是否需要进行一次性查询?

我希望所有其他时间都会使用通知。

1 个答案:

答案 0 :(得分:3)

让我们从订阅通知的一些相关特征开始:

首先:订阅通知特定于用户+设备对。如果我在手机上安装了应用程序,我会开始收到通知。在我在那里安装应用程序之前,我不会在另一台设备上收到通知。

第二:通知不可靠。 Apple文档很清楚,他们不保证交付。当您收到通知时,可能会有几个先前的通知。因此,Apple提供了两种机制来跟踪您已经看到的通知:

  1. 已读/未读状态:您可以将通知标记为已读。 Apple的文档与实际上的内容相矛盾。 This page
  2.   

    如果使用CKMarkNotificationsReadOperation对象将一个或多个通知标记为已读,则即使您为previousServerChangeToken指定了nil,也不会返回这些通知。

    但是,这不是真的。获取操作清楚地返回读取和未读通知。 WWDC 2014视频231(高级Cloudkit)与文档页面相矛盾,解释了总是返回未读令牌以及读取令牌,因此多个设备可以同步。该视频给出了一个显示此行为的好处的具体示例。 SO:CKFetchNotificationChangesOperation returning old notifications

    上也记录了此行为
    1. 更改令牌:每次获取操作都会返回一个可以缓存的更改令牌。如果您将令牌传递给fetch,则fetch将仅从该点返回令牌,无论是已读还是未读。
    2. 乍一看,似乎Apple正在提供您想要的行为:在一台设备上安装应用程序,开始处理通知,在第二台设备上安装应用程序,并获取所有这些先前的通知以便赶上

      不幸的是,正如我在CKFetchNotificationChangesOperation: why are READ notifications all nil?中记录的那样,无论何时我都会收到通知,之前标记为"""都没有内容。读取通知中的所有信息都将丢失。

      在我的场景中,我选择了:

      1. 始终在启动时获取最新记录
      2. 使用以前保存的更改令牌(如果存在)获取通知
      3. 处理新通知
      4. 将通知标记为已读
      5. 保存最新的更改令牌以供下次提取
      6. 使用

        对于您的方案,您可以尝试:

        1. 使用以前保存的更改令牌(如果存在)获取通知
        2. 处理通知(请勿将其标记为已阅读)
        3. 保存最新的更改令牌以供下次提取
        4. 使用

          您的第一个设备会在每次后续提取时忽略旧通知,因为您正在从更改令牌点开始每次提取。您的第二个设备将在第一次执行时以零更改令牌开始,从而获取所有旧通知。

          提醒一句:尽管前面提到的WWDC视频明确表示Apple保留了所有旧通知,但我没有找到任何文档说明他们持有这些信息的时间。它可能是永远的,也可能不是。

          使用通知获取示例

          进行了更新

          以下是我如何获取通知,标记它们以及缓存更改标记:

          @property CKServerChangeToken *notificationServerChangeToken;
          

          则...

          -(void)checkForUnreadNotifications
          {
              //check for unread cloudkit messages
              CKFetchNotificationChangesOperation *op = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:_notificationServerChangeToken];
          
              op.notificationChangedBlock = ^(CKNotification *notification)
              {
                  //this fires for each received notification. Take action as needed.
              };
          
              //maintain a pointer to the op. We will need to look at a property on the
              //op from within the completion block. Use __weak to prevent retain problems
              __weak CKFetchNotificationChangesOperation *operationLocal = op;
          
              op.fetchNotificationChangesCompletionBlock = ^(CKServerChangeToken *newServerChangeToken, NSError *opError)
              {
                  //this fires once, at the end, after all notifications have been returned.
                  //this is where I mark the notifications as read, for example. I've
                  //omitted that step because it probably doesn't fit your scenario.
          
                  //update the change token so we know where we left off
                  [self setNotificationServerChangeToken:newServerChangeToken]; 
          
                  if (operationLocal.moreComing)
                  {
                      //more notifications are waiting, recursively keep reading
                      [self checkForUnreadNotifications];
                      return;
                  }
              };
          
              [[CKContainer defaultContainer] addOperation:op];
          }
          

          要从用户默认值设置和检索缓存的更改标记,我使用以下两个函数:

          -(void)setNotificationServerChangeToken:(CKServerChangeToken *)newServerChangeToken
          {
          
              //update the change token so we know where we left off
              _notificationServerChangeToken = newServerChangeToken;
              NSData *encodedServerChangeToken = [NSKeyedArchiver archivedDataWithRootObject:newServerChangeToken];
              NSUserDefaults *userSettings = [NSUserDefaults standardUserDefaults];
              [userSettings setObject:encodedServerChangeToken forKey:UD_KEY_NOTIFICATION_TOKEN_CKSERVERCHANGETOKEN_PROD];
          
              //Note, the development and production cloudkit environments have separate change tokens. Depending on your needs, you may need to save both.
          }
          

          和...

          -(void)getNotificationServerChangeToken
          {
              NSUserDefaults *userSettings = [NSUserDefaults standardUserDefaults];
              NSData *encodedServerChangeToken = [userSettings objectForKey:UD_KEY_NOTIFICATION_TOKEN_CKSERVERCHANGETOKEN_PROD];
              _notificationServerChangeToken = [NSKeyedUnarchiver unarchiveObjectWithData:encodedServerChangeToken];    
          }