Как получить CKRecord из CKSubscriptionID, полученного из fetchAllSubscriptionsWithCompletionHandler?

и спасибо за совет!

Я использую fetchAllSubscriptionsWithCompletionHandler и вижу идентификатор подписки для каждой CKSubscription после отправки push-уведомления. Как получить CKRecord из идентификатора подписки?

Я не вижу удаленного push-уведомления из созданного CKReference. Я вижу CKRecord и связанную с ним запись через CloudKit DashBoard. Я получаю push-уведомление, когда создается его родительская запись, но не когда 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://stackoverflow.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];
}

person Tony Shaker    schedule 11.07.2017    source источник


Ответы (2)


Когда вы получаете подписки, вы не получаете конкретный идентификатор записи. Вместо этого .recordType подписки сообщит вам тип записи, отслеживаемой этой подпиской. Как правило, если у вас есть 1000 пользователей, у вас будет 1000 экземпляров записи, и каждый пользователь будет создавать подписки для отслеживания изменений их экземпляра.

Когда подписка активируется, вы получите уведомление. Вы можете использовать CKFetchNotificationChangesOperation для получения уведомлений. Каждое уведомление содержит значение .recordID, которое конкретно указывает, какая запись была изменена и вызвала срабатывание подписки.

В настоящее время вы запрашиваете подписки, чтобы убедиться, что пользователь правильно подписался. Затем вам также необходимо запросить уведомления с помощью CKFetchNotificationChangesOperation, чтобы увидеть, какие записи были обновлены при активации подписки.

person Thunk    schedule 12.07.2017
comment
Благодарю за ваш ответ! CKFetchSubscriptionsOperation *fetchSubscriptionsOperation взята из примера кода Apple CloudPhoto. Он извлекает непрочитанные идентификаторы cksubscription, которые хранятся в iCloud. Моя проблема в том, что теперь у меня есть эти идентификаторы подписки, как мне получить CKRecord, вызвавший push-уведомление? Например, я добавил 5 записей в iCloud, но увидел только одно push-уведомление. Как получить CKRecord из других 4-CKRecord, вызвавших push-уведомление? - person Tony Shaker; 12.07.2017
comment
Ниже показано, что я вижу после получения подписок, находящихся в 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). - person Tony Shaker; 12.07.2017
comment
Извлеченные значения словаря CKSubscription ... allSubscriptionIDVALUES = ( ‹CKQuerySubscription: 0x170467740; ; Подписка на запрос: recordType= RecordTypeName, predicate=sentences == ‹CKReference: 0x1740316a0; 683B6DED-1C07-49BD-ACF6-1A8B11EDFFA4:(<__defaultOonedefaultZone )›, subscribeOptions=7, subscribeID=AB3A1141-B86C-445E-8B4C-06142F2717C9, zoneID=(null)› - person Tony Shaker; 12.07.2017
comment
Как я уже сказал выше, вы не получаете идентификатор записи из подписки. Вы получаете идентификатор записи, вызвавший уведомление, из свойства .recordID уведомления. Вы получаете уведомления, используя CKFetchNotificationChangesOperation - person Thunk; 12.07.2017

Вы можете извлечь 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;
        }];
    }
}];
person Andrey    schedule 18.04.2018