CKFetchRecordsOperation + CKQueryOperations … what am I missing?

女生的网名这么多〃 提交于 2019-12-10 22:39:49

问题


Managed to cobble together a CKFetchRecordsOperation after much searching for sample code; and here it is... but I must have missed something. Don't get me wrong it works a treat... but...

To execute a CKFetchRecordsOperation you need an NSArray of CKRecordIDs; to get a NSArray of CKRecordIDs, you need to execute CKQuery thru which you can build your NSArray of CKRecordIDs.

But wait a minute, the process of extracting the CKRecordIDs uses a CKQuery, thru which I could simply download the CKRecords anyway?

How do you get your NSArray of CKRecordIDs if not with a CKQuery?

-(void)doSingleBeaconsDownload
{
    CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase];
    NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID];
    CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex];

    [self.delegate performSelectorOnMainThread:@selector(processCompleted:) withObject:@"Downloading Configuration ..." waitUntilDone:YES];

    [publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {

        if (error) {
            NSLog(@"Batch Download Error iCloud error %@",error);
        }
        else {
            NSMutableArray *rex2download = [[NSMutableArray alloc] init];
            for (CKRecord *rex in results) {
                [rex2download addObject:rex.recordID];
            }

            CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:rex2download];

           /* fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) {
                if (error) {
                    // Retain the record IDs for failed fetches
                }
                else {
                   // Do something with each record downloaded
                }
            };*/

            fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded) {
                if (error) {
                    // damn ...
                } else {
                    NSLog(@"Downloaded %f", recordsDownloaded);
                }
            };
            fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) {
                if (error) {
                    // Failed to fetch all or some of the records
                }
                else {
                    for(CKRecord *record in results) {
                        NSLog(@"Fini download %lu",(unsigned long)[recordsByRecordID count]);
                    }
                    [self.delegate performSelectorOnMainThread:@selector(beaconsDownloaded:) withObject:noOf waitUntilDone:YES];
                }
            };
            [publicDatabase addOperation:fetchRecordsOperation];
        }
    }];
}

回答1:


From Apple Documentation: A CKFetchRecordsOperation object retrieves CKRecord objects (whose IDs you already know) from iCloud.

A CKQueryOperation is used to retrieve CKRecords from iCloud based on some Query, so you can get them even if you do not know their recordIDs. A CKFetchRecordsOperation is used ONLY when you have the CKRecordIDs. You can create a CKRecordID without accessing iCloud, and you can store them in any local storage you have.

A good use case, which I use for this kind of operation, is when you want to modify a CKRecord, you need to first retrieve it from iCloud (using CKFetchRecordsOperation) and then save it back using CKModifyRecordsOperation.

Have a look at the two WWDC 2014 Videos on CloudKit that explain this pretty well.




回答2:


Thanks for your help! I managed to craft an CKQueryOperation into the code, but ... but my code is soon going be become unreadable with many more of these nested loops? Surely there is a more elegant way to link CKQuery/Fetch/Modify operations; tried dependancies but missing something still ?

-(void)doSingleBeaconsDownload

{

[self.delegate performSelectorOnMainThread:@selector(processCompleted:) withObject:@"Downloading Configuration ..." waitUntilDone:YES];



CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase];

NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID];



CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex];

CKQueryOperation *queryOp =[[CKQueryOperation alloc] initWithQuery:query];

queryOp.desiredKeys = @[@"record.recordID.recordName"];

queryOp.resultsLimit = CKQueryOperationMaximumResults;

NSMutableArray *rex2download = [[NSMutableArray alloc] init];



queryOp.recordFetchedBlock = ^(CKRecord *results)

{

   [rex2download addObject:results.recordID];

};



queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error)

{

// Cursor it seems contains a reference to a second call to it [required] if you try download more then 100 records       

    if (error) {

        NSLog(@"Batch Download Error iCloud error %@",error);

    }

    else {

        CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:rex2download];

       fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) {

            if (error) {

                // Retain the record IDs for failed fetches

            }

            else {

              // Do something ..

            }

        };



        fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded) {

            if (error) {

                // damn

            } else {

                NSLog(@"Downloaded X");

            }

        };



        fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) {

            if (error) {

                // Failed to fetch all or some of the records

            }

            else {

                … Final clean up

            }

        };

        [publicDatabase addOperation:fetchRecordsOperation];

    }

 };



 [publicDatabase addOperation:queryOp];

}



回答3:


Thanks Harry; Here is my third and final working solution; used a singleton global variable to pass the data between the two CKQueryOperations; don't know if that is best/good practice, but it works

... seems a pity you cannot use something like this ...

[fetchRecordsOperation addDependency:queryOp]; &
[queue fetchRecordsOperation]; (doesn't compile)

Would be a far cleaner solution... anyway here is V3 for completeness..

-(void)doSingleBeaconsDownloadV3
{

NSLog(@"doQuery executing");
CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase];
NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID];

CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex];
CKQueryOperation *queryOp =[[CKQueryOperation alloc] initWithQuery:query];
queryOp.desiredKeys = @[@"record.recordID.recordName"];
queryOp.resultsLimit = CKQueryOperationMaximumResults;
//NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO];
//query.sortDescriptors = @[sortDescriptor];

queryOp.recordFetchedBlock = ^(CKRecord *results)
{
    [iBeaconsConfirmed.giRex2Download addObject:results.recordID];
    NSLog(@"fetched %lu",[iBeaconsConfirmed.giRex2Download count]);
};

queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error)
{
    if (error) {
        NSLog(@"Batch Download Error iCloud error %@",error);
    } else {
       NSLog(@"fetched %lu",[iBeaconsConfirmed.giRex2Download count]);
        [self DoFetchV2];
    }
};
[publicDatabase addOperation:queryOp];
}

-(void)DoFetchV2
{

NSLog(@"dofetch executing %lu",[iBeaconsConfirmed.giRex2Download count]);

CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase];
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:iBeaconsConfirmed.giRex2Download];
fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) {
    if (error) {
        // Retain the record IDs for failed fetches
    }
    else {

        // Do something useful with data
    }
};

fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded)
{
    NSLog(@"Downloaded X");
};

fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) {
    if (error) {
        // Failed to fetch all or some of the records
    } else {
        NSLog(@"Fini download %lu",(unsigned long)[recordsByRecordID count]);
    }
};
 [publicDatabase addOperation:fetchRecordsOperation];

}



来源:https://stackoverflow.com/questions/30444050/ckfetchrecordsoperation-ckqueryoperations-what-am-i-missing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!