我正在设置注册iCloud更改的通知。
说添加了一个新设备添加到iCloud帐户中,我只是想知道该设备将如何获取私有数据库记录。
我需要做一个关闭查询吗?
我希望在其他时间都使用通知。
让我们从订阅通知的一些相关特征开始:
首先:订阅通知特定于用户 设备对。如果我在手机上安装您的应用程序,我将开始收到通知。直到我也将应用程序安装到那里,我才会在其他设备上收到通知。
第二:通知不可靠。Apple文档很清楚,它们不能保证交货。当您收到通知时,可能已经有几次通知。因此,Apple提供的两种机制可以跟踪您看到的通知:
- 读取/未读状态:您可以将Notifs标记为读取。苹果的文档与实际的作用相矛盾。此页面说
如果使用ckmarknotificationReadoperation对象将一个或多个通知标记为读取,则这些通知也不会返回,即使您为先前的verchangetoken指定零。
但是,这不是真的。获取操作清楚地返回了读取和未读通知。WWDC 2014 Video 231(高级CloudKit)与文档页面相矛盾,解释了始终返回未读令牌以及读取令牌,因此可以同步多个设备。该视频给出了一个特定的示例,显示了这种行为的好处。此行为也记录在SO上:CKFetchNotificationChangeSoperation返回旧通知
- 更改令牌:每个获取操作将返回可以缓存的更改令牌。如果将令牌传递给获取,则获取只会从那时起返回令牌,无论是阅读还是未读。
乍一看,Apple似乎正在为您想要的行为提供:在一个设备上安装应用程序,开始处理通知,在第二个设备上安装应用程序,然后获取所有先前的通知以赶上。
不幸的是,正如我在ckfetchnotification ChangeSoperation中记录的那样:为什么读通知全部nil?读取通知中的所有信息都丢失了。
在我的情况下,我选择了:
- 始终在启动时获取最新记录
- 使用先前保存的更改令牌获取通知(如果存在)
- 处理新通知
- 将通知标记为读取
- 保存最新的更改令牌,用于在下一个获取
对于您的方案,您可以尝试:
- 使用先前保存的更改令牌获取通知(如果存在)
- 处理通知(请勿将其标记为读取)
- 保存最新的更改令牌,用于在下一个获取
您的第一个设备将忽略每个后续获取上的旧通知,因为您正在从更改令点开始每个获取。您的第二个设备将从第一次执行的零更改令牌开始,从而拾取所有旧通知。
一个谨慎的词:即使上述WWDC视频清楚地说苹果保留了所有旧的通知,但我没有发现任何文档,说明他们持有此信息多长时间。它可能是永远的,可能不是。
使用通知fetch示例
更新这是我如何获取通知,标记它们阅读并缓存更改令牌:
@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];
}