简体   繁体   中英

batch fetching with cloudkit ckqueryoperation

Is it possible to implement 'batch fetching' in cloudkit, so that i can call a method to pull the next X records? Currently, according to CloudKit Batch Fetches? cloudkit handles this implicitly, but I would like to somehow create a method that allows me to pull a specified number of queries each time. Heres what I have so far: (where continuePullPosts is a similar method to the one i posted)

queryOP.recordFetchedBlock = { record in
        //do stuff here
        annotations.append(postToAdd)
    }

    queryOP.queryCompletionBlock = { [unowned self] (cursor, error) in
        DispatchQueue.main.async {
            if error == nil {
                if completionHandler(annotations) {
                    if cursor != nil {
                        let newQueryOP = CKQueryOperation(cursor: cursor!)
                        self.continuePullPosts(curLocation: curLocation, queryOP: newQueryOP,
                                               annotations: annotations, completionHandler: completionHandler)
                    }
                }
            } else {
                print(error)
                print("could not pull posts")
            }
        }
    }

    queryOP.resultsLimit = CKQueryOperationMaximumResults
    CKContainer.default().publicCloudDatabase.add(queryOP)
}

You should set the result limit with your desired value instead of CKQueryOperationMaximumResults constant value.

My recomendation is define a completion handler with parameters for the CKRecord results and the CKQueryCursor . This completion handler must be invoked in the queryCompletionBlock handler of your CKQueryOperation.

Once your handler is invoked you can process the results, and if the cursor is not null means that there are more results to fetch.

It could be something like that

// Completion Handler (closure) definition
public typealias YourFetchCompletionHandler = (_ records: [CKRecords]?, cursor: CKQueryCursor?) -> (Void)

And here the function where you fetch records

public func fetchYourRecords(_ cursor: CKQueryCursor? = nil, completionHandler handler: @escaping YourFetchCompletionHandler) -> Void
{
    var result: [CKRecord] = [CKRecord]()

    let queryOP: CKQueryOperation

    if let cursor = cursor
    {
        // Operation to fetch another 10 records.
        queryOP = CKQueryOperation(cursor: cursor)
    }
    else
    {
        // Create the operation for the first time
        queryOP = CKQueryCursor(query:...)
    }

    queryOp.recordFetchedBlock = { (record: CKRecord) -> Void in
        result.append(record)
    }

    queryOP.queryCompletionBlock = { [unowned self] (cursor, error) in
        handler(result, cursor)    
    }

    // Fetch only 10 records
    queryOP.resultsLimit = 10
    CKContainer.default().publicCloudDatabase.add(queryOP)
}

Once you invoke this function you can save the cursor returned in the closure in a variable, if it's not nil, to call once again the function to recover the next 10 records.

So @Adolfo's answer got me 95% of the way, but I had one issue:

Every time I would reach the last page (or batch) or data, it just started sending me data from the beginning of my data set.

This was a problem because after I ran out of data, I wanted the loading to stop.

To fix this, I added an argument to specify if it was the first fetch or not. This allowed me to only create a new query when it was the first fetch. If it wasn't the first fetch, no new query would be made and an empty array would be returned.

public func fetchYourRecords(isFirstFetch: Bool, _ cursor: CKQueryCursor? = nil, completionHandler handler: @escaping YourFetchCompletionHandler) -> Void {
    var result: [CKRecord] = [CKRecord]()
    let queryOP: CKQueryOperation
    
    if isFirstFetch {
        // Create the operation for the first time

        queryOP = CKQueryCursor(query:...)
    } else if let cursor = cursor {
        // Operation to fetch another 10 records.

        queryOP = CKQueryOperation(cursor: cursor)
    } else {
        // If not first time and if cursor is nil (which means
        // there is no more data) then return empty array
        // or whatever you want

        handler([], nil)
        return
    }

    queryOp.recordFetchedBlock = { (record: CKRecord) -> Void in
        result.append(record)
    }

    queryOP.queryCompletionBlock = { [unowned self] (cursor, error) in
        handler(result, cursor)    
    }

    // Fetch only 10 records
    queryOP.resultsLimit = 10
    CKContainer.default().publicCloudDatabase.add(queryOP)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM