CKFetchRecordsOperation + CKQueryOperations ...我错过了什么?

时间:2015-05-25 18:25:11

标签: ios objective-c nsoperation cloudkit ckrecord

在大量搜索示例代码后,管理好凑齐CKFetchRecordsOperation;这是......但我一定错过了什么。不要误会我的意思......但是......

要执行CKFetchRecordsOperation,您需要一个CKRecordIDs的NSArray;要获得CKRecordID的NSArray,你需要执行CKQuery,你可以构建你的CKRecordIDs的NSArray。

但是等一下,提取CKRecordIDs的过程使用CKQuery,我可以直接下载CKRecords吗?

如果不使用CKQuery,如何获得CKRecordID的NSArray?

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

3 个答案:

答案 0 :(得分:9)

来自Apple Documentation:CKFetchRecordsOperation对象从iCloud中检索CKRecord对象(您已知道的ID)。

CKQueryOperation用于根据某些查询从iCloud中检索CKRecords,因此即使您不知道他们的recordID,也可以获取它们。只有拥有CKRecordID时才使用CKFetchRecordsOperation。您可以在不访问iCloud的情况下创建CKRecordID,并且可以将它们存储在您拥有的任何本地存储中。

我用于此类操作的一个好用例是,当您想要修改CKRecord时,您需要先从iCloud中检索它(使用CKFetchRecordsOperation),然后使用CKModifyRecordsOperation将其保存回来。

查看CloudKit上的两个WWDC 2014视频,可以很好地解释这一点。

答案 1 :(得分:0)

感谢您的帮助!我设法在代码中创建了一个CKQueryOperation,但是......但是我的代码很快就会变得难以理解,还有更多这些嵌套循环?当然有一种更优雅的方式来链接CKQuery / Fetch / Modify操作;尝试过依赖但仍缺少某些东西?

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

}

答案 2 :(得分:0)

谢谢哈利;这是我的第三个也是最后一个解决方案;使用单例全局变量在两个CKQueryOperations之间传递数据;不知道这是最佳/良好的做法,但它有效

...似乎很可惜你不能使用这样的东西......

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

将是一个更清洁的解决方案......无论如何这里是完整性的V3 ..

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

}