CKQuery from private zone returns only first 100 CKRecords from in CloudKit

匿名 (未验证) 提交于 2019-12-03 02:56:01

问题:

Is there any limit to the result of a query to Cloudkit private default zone? I have no clue why I only receive first 100 records with the following query:

let p = NSPredicate(format: "(type == 'entered') AND (timestamp >= %@) AND (timestamp 

Okay. As Edwin mention in the answer, the solution is to use CKQueryOperation to fetch the initial block of data then use the "cursor" in completionBlock to fire another operation. Here is an example:

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)") }

回答1:

100 is the default limit for standard queries. That amount is not fixed. It can vary depending on the total iCloud load. If you want to influence that amount, then you need to use CKQueryOperation and set the resultsLimit like this: operation.resultsLimit = CKQueryOperationMaximumResults; That CKQueryOperationMaximumResults is the default and will limit it to 100 (most of the time). Don't set that value too high. If you want more records, then use the cursor of the queryCompletionBlock to continue reading more records.



回答2:

I use this code for my project to fetch all record from a record type, it's in objective c. I use "Entry" as desiredKeys.

+ (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); }); }}


回答3:

Another way of running it inside a function with a completion handler that won't stop until all records are fetched. This can be re-used by different different view controllers across the app.

Query

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) }

Usage

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!                     }                 }             }         }


回答4:

The simplest example for 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)     } }


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