iOS、CloudKit - 我的应用程序启动时是否需要进行提取?
iOS, CloudKit - do I need to do a fetch when my app starts?
我正在为 iCloud 更改通知设置注册。
假设一个新设备被添加到icloud帐户,我只是想知道该设备将如何获取私人数据库记录。
我需要一次性查询吗?
我希望在所有其他时间都能使用通知。
让我们从订阅通知的一些相关特征开始:
首先: 订阅通知特定于用户 + 设备对。如果我在我的 phone 上安装你的应用程序,我就会开始收到通知。在我也安装该应用程序之前,我不会在另一台设备上收到通知。
其次:通知不可靠。苹果文档很清楚,他们不保证交付。当您收到通知时,之前可能有多个通知。因此,Apple 提供了两种机制来跟踪您看到的通知:
- Read/unread status: 您可以将通知标记为已读。 Apple 的文档在这方面的实际作用自相矛盾。 This page 说
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.
然而,事实并非如此。提取操作显然 returns 已读和未读通知。 WWDC 2014 视频 231(高级 Cloudkit)与文档页面相矛盾,解释说未读令牌总是 returned 以及已读令牌,因此多个设备可以同步。该视频提供了一个具体示例,展示了此行为的好处。此行为也记录在 SO 上:CKFetchNotificationChangesOperation returning old notifications
- 更改令牌:每个提取操作都会return一个您可以缓存的更改令牌。如果您将令牌传递给提取,则提取将从该点开始仅 return 个令牌,无论是已读还是未读。
乍一看,Apple 似乎正在提供您想要的行为:在一台设备上安装应用程序,开始处理通知,在第二台设备上安装应用程序,并获取所有这些先前的通知以便赶上。
不幸的是,正如我在 CKFetchNotificationChangesOperation: why are READ notifications all nil? 中记录的那样,每当我获取通知时,之前标记为 "read" 的通知都没有内容。已读通知中的所有信息都丢失了。
在我的场景中,我选择了:
- 始终在启动时获取最新记录
- 使用之前保存的更改令牌(如果存在)获取通知
- 处理新通知
- 将通知标记为已读
- 保存最新的更改令牌以供下次提取使用
对于您的场景,您可以尝试:
- 使用之前保存的更改令牌(如果存在)获取通知
- 处理通知(不要将它们标记为已读)
- 保存最新的更改令牌以供下次提取使用
您的第一台设备将在每次后续提取时忽略旧通知,因为您是从更改令牌点开始每次提取。您的第二个设备将在第一次执行时以零更改令牌开始,从而接收所有旧通知。
提醒一句:尽管前面提到的 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];
}
我正在为 iCloud 更改通知设置注册。
假设一个新设备被添加到icloud帐户,我只是想知道该设备将如何获取私人数据库记录。
我需要一次性查询吗?
我希望在所有其他时间都能使用通知。
让我们从订阅通知的一些相关特征开始:
首先: 订阅通知特定于用户 + 设备对。如果我在我的 phone 上安装你的应用程序,我就会开始收到通知。在我也安装该应用程序之前,我不会在另一台设备上收到通知。
其次:通知不可靠。苹果文档很清楚,他们不保证交付。当您收到通知时,之前可能有多个通知。因此,Apple 提供了两种机制来跟踪您看到的通知:
- Read/unread status: 您可以将通知标记为已读。 Apple 的文档在这方面的实际作用自相矛盾。 This page 说
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.
然而,事实并非如此。提取操作显然 returns 已读和未读通知。 WWDC 2014 视频 231(高级 Cloudkit)与文档页面相矛盾,解释说未读令牌总是 returned 以及已读令牌,因此多个设备可以同步。该视频提供了一个具体示例,展示了此行为的好处。此行为也记录在 SO 上:CKFetchNotificationChangesOperation returning old notifications
- 更改令牌:每个提取操作都会return一个您可以缓存的更改令牌。如果您将令牌传递给提取,则提取将从该点开始仅 return 个令牌,无论是已读还是未读。
乍一看,Apple 似乎正在提供您想要的行为:在一台设备上安装应用程序,开始处理通知,在第二台设备上安装应用程序,并获取所有这些先前的通知以便赶上。
不幸的是,正如我在 CKFetchNotificationChangesOperation: why are READ notifications all nil? 中记录的那样,每当我获取通知时,之前标记为 "read" 的通知都没有内容。已读通知中的所有信息都丢失了。
在我的场景中,我选择了:
- 始终在启动时获取最新记录
- 使用之前保存的更改令牌(如果存在)获取通知
- 处理新通知
- 将通知标记为已读
- 保存最新的更改令牌以供下次提取使用
对于您的场景,您可以尝试:
- 使用之前保存的更改令牌(如果存在)获取通知
- 处理通知(不要将它们标记为已读)
- 保存最新的更改令牌以供下次提取使用
您的第一台设备将在每次后续提取时忽略旧通知,因为您是从更改令牌点开始每次提取。您的第二个设备将在第一次执行时以零更改令牌开始,从而接收所有旧通知。
提醒一句:尽管前面提到的 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];
}