Reputation: 250
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)
}
Upvotes: 1
Views: 578
Reputation: 1358
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)
}
Upvotes: 0
Reputation: 1862
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.
Upvotes: 3