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

前端 未结 5 1866
遇见更好的自我
遇见更好的自我 2020-12-03 08:19

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:<

相关标签:
5条回答
  • 2020-12-03 08:48

    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.

    0 讨论(0)
  • 2020-12-03 08:54

    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); }); }}
    
    0 讨论(0)
  • 2020-12-03 08:58

    I have updated GuiSoySauce's code in Swift 4.2.

    func cloudKitLoadRecords(result: @escaping (_ objects: [CKRecord]?, _ error: Error?) -> Void) {
        // predicate
        var predicate = NSPredicate(value: true)
        // query
        let cloudKitQuery = CKQuery(recordType: "recordType", 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)")
                recurrentOperationsCounter += 1
                records.append(record)
            }
            recurrentOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
                if ((error) != nil) {
                    print("-> cloudKitLoadRecords - recurrentOperations - error - \(String(describing: error))")
                    result(nil, error)
                } else {
                    if cursor != nil {
                        print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
                        recurrentOperations(cursor: cursor!)
                    } else {
                        print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
                        result(records, nil)
                    }
                }
            }
            publicDatabase.add(recurrentOperation)
        }
        // initial operation
        var initialOperationCounter = 1
        let initialOperation = CKQueryOperation(query: cloudKitQuery)
        initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
            print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter)")
            initialOperationCounter += 1
            records.append(record)
        }
        initialOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
            if ((error) != nil) {
                print("-> cloudKitLoadRecords - initialOperation - error - \(String(describing: error))")
                result(nil, error)
            } else {
                if cursor != nil {
                    print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
                    recurrentOperations(cursor: cursor!)
                } else {
                    print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
                    result(records, nil)
                }
            }
        }
        publicDatabase.add(initialOperation)
    }
    

    Usage

    cloudKitLoadRecords() { (records, error) -> Void in
        if let error = error {
            // do something
        } else {
            if let records = records {
                // do something
            } else {
                // do something
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-03 08:58

    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!
                        }
                    }
                }
            }
    
    0 讨论(0)
  • 2020-12-03 09:02

    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)
        }
    }
    
    0 讨论(0)
提交回复
热议问题