我正在设置iCloud更改通知的注册。
假设有一个新设备被添加到icloud帐户,我只是想知道该设备将如何获取私人数据库记录。
我是否需要进行一次性查询?
我希望所有其他时间都会使用通知。
答案 0 :(得分:3)
让我们从订阅通知的一些相关特征开始:
首先:订阅通知特定于用户+设备对。如果我在手机上安装了应用程序,我会开始收到通知。在我在那里安装应用程序之前,我不会在另一台设备上收到通知。
第二:通知不可靠。 Apple文档很清楚,他们不保证交付。当您收到通知时,可能会有几个先前的通知。因此,Apple提供了两种机制来跟踪您已经看到的通知:
如果使用CKMarkNotificationsReadOperation对象将一个或多个通知标记为已读,则即使您为previousServerChangeToken指定了nil,也不会返回这些通知。
但是,这不是真的。获取操作清楚地返回读取和未读通知。 WWDC 2014视频231(高级Cloudkit)与文档页面相矛盾,解释了总是返回未读令牌以及读取令牌,因此多个设备可以同步。该视频给出了一个显示此行为的好处的具体示例。 SO:CKFetchNotificationChangesOperation returning old notifications
上也记录了此行为乍一看,似乎Apple正在提供您想要的行为:在一台设备上安装应用程序,开始处理通知,在第二台设备上安装应用程序,并获取所有这些先前的通知以便赶上
不幸的是,正如我在CKFetchNotificationChangesOperation: why are READ notifications all nil?中记录的那样,无论何时我都会收到通知,之前标记为"""都没有内容。读取通知中的所有信息都将丢失。
在我的场景中,我选择了:
对于您的方案,您可以尝试:
您的第一个设备会在每次后续提取时忽略旧通知,因为您正在从更改令牌点开始每次提取。您的第二个设备将在第一次执行时以零更改令牌开始,从而获取所有旧通知。
提醒一句:尽管前面提到的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];
}