2015-04-12 6 views
11

Czy istnieje limit wyniku zapytania do Cloudkit prywatnej strefy domyślnej? Nie mam pojęcia, dlaczego otrzymałeś tylko pierwsze 100 rekordów z następującym zapytaniem:CKQuery ze strefy prywatnej zwraca tylko pierwszych 100 CKRecords z CloudKit

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) 

} 

porządku. Jak wspomniał Edwin w odpowiedzi, rozwiązaniem jest użycie CKQueryOperation do pobrania początkowego bloku danych, a następnie użycie "kursora" w zakończeniuBlock, aby uruchomić inną operację. Oto przykład:

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

OK. Znalazłem to. http://stackoverflow.com/a/27135836/893771. Jednak kawałek kodu bardzo by pomógł. – CppChase

+0

[Ta odpowiedź] (http://stackoverflow.com/a/27728609/1966109) na podobne pytanie pokazuje również, jak zachować silne odniesienie do 'queryOperation', aby zapobiec zakończeniu pobierania, gdy jest ono niekompletne. –

+1

to rozwiązanie spowoduje, że operacja będzie działać tylko 3 razy !, użyj zamiast tego: http://stackoverflow.com/a/31664231/530884 – Shaybc

Odpowiedz

8

100 jest domyślny limit dla standardowych zapytań. Ta kwota nie jest stała. Może się różnić w zależności od całkowitego obciążenia iCloud. Jeśli chcesz wpłynąć na tę kwotę, musisz użyć CKQueryOperation i ustawić resultLimit w następujący sposób: operation.resultsLimit = CKQueryOperationMaximumResults; To CKQueryOperationMaximumResults jest ustawieniem domyślnym i ograniczy do 100 (w większości przypadków). Nie ustawiaj tej wartości za wysoko. Aby uzyskać więcej rekordów, użyj kursora queryCompletionBlock, aby kontynuować czytanie kolejnych rekordów.

+0

Wielki Edwin. Odpowiedziałem wcześniej na twoje pytanie :). Czy masz przykład kodu, jak zrobić "kursor"? To zaoszczędziłoby trochę czasu dla mnie. – CppChase

+0

Przepraszam, nie mam na to ładnej próbki. Ale nie jest to trudne. Możesz po prostu uruchomić nową funkcję queryOperation w ten sposób: var operation = CKQueryOperation (kursor: kursor) –

+2

Zaakceptowałem twoją odpowiedź i zaktualizowałem moje pytanie za pomocą przykładowego kodu. Tnx stary. – CppChase

1

Używam tego kodu do mojego projektu, aby pobrać cały rekord z rekordu, jest on w ramach celu c. Używam "Entry" jako requiredKeys.

+ (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

Innym sposobem uruchomienia go wewnątrz funkcji z obsługą zakończenia, która nie zostanie zatrzymana, dopóki nie zostaną pobrane wszystkie rekordy. To może być ponownie użyte przez różne kontrolery widoku w całej aplikacji.

Zapytanie

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

Zastosowanie

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

Doskonała funkcja, dzięki! –

0

Najprostszym przykładem 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) 
    } 
} 
Powiązane problemy