I have this code that saves records into CloudKit in iOS in an iPhone 8 Simulator, but not all records are there when I check the database after the save code succeeds in saving 688 records whether 'maxNumberOfRecordsToModify' equal 50 or 400. I am sure I did everything correctly.
let privateDatabase = CKContainer.default().privateCloudDatabase
let maxNumberOfRecordsToModify = 400
func save(_ records: [CKRecord]) {
if iCloudAvailable() {
if records.count > maxNumberOfRecordsToModify {
let sliceOfRecords = Array(records[0 ..< maxNumberOfRecordsToModify])
let leftOverRecords = Array(records[maxNumberOfRecordsToModify ... records.count - 1])
let operation = CKModifyRecordsOperation(recordsToSave: sliceOfRecords, recordIDsToDelete: nil)
operation.savePolicy = CKModifyRecordsOperation.RecordSavePolicy.allKeys
operation.qualityOfService = QualityOfService.userInitiated
operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
if error == nil {
print("Batch saved records!")
save(leftOverRecords)
} else {
if let err = error as? CKError, let time = err.retryAfterSeconds {
print(err)
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
save(sliceOfRecords)
}
} else {
print(error!)
}
}
}
privateDatabase.add(operation)
} else {
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.savePolicy = CKModifyRecordsOperation.RecordSavePolicy.allKeys
operation.qualityOfService = QualityOfService.userInitiated
operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
if error == nil {
print("Batch saved records!")
printNumberOfRecords()
} else {
if let err = error as? CKError, let time = err.retryAfterSeconds {
print(err)
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
save(records)
}
} else {
print(error!)
}
}
}
privateDatabase.add(operation)
}
}
}
func printNumberOfRecords() {
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
privateDatabase.perform(query, inZoneWith: nil) {
(records: [CKRecord]?, error: Error?) in
if error != nil {
print(error as Any)
} else {
if let records = records {
print("Number of records in CloudKit=", records.count)
}
}
}
}
Here is the debug window output:
Batch saved records!
Batch saved records!
Number of records in CloudKit= 32
A couple reasons...
1. If you start an op like `printNumberOfRecords` from that `modifyRecordsCompletionBlock` closure, not all the records will be there yet. I don't know how long you have to wait though.
2. You're calling `perform`, the docs for `perform` say not to use it if you have a lot of records. For me it only returns 100. Instead use CKQueryOperation and the cursor. Like this:
let privateDatabase = CKContainer.default().privateCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: DatabaseNameStrings.recordTypeAffirmation, predicate: predicate)
let op = CKQueryOperation(query: query)
var recordCount = 0
func getChunk(_ op: CKQueryOperation, _ chunkNum: Int) {
op.recordFetchedBlock = { rec in
recordCount += 1
}
op.queryCompletionBlock = { cursor, error in
print("finished chunk \(chunkNum). Count so far: \(recordCount)")
if let error = error {
print(error)
} else if let c = cursor {
let op = CKQueryOperation(cursor: c)
getChunk(op, chunkNum+1)
} else {
print("Done. Record count = \(recordCount)")
}
}
privateDatabase.add(op)
}
getChunk(op, 1)
That returns all 688 records, *if* you wait long enough.