如何从来自 fetchAllSubscriptionsWithCompletionHandler 的 CKSubscriptionID 中检索 CKRecord?
How to Retrieve the CKRecord from the CKSubscriptionID that came from fetchAllSubscriptionsWithCompletionHandler?
感谢您的建议!
我正在使用 fetchAllSubscriptionsWithCompletionHandler,在发送推送通知后,我确实看到了每个 CKSubscription 的订阅 ID。如何从 subscriptionID 中检索 CKRecord?
我没有看到来自已创建的 CKReference 的远程推送通知。我可以通过 CloudKit DashBoard 查看 CKRecord 及其相关记录。我确实在创建父记录时收到推送通知,但在子记录上创建 CKReference 时没有收到。
-(void)SubscribeForReference:(CKRecord *)record
{
NSUserDefaults *trackSubscription = [NSUserDefaults standardUserDefaults];
BOOL hasSubscribed = [[NSUserDefaults standardUserDefaults] objectForKey:@"alreadySubscribedForReference"] != nil;
//BOOL hasSubscribed = [trackSubscription objectForKey:@"alreadySubscribedForReference"];
if (hasSubscribed == false) {
//creates a subscription based on a CKReference between two ckrecords
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"sentences == %@", record.recordID];
// 1) subscribe to record creations
CKSubscription *subscriptionRelation =
[[CKSubscription alloc] initWithRecordType:@"RecordTypeName"
predicate:predicate
options:CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordUpdate | CKSubscriptionOptionsFiresOnRecordDeletion | CKSubscriptionOptionsFiresOnRecordUpdate];
//
CKNotificationInfo *notificationInfo = [[CKNotificationInfo alloc] init];
// I added this because of apple's documentation
notificationInfo.desiredKeys = @[@"word",@"identifier"];
notificationInfo.alertLocalizationArgs = @[@"word"];
notificationInfo.alertLocalizationKey = @"%1$@";
notificationInfo.shouldBadge = YES;
subscriptionRelation.notificationInfo = notificationInfo;
[self.privateDatabase saveSubscription:subscriptionRelation completionHandler:^(CKSubscription * _Nullable subscription, NSError * _Nullable error) {
if (error == nil) {
[trackSubscription setObject:subscription.subscriptionID forKey:@"alreadySubscribedForReference"];
[trackSubscription synchronize];
}else
NSLog(@"something went wrong with saving the CKSubcription with error...\n%@\n",[error localizedDescription]);
}];
}
else{
NSLog(@"\nSubscribeForReference: ALREADY has subscription: %@ set for key 'alreadySubscribedForReference' \n\n ", [trackSubscription objectForKey:@"alreadySubscribedForReference"]);
}
}
下面的代码是 运行 当应用程序启动时,前提是有 Internet 连接:
-(void)runWhenAppStarts
{
CKFetchSubscriptionsOperation *fetchSubscriptionsOperation = [CKFetchSubscriptionsOperation fetchAllSubscriptionsOperation];
fetchSubscriptionsOperation.fetchSubscriptionCompletionBlock = ^(NSDictionary *subscriptionsBySubscriptionID, NSError *operationError) {
if (operationError != nil)
{
// error in fetching our subscription
CloudKitErrorLog(__LINE__, NSStringFromSelector(_cmd), operationError);
if (operationError.code == CKErrorNotAuthenticated)
{
// try again after 3 seconds if we don't have a retry hint
//
NSNumber *retryAfter = operationError.userInfo[CKErrorRetryAfterKey] ? : @3;
NSLog(@"Error: %@. Recoverable, retry after %@ seconds", [operationError description], retryAfter);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryAfter.intValue * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self subscribe];
});
}
}
else
{
if (self.subscribed == NO)
{
// our user defaults says we haven't subscribed yet
//
if (subscriptionsBySubscriptionID != nil && subscriptionsBySubscriptionID.count > 0)
{
// we already have our one CKSubscription registered with the server that we didn't know about
// (not kept track in our NSUserDefaults) from a past app install perhaps,
//
NSLog(@"\nsubscriptionsBySubscriptionID (dictionary) = %@\n",subscriptionsBySubscriptionID);
NSArray *allSubscriptionIDKeys = [subscriptionsBySubscriptionID allKeys];
NSLog(@"\nallSubscriptionIDKeys (array) = %@\n",allSubscriptionIDKeys);
if (allSubscriptionIDKeys != nil)
{
[self updateUserDefaults:allSubscriptionIDKeys[0]];
for (NSString *subscriptions in allSubscriptionIDKeys) {
NSLog(@"subscriptionID: %@\n",subscriptions);
}
}
}
else
{
// no subscriptions found on the server, so subscribe
NSLog(@"...starting subscriptions on server...\n");
[self startSubscriptions];
}
}
else
{
// our user defaults says we have already subscribed, so check if the subscription ID matches ours
//
NSLog(@"...our user defaults says we have already subscribed, with subscriptionsBySubscriptionID = %@\nso check if the subscription ID matches the one already stored in NSUserDefaults...\n",subscriptionsBySubscriptionID);
if (subscriptionsBySubscriptionID != nil && subscriptionsBySubscriptionID.count > 0)
{
// we already have our one CKSubscription registered with the server that
// we didn't know about (not kept track in our NSUserDefaults) from a past app install perhaps,
//
//NSDictionary *subscriptionsBySubscriptionID has a structure of @{key: value} == @{NSString: CKSubscription}
NSArray *allSubscriptionIDKeys = [subscriptionsBySubscriptionID allKeys];//contains the NSString representation of the subscriptionID.
NSArray *allSubscriptionIDVALUES = [subscriptionsBySubscriptionID allValues];// the values are the corresponding CKSubscription objects
for (CKSubscription *values in allSubscriptionIDVALUES) {
NSLog(@"\nCKSubscriptionValue = %@\n",values);
}
NSLog(@"\n...we already have our one CKSubscription registered with the server that..so lets look at allSubscriptionIDKeys =%@.\n\nvalues ...\nallSubscriptionIDVALUES = %@\n\n",allSubscriptionIDKeys,allSubscriptionIDVALUES);
if (allSubscriptionIDKeys != nil)
{
NSString *ourSubscriptionID = [[NSUserDefaults standardUserDefaults] objectForKey:kSubscriptionIDKey];
if (![allSubscriptionIDKeys[0] isEqualToString:ourSubscriptionID])
{
// our subscription ID doesn't match what is on the server, to update our to match
NSLog(@"...our subscription ID doesn't match what is on the server, going to update our NSUserDefaults...\n");
[self updateUserDefaults:allSubscriptionIDKeys[0]];
}
else
{
// they match, no more work here
NSLog(@"...iCloud server already has this subscriptionID, so do nothing.i.e. don't subscribe again..\n");
}
}
}
}
}
};
[self.privateDatabase addOperation:fetchSubscriptionsOperation];
}
当您获取订阅时,您不会获得特定的记录 ID。相反,订阅的 .recordType
会告诉您此订阅监控的记录的 类型 。通常,如果您有 1,000 个用户,那么您将有 1,000 个记录实例,并且每个用户都将创建 sub 来监视他们的实例何时被修改。
触发订阅后,您会收到通知。您可以使用 CKFetchNotificationChangesOperation
检索通知。每个 notif 都包含一个 .recordID
值,具体告诉您哪个记录发生更改并导致订阅触发。
您当前正在查询订阅者以确保用户已正确订阅。接下来,您还需要使用 CKFetchNotificationChangesOperation
查询通知,以查看在触发订阅时更新了哪些记录。
您可以从订阅的谓词中提取RecordID。
例如 - 如果订阅谓词是:
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(author == %@)", artistRecordID];
其中 artistRecordID 是对另一个 recordType 的引用 - 那么您可以通过以下方式提取 RecordID:
[publicDatabase fetchAllSubscriptionsWithCompletionHandler:^(NSArray<CKSubscription *> *subscriptions, NSError *error){
if(error){
// handle error
}
else {
[subscriptions indexOfObjectPassingTest:^BOOL(CKSubscription * obj, NSUInteger idx, BOOL *stop){
if ([obj.predicate isKindOfClass:[NSComparisonPredicate class]]) {
NSComparisonPredicate *p2a = (NSComparisonPredicate *)obj.predicate;
NSExpression *e6 = p2a.rightExpression;
CKReference* ref=e6.constantValue;
// you may extract RecordID here from ref
// for example - to compare against another RecordID:
if([ref.recordID.recordName isEqualToString:artistRecordID.recordID.recordName]){
*stop=YES;
return YES;
}
}
return NO;
}];
}
}];
感谢您的建议!
我正在使用 fetchAllSubscriptionsWithCompletionHandler,在发送推送通知后,我确实看到了每个 CKSubscription 的订阅 ID。如何从 subscriptionID 中检索 CKRecord?
我没有看到来自已创建的 CKReference 的远程推送通知。我可以通过 CloudKit DashBoard 查看 CKRecord 及其相关记录。我确实在创建父记录时收到推送通知,但在子记录上创建 CKReference 时没有收到。
-(void)SubscribeForReference:(CKRecord *)record
{
NSUserDefaults *trackSubscription = [NSUserDefaults standardUserDefaults];
BOOL hasSubscribed = [[NSUserDefaults standardUserDefaults] objectForKey:@"alreadySubscribedForReference"] != nil;
//BOOL hasSubscribed = [trackSubscription objectForKey:@"alreadySubscribedForReference"];
if (hasSubscribed == false) {
//creates a subscription based on a CKReference between two ckrecords
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"sentences == %@", record.recordID];
// 1) subscribe to record creations
CKSubscription *subscriptionRelation =
[[CKSubscription alloc] initWithRecordType:@"RecordTypeName"
predicate:predicate
options:CKSubscriptionOptionsFiresOnRecordCreation | CKSubscriptionOptionsFiresOnRecordUpdate | CKSubscriptionOptionsFiresOnRecordDeletion | CKSubscriptionOptionsFiresOnRecordUpdate];
//
CKNotificationInfo *notificationInfo = [[CKNotificationInfo alloc] init];
// I added this because of apple's documentation
notificationInfo.desiredKeys = @[@"word",@"identifier"];
notificationInfo.alertLocalizationArgs = @[@"word"];
notificationInfo.alertLocalizationKey = @"%1$@";
notificationInfo.shouldBadge = YES;
subscriptionRelation.notificationInfo = notificationInfo;
[self.privateDatabase saveSubscription:subscriptionRelation completionHandler:^(CKSubscription * _Nullable subscription, NSError * _Nullable error) {
if (error == nil) {
[trackSubscription setObject:subscription.subscriptionID forKey:@"alreadySubscribedForReference"];
[trackSubscription synchronize];
}else
NSLog(@"something went wrong with saving the CKSubcription with error...\n%@\n",[error localizedDescription]);
}];
}
else{
NSLog(@"\nSubscribeForReference: ALREADY has subscription: %@ set for key 'alreadySubscribedForReference' \n\n ", [trackSubscription objectForKey:@"alreadySubscribedForReference"]);
}
}
下面的代码是 运行 当应用程序启动时,前提是有 Internet 连接:
-(void)runWhenAppStarts
{
CKFetchSubscriptionsOperation *fetchSubscriptionsOperation = [CKFetchSubscriptionsOperation fetchAllSubscriptionsOperation];
fetchSubscriptionsOperation.fetchSubscriptionCompletionBlock = ^(NSDictionary *subscriptionsBySubscriptionID, NSError *operationError) {
if (operationError != nil)
{
// error in fetching our subscription
CloudKitErrorLog(__LINE__, NSStringFromSelector(_cmd), operationError);
if (operationError.code == CKErrorNotAuthenticated)
{
// try again after 3 seconds if we don't have a retry hint
//
NSNumber *retryAfter = operationError.userInfo[CKErrorRetryAfterKey] ? : @3;
NSLog(@"Error: %@. Recoverable, retry after %@ seconds", [operationError description], retryAfter);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryAfter.intValue * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self subscribe];
});
}
}
else
{
if (self.subscribed == NO)
{
// our user defaults says we haven't subscribed yet
//
if (subscriptionsBySubscriptionID != nil && subscriptionsBySubscriptionID.count > 0)
{
// we already have our one CKSubscription registered with the server that we didn't know about
// (not kept track in our NSUserDefaults) from a past app install perhaps,
//
NSLog(@"\nsubscriptionsBySubscriptionID (dictionary) = %@\n",subscriptionsBySubscriptionID);
NSArray *allSubscriptionIDKeys = [subscriptionsBySubscriptionID allKeys];
NSLog(@"\nallSubscriptionIDKeys (array) = %@\n",allSubscriptionIDKeys);
if (allSubscriptionIDKeys != nil)
{
[self updateUserDefaults:allSubscriptionIDKeys[0]];
for (NSString *subscriptions in allSubscriptionIDKeys) {
NSLog(@"subscriptionID: %@\n",subscriptions);
}
}
}
else
{
// no subscriptions found on the server, so subscribe
NSLog(@"...starting subscriptions on server...\n");
[self startSubscriptions];
}
}
else
{
// our user defaults says we have already subscribed, so check if the subscription ID matches ours
//
NSLog(@"...our user defaults says we have already subscribed, with subscriptionsBySubscriptionID = %@\nso check if the subscription ID matches the one already stored in NSUserDefaults...\n",subscriptionsBySubscriptionID);
if (subscriptionsBySubscriptionID != nil && subscriptionsBySubscriptionID.count > 0)
{
// we already have our one CKSubscription registered with the server that
// we didn't know about (not kept track in our NSUserDefaults) from a past app install perhaps,
//
//NSDictionary *subscriptionsBySubscriptionID has a structure of @{key: value} == @{NSString: CKSubscription}
NSArray *allSubscriptionIDKeys = [subscriptionsBySubscriptionID allKeys];//contains the NSString representation of the subscriptionID.
NSArray *allSubscriptionIDVALUES = [subscriptionsBySubscriptionID allValues];// the values are the corresponding CKSubscription objects
for (CKSubscription *values in allSubscriptionIDVALUES) {
NSLog(@"\nCKSubscriptionValue = %@\n",values);
}
NSLog(@"\n...we already have our one CKSubscription registered with the server that..so lets look at allSubscriptionIDKeys =%@.\n\nvalues ...\nallSubscriptionIDVALUES = %@\n\n",allSubscriptionIDKeys,allSubscriptionIDVALUES);
if (allSubscriptionIDKeys != nil)
{
NSString *ourSubscriptionID = [[NSUserDefaults standardUserDefaults] objectForKey:kSubscriptionIDKey];
if (![allSubscriptionIDKeys[0] isEqualToString:ourSubscriptionID])
{
// our subscription ID doesn't match what is on the server, to update our to match
NSLog(@"...our subscription ID doesn't match what is on the server, going to update our NSUserDefaults...\n");
[self updateUserDefaults:allSubscriptionIDKeys[0]];
}
else
{
// they match, no more work here
NSLog(@"...iCloud server already has this subscriptionID, so do nothing.i.e. don't subscribe again..\n");
}
}
}
}
}
};
[self.privateDatabase addOperation:fetchSubscriptionsOperation];
}
当您获取订阅时,您不会获得特定的记录 ID。相反,订阅的 .recordType
会告诉您此订阅监控的记录的 类型 。通常,如果您有 1,000 个用户,那么您将有 1,000 个记录实例,并且每个用户都将创建 sub 来监视他们的实例何时被修改。
触发订阅后,您会收到通知。您可以使用 CKFetchNotificationChangesOperation
检索通知。每个 notif 都包含一个 .recordID
值,具体告诉您哪个记录发生更改并导致订阅触发。
您当前正在查询订阅者以确保用户已正确订阅。接下来,您还需要使用 CKFetchNotificationChangesOperation
查询通知,以查看在触发订阅时更新了哪些记录。
您可以从订阅的谓词中提取RecordID。
例如 - 如果订阅谓词是:
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(author == %@)", artistRecordID];
其中 artistRecordID 是对另一个 recordType 的引用 - 那么您可以通过以下方式提取 RecordID:
[publicDatabase fetchAllSubscriptionsWithCompletionHandler:^(NSArray<CKSubscription *> *subscriptions, NSError *error){
if(error){
// handle error
}
else {
[subscriptions indexOfObjectPassingTest:^BOOL(CKSubscription * obj, NSUInteger idx, BOOL *stop){
if ([obj.predicate isKindOfClass:[NSComparisonPredicate class]]) {
NSComparisonPredicate *p2a = (NSComparisonPredicate *)obj.predicate;
NSExpression *e6 = p2a.rightExpression;
CKReference* ref=e6.constantValue;
// you may extract RecordID here from ref
// for example - to compare against another RecordID:
if([ref.recordID.recordName isEqualToString:artistRecordID.recordID.recordName]){
*stop=YES;
return YES;
}
}
return NO;
}];
}
}];