如何从来自 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;
        }];
    }
}];