iOS、CloudKit - 我的应用启动时是不是需要进行抓取?
Posted
技术标签:
【中文标题】iOS、CloudKit - 我的应用启动时是不是需要进行抓取?【英文标题】:iOS, CloudKit - do I need to do a fetch when my app starts?iOS、CloudKit - 我的应用启动时是否需要进行抓取? 【发布时间】:2017-03-04 18:23:06 【问题描述】:我正在为 iCloud 更改通知设置注册。
假设将新设备添加到 icloud 帐户,我只是想知道该设备将如何获取私有数据库记录。
我需要一次性查询吗?
我希望在所有其他时间都会使用通知。
【问题讨论】:
【参考方案1】:让我们从订阅通知的一些相关特征开始:
第一:订阅通知特定于用户+设备对。如果我在手机上安装你的应用程序,我就会开始收到通知。在我也安装应用程序之前,我不会在其他设备上收到通知。
第二:通知不可靠。苹果文档很清楚,他们不保证交付。当您收到通知时,可能已经有几个先前的通知。因此,Apple 提供了两种机制来跟踪您看到的通知:
-
已读/未读状态:您可以将通知标记为已读。 Apple 的文档对这实际上做了什么自相矛盾。 This page 说
如果您使用 CKMarkNotificationsReadOperation 对象将一个或多个通知标记为已读,则不会返回这些通知,即使您为 previousServerChangeToken 指定 nil 也是如此。
然而,事实并非如此。获取操作清楚地返回已读和未读通知。 WWDC 2014 视频 231(高级 Cloudkit)与文档页面相矛盾,解释说始终返回未读令牌和已读令牌,因此多个设备可以同步。该视频给出了一个具体的例子,展示了这种行为的好处。这种行为也记录在 SO:CKFetchNotificationChangesOperation returning old notifications
-
更改令牌:每个获取操作都会返回一个您可以缓存的更改令牌。如果您将令牌传递给 fetch,则 fetch 只会从该点返回令牌,无论是已读还是未读。
乍一看,Apple 似乎提供了您想要的行为:在一台设备上安装应用程序,开始处理通知,在第二台设备上安装应用程序,然后获取所有之前的通知以赶上进度.
不幸的是,正如我在CKFetchNotificationChangesOperation: why are READ notifications all nil? 中记录的那样,每当我获取通知时,之前标记为“已读”的通知都具有 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];
【讨论】:
我将添加一个我的方法的示例。我将更改令牌存储在用户默认值中(编码/解码有点痛苦),以及我的获取操作。 是的,在初始设置时调用它以确保设备已“赶上”。您可以在didFinishLaunchingWithOptions
中调用它,但我更喜欢使用applicationDidBecomeActive
,因为这还包括切换到应用程序/重新进入应用程序,以及重新启动应用程序。我还在didReceiveRemoteNotification
中调用它,因此始终使用相同的代码将设备带到最新的通知。
是的,没错。您可以将该代码放入op.notificationChangedBlock
的完成处理程序中。然后,您确定每个通知都会运行相同的评估,即使是那些错过发送推送通知的通知。您最终有 3 种方式来触发通知处理:启动应用程序 (applicationDidBecomeActive
)、恢复应用程序 (applicationDidBecomeActive
) 和实际接收推送通知 (didReceiveRemoteNotification
)。然后所有 3 个都将使用该操作来检查所有待处理的通知。
实际上,有 4 种方式...因为您在实例化类时也会调用它。
让我们continue this discussion in chat。以上是关于iOS、CloudKit - 我的应用启动时是不是需要进行抓取?的主要内容,如果未能解决你的问题,请参考以下文章