如何从来自 fetchAllSubscriptionsWithCompletionHandler 的 CKSubscriptionID 中检索 CKRecord?

Posted

技术标签:

【中文标题】如何从来自 fetchAllSubscriptionsWithCompletionHandler 的 CKSubscriptionID 中检索 CKRecord?【英文标题】:How to Retrieve the CKRecord from the CKSubscriptionID that came from fetchAllSubscriptionsWithCompletionHandler? 【发布时间】:2017-07-11 14:17:31 【问题描述】:

感谢您的建议!

我正在使用 fetchAllSubscriptionsWithCompletionHandler,并且在发送推送通知后,我确实看到了每个 CKSubscription 的订阅 ID。如何从订阅 ID 中检索 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];

    //http://***.com/questions/27371588/cloudkit-notifications

    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"]);




下面的代码在应用启动时运行,前提是有互联网连接:

     -(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];

【问题讨论】:

【参考方案1】:

当您获取订阅时,您不会获得特定的记录 ID。相反,订阅的.recordType 会告诉您此订阅监控的记录的类型。通常,如果您有 1,000 个用户,那么您将拥有 1,000 个记录实例,并且每个用户都会创建 subs 来监控他们的实例何时被修改。

当订阅被触发时,您会收到通知。您可以使用CKFetchNotificationChangesOperation 来检索通知。每个通知都包含一个.recordID 值,该值具体告诉您哪些记录发生了更改并导致订阅触发。

您当前正在查询订阅者以确保用户已正确订阅。接下来,您还需要使用CKFetchNotificationChangesOperation 查询通知,以查看在订阅触发时更新了哪些记录。

【讨论】:

感谢您的回复! CKFetchSubscriptionsOperation *fetchSubscriptionsOperation 来自 Apple 的 CloudPhoto 示例代码。它检索存储在 iCloud 上的未读 cksubscription id。我的问题是现在我有那些订阅 ID,如何获取导致推送通知的 CKRecord?例如,我向 iCloud 添加了 5 条记录,但只看到 1 次推送通知。如何从导致推送通知的其他 4-CKRecord 中检索 CKRecord? 下面是我在检索 iCloud 上的订阅后看到的内容......我们已经在服务器上注册了一个 CKSubscription ......所以让我们看看 allSubscriptionIDKeys =( "AB3A1141 -B86C-445E-8B4C-06142F2717C9" , “98D91BCB-4D01-429F-90FB-D26C91993348”, “54FF397E-373D-4289-B6C0-D553BF104CD1”, “A07AC98F-AFCF-48D6-8DA1-A4F986B6E246”,“3E283535-8BAA -4BD2-8C8C-5E83F5274632”、“F35ADFBE-FEEA-4531-9BF3-D76F70F4E87A”、“CFD160D3-EB99-4933-965A-2EF5C1C68798”、“F1C7DA0C-7DBA-4D4C-BB0A-34AD180FD410”) Fetched CKSubscription 字典值 ... allSubscriptionIDVALUES = ( "defaultZone:__defaultOwner_)>,subscriptionOptions=7,subscriptionID=AB3A1141-B86C-445E-8B4C-06142F2717C9,zoneID=(null)>" 就像我上面所说的,您不会从订阅中获得记录 ID。您可以从通知的.recordID 属性中获取触发通知的记录ID。您使用 CKFetchNotificationChangesOperation 获取通知【参考方案2】:

您可以从订阅谓词中提取 RecordID。

例如 - 如果订阅谓词是:

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(author == %@)", artistRecordID];

artistRecordID 是对另一个记录类型的引用 - 那么您可以通过以下方式提取 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 的 CKSubscriptionID 中检索 CKRecord?的主要内容,如果未能解决你的问题,请参考以下文章

如何从来自api控制器的jquery中获取价值

JRBeanCollectionDataSource:如何从 JavaBean 显示来自 java.util.List 的数据?

如何从来自 http.get 响应的对象中提取数据

从 C# 方法,如何调用和运行 DLL,其中 DLL 名称来自 String 变量?

如何从来自api响应的json字符串构造对象列表?

如何从来自 REST 服务的另一个数组中匹配一个数组?