0
votes

I am trying to query all the records of a specified RecordType in my CloudKit database. Because I may have more than 100 records of that type, I am using CloudKit cursors, which return records in batches until there are no more records to return (at which point the cursor is nil).

I save the number of records created on each creationDate. I am getting much higher numbers for each date than there are records in the CloudKit dashboard for those dates. For example, on one day when I count 110 records by hand on the CloudKit dashboard, my code says there are 256. On another day where I count 2, my code says there are 7.

Issues I've considered

  • Time zones differences - unlikely because these high counts occur even on days where there are no records created for several days before and after.
  • Maybe I'm counting the same records multiple times - I printed out the list of all 472 records that CloudKit found across all the cursor queries. From some testing, it would appear that there are not any record ID duplicates in that list, and all of the records are of the proper type. (Which seems so weird that I wonder if I need to look at that list more.)
  • Maybe there's something I'm misunderstanding with my use of cursors. Hence, this StackOverflow post.

Here's my code for how I'm working with cursors. If anyone sees anything off, please let me know!

// This is the main code that kicks off the cursor handling

let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: recordType, predicate: predicate)
let sort = NSSortDescriptor(key: "creationDate", ascending: true) // so the 0th result is the earliest
query.sortDescriptors = [sort]
let operation1 = CKQueryOperation(query: query)
operation1.resultsLimit = 5
operation1.queryCompletionBlock = { (cursor, error) in
if error != nil {
    self.recordTypeErrorHandling(error: error as! CKError, uid: uid, appID: appID, recordType: recordType)
}
else {
    self.queryRecordsWithCursor(cursor: cursor, isFirstCheck: true, uid: uid, appID: appID, recordType: recordType)
}

CKContainer.default().publicCloudDatabase.add(operation1)

// Related functions

// adapted from: https://gist.github.com/evermeer/5df7ad1f8db529893f40
func queryRecordsWithCursor(cursor: CKQueryCursor?, isFirstCheck: Bool, uid: String, appID: String, recordType: String) {

    guard let theCursor = cursor else { return }
    let operation = CKQueryOperation(cursor: theCursor)

    // happens each time a record is received
    operation.recordFetchedBlock = { [recordType] record in
        if self.recordTypeToRecordListDict[recordType] == nil {
            self.recordTypeToRecordListDict[recordType] = [record]
        }
        else {
            self.recordTypeToRecordListDict[recordType]?.append(record)
        }
    }
    // happens when all records are done for that cursor
    operation.queryCompletionBlock = { [recordType] cursor, error in
        if error == nil {
            if cursor == nil { // cursor is nil => we've gotten all records, so save them
                self.saveRecordCounts(records: self.recordTypeToRecordListDict[recordType]!, uid: uid, appID: appID, recordType: recordType, isFirstCheck: isFirstCheck) // use isFirstCheck, not the value in the dictionary
            }
            else if self.recordTypeToRecordListDict[recordType] != nil {
                self.queryRecordsWithCursor(cursor: cursor, isFirstCheck: isFirstCheck, uid: uid, appID: appID, recordType: recordType) // recursive call. if we've gotten here, there's definitely a non-nil cursor
            }
        }
        else {
            self.recordTypeErrorHandling(error: error as! CKError, uid: uid, appID: appID, recordType: recordType)
        }
    }
    CKContainer.default().publicCloudDatabase.add(operation)
}

Please let me know if you need more of my code than this! Thank you!

P.S. For what it's worth, when I step through with the debugger, setting the results limit to 5 had no effect. Not sure if that's a related issue.

1
You think your array that you are adding them to already has results. You might need to empty it first - agibson007
I do empty the list in another part of my code. Regardless, the list has no repeats, suggesting there isn't a list clearing issue. - mlecoz

1 Answers

0
votes

Figured it out. It was a combination of issues. One was that I didn't have a recordFetchedBlock for first set of records. Another issue was that I had a for-loop that needed a DispatchGroup to synchronize things.

Another important thing I learned - If you query CloudKit records in the dashboard, it's not necessarily the case that ALL of your records show up.