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];
}
}];
}
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.