CloudKit 中的 CKNotification 和 CKQueryNotification 有什么区别?

What is the difference between CKNotification and CKQueryNotification in CloudKit?

有时当我在第一台设备上执行更改(例如添加 40 条记录)时,我在第二台设备上得到 40 CKQueryNotifications

虽然我可以将 CKQueryNotifications 标记为已读,但我无法将其标记为已读 CKNotifications。为什么?每次启动后,他们总是回到我的设备。为什么?

如何获取待处理的通知?

var serverChangeToken: CKServerChangeToken? {
    return UserDefaults(suiteName: SharedGroupName)?.object(forKey: "a") as? CKServerChangeToken
}

func fetchPendingNotifications(notifications: [CKNotification] = [], completion: ErrorHandler? = nil) {

    var queryNotifications = notifications

    let operation = CKFetchNotificationChangesOperation(previousServerChangeToken: serverChangeToken)
    let operationQueue = OperationQueue()

    operation.notificationChangedBlock = { notification in

        print("+++++ \(notification)")
        queryNotifications.append(notification)
    }

    operation.fetchNotificationChangesCompletionBlock = { token, error in

        if let token = token {

            UserDefaults(suiteName: SharedGroupName)?.set(NSKeyedArchiver.archivedData(withRootObject: token), forKey: "a")
            UserDefaults(suiteName: SharedGroupName)?.synchronize()
        }

        if error == nil && operation.moreComing {
            self.fetchPendingNotifications(notifications: queryNotifications, completion: completion)
        } else {
            self.perform(queryNotifications: queryNotifications, completion: completion)
        }
    }

    operationQueue.addOperation(operation)
}

如何将通知标记为已读?

func perform(queryNotifications: [CKNotification], completion: ErrorHandler? = nil) {

    var currentQueryNotifications = queryNotifications

    if let queryNotification = currentQueryNotifications.first {

        currentQueryNotifications.removeFirst()

        let operation = CKMarkNotificationsReadOperation(notificationIDsToMarkRead: [queryNotification.notificationID!])
        operation.markNotificationsReadCompletionBlock = { _, _ in

            print("marked as read")
            self.perform(queryNotifications: currentQueryNotifications, completion: completion)
        }

        OperationQueue().addOperation(operation)

    } else {

        completion?(nil)
    }
}

控制台上的输出是什么?

+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
+++++ , containerIdentifier=iCloud.pl.blueworld.fieldservice, badge=0>
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read
marked as read

关于为什么一直出现已读通知,文档直接与WWDC视频相矛盾。 Apple Docs 说清楚(强调):

If you mark one or more notifications as read using a CKMarkNotificationsReadOperation object, those notifications are not returned, even if you specify nil for previousServerChangeToken.

但是,WWDC 2014 视频 231(高级 Cloudkit)明确表示已读通知被删除并且总是 返回以便用户的其他设备仍然可以看到它们。演示者甚至举了一个例子,引用了一个在所有设备上突出显示大笔交易的应用程序,一旦任何设备将通知标记为已读,任何其他设备都应该看到 "read" 通知并禁用突出显示。为此,他们必须能够仔细阅读标记为 "read."

的通知

因此,我们必须考虑通知的两个独立属性:

  • read/unread 状态:告诉我们用户的任何设备是否看到了此通知。
  • 服务器更改令牌:告诉我们在每个特定设备上查看了哪些通知。

将您收到的更改令牌保存在提取的完成块中,并将其传递给后续的提取。这是确保设备只看到 "new" 通知的唯一方法。使用 read/unread 状态来了解用户是否已经在另一台设备上对事件进行了操作。