2015-04-12 44 views
11

对私人默认区域的查询结果有任何限制Cloudkit?我不知道为什么,我只接受前100条记录与下面的查询:私有区域的CKQuery只返回来自CloudKit的前100条CKRecords

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在答案中提到的,解决方案是使用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)") 
} 
+0

确定。我找到了这个。 http://stackoverflow.com/a/27135836/893771。尽管如此,一段代码仍会有所帮助。 – CppChase

+0

[This answer](http://stackoverflow.com/a/27728609/1966109)对一个类似的问题也展示了如何保持对'queryOperation'的强引用,以防止你的抓取在不完整时结束。 –

+1

此解决方案将导致操作只运行3次!,请改用此代码:http://stackoverflow.com/a/31664231/530884 – Shaybc

回答

8

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

+0

伟大的埃德温。我链接到你以前回答:)。你有任何示例代码如何做'游标'?这会为我节省一些时间。 – CppChase

+0

对不起,我没有一个好的样品准备好了。但这并不困难。你可以像这样开始一个新的queryOperation:var operation = CKQueryOperation(cursor:cursor) –

+2

我接受了你的答案,并用示例代码更新了我的问题。 Tnx哥们。 – CppChase

1

我使用此代码为我的项目从记录类型获取所有记录,它在目标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); }); }} 
1

运行它以完成处理程序,将不会停止,直到所有的记录都取一个函数中的另一种方式。这可以在应用程序中由不同的视图控制器重新使用。

查询

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

优秀的功能,谢谢! –

0

最简单的例子为夫特:

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