私有区域的CKQuery只返回来自CloudKit的前100条CKRecords

Cloudkit私有默认区域的查询结果是否有限制? 我不知道为什么我只收到前100个logging与以下查询:

let p = NSPredicate(format: "(type == 'entered') AND (timestamp >= %@) AND (timestamp <= %@)", from, to) let q = CKQuery(recordType: self.beaconRecordType, predicate: p) q.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)] self.privateDatabase?.performQuery(q, inZoneWithID: nil, completionHandler: { results, error in //count = 100 println(results.count) } 

好的。 正如Edwin在答案中提到的,解决scheme是使用CKQueryOperation获取数据的初始块,然后使用completionBlock中的“游标”来触发另一个操作。 这里是一个例子:

UPDATE

 func fetchBeacons(from:NSDate, to:NSDate) { let p = NSPredicate(value: true) let q = CKQuery(recordType: self.beaconRecordType, predicate: p) let queryOperation = CKQueryOperation(query: q) queryOperation.recordFetchedBlock = fetchedARecord queryOperation.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor!, error : NSError!) in if cursor != nil { println("there is more data to fetch") let newOperation = CKQueryOperation(cursor: cursor) newOperation.recordFetchedBlock = self!.fetchedARecord newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock self!.privateDatabase?.addOperation(newOperation) } } privateDatabase?.addOperation(queryOperation) } var i = 0 func fetchedARecord (record: CKRecord!) { println("\(NSDate().timeIntervalSinceReferenceDate*1000) \(++i)") } 

100是标准查询的默认限制。 这个数额是不固定的。 它可以根据总的iCloud负载而变化。 如果你想影响这个数量,那么你需要使用CKQueryOperation并像下面这样设置resultsLimit:operation.resultsLimit = CKQueryOperationMaximumResults; CKQueryOperationMaximumResults是默认值,并将其限制为100(大部分时间)。 不要把这个值设得太高。 如果需要更多logging,则使用queryCompletionBlock的游标继续读取更多logging。

我使用这个代码为我的项目从loggingtypes获取所有logging,这是在目标c。 我使用“Entry”作为所需的键。

 + (void)fetchRecordsWithType:(NSString *)recordType completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler { NSPredicate *truePredicate = [NSPredicate predicateWithValue:YES]; CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType predicate:truePredicate]; CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query]; queryOperation.desiredKeys = @[@"Entry"]; NSMutableArray *results = [NSMutableArray new]; queryOperation.recordFetchedBlock = ^(CKRecord *record) { [results addObject:record]; }; queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) { [self retrieveNextBatchOfQueryFromCursor:cursor results:results error:error completionHandler:completionHandler]; }; [[self CloudKitContainer].privateCloudDatabase addOperation:queryOperation]; } + (void)retrieveNextBatchOfQueryFromCursor:(CKQueryCursor *)cursor results:(NSMutableArray *)results error:(NSError *)error completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler { // CloudKit apparently has query limit if (cursor != nil && !error) { CKQueryOperation *nextOperation = [[CKQueryOperation alloc] initWithCursor:cursor]; nextOperation.recordFetchedBlock = ^(CKRecord *record) { [results addObject:record]; }; nextOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) { [self retrieveNextBatchOfQueryFromCursor:cursor results:results error:error completionHandler:completionHandler]; }; [[self CloudKitContainer].privateCloudDatabase addOperation:nextOperation]; } else { dispatch_async(dispatch_get_main_queue(), ^(void){ completionHandler(results, error); }); }} 

另一种使用完成处理函数在函数内部运行它的方法,它不会停止,直到所有logging被提取。 这可以通过应用程序的不同不同的视图控制器重新使用。

询问

 func cloudKitLoadRecords(result: (objects: [CKRecord]?, error: NSError?) -> Void){ // predicate var predicate = NSPredicate(value: true) // query let cloudKitQuery = CKQuery(recordType: "ClassName", predicate: predicate) // records to store var records = [CKRecord]() //operation basis let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase // recurrent operations function var recurrentOperationsCounter = 101 func recurrentOperations(cursor: CKQueryCursor?){ let recurrentOperation = CKQueryOperation(cursor: cursor!) recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter++)") records.append(record) } recurrentOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in if ((error) != nil) { print("-> cloudKitLoadRecords - recurrentOperations - error - \(error)") result(objects: nil, error: error) } else { if cursor != nil { print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)") recurrentOperations(cursor!) } else { print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done") result(objects: records, error: nil) } } } publicDatabase.addOperation(recurrentOperation) } // initial operation var initialOperationCounter = 1 let initialOperation = CKQueryOperation(query: cloudKitQuery) initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter++)") records.append(record) } initialOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in if ((error) != nil) { print("-> cloudKitLoadRecords - initialOperation - error - \(error)") result(objects: nil, error: error) } else { if cursor != nil { print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)") recurrentOperations(cursor!) } else { print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done") result(objects: records, error: nil) } } } publicDatabase.addOperation(initialOperation) } 

用法

 cloudKitLoadRecords() { (queryObjects, error) -> Void in dispatch_async(dispatch_get_main_queue()) { if error != nil { // handle error } else { // clean objects array if you need to self.objects.removeAll() if queryObjects!.count == 0 { // do nothing } else { // attach found objects to your object array self.objects = queryObjects! } } } } 

Swift最简单的例子:

 func fetchServices(completion: ErrorHandler? = nil) { var records = [CKRecord]() let query = CKQuery(recordType: "Service", predicate: NSPredicate(value: true)) let queryOperation = CKQueryOperation(query: query) queryOperation.recordFetchedBlock = { record in records.append(record) } queryOperation.queryCompletionBlock = { cursor, error in self.fetchServices(with: cursor, error: error, records: records, completion: completion) } database.add(queryOperation) } private func fetchServices(with cursor: CKQueryCursor?, error: Swift.Error?, records: [CKRecord], completion: ErrorHandler? = nil) { var currentRecords = records if let cursor = cursor, error == nil { let queryOperation = CKQueryOperation(cursor: cursor) queryOperation.recordFetchedBlock = { record in currentRecords.append(record) } queryOperation.queryCompletionBlock = { cursor, error in self.fetchServices(with: cursor, error: error, records: currentRecords, completion: completion) } database.add(queryOperation) } else { parseAndSaveServices(with: records, completion: completion) } }