Search code examples
iosswifticloudcloudkit

Getting a "record to insert already exists" error for CloudKit


When I write to the public container of CloudKit, I'm getting the following error:

CKError 0x283a01a10: "Server Record Changed" (14/2004); server message = "record to insert already exists"

I'm currently saving with the following code:

let publicCloudDatabase = CKContainer.default().publicCloudDatabase
let operation = CKModifyRecordsOperation(recordsToSave: [exampleRecord], recordIDsToDelete: nil)
let operationConfiguration = CKOperation.Configuration()

operationConfiguration.allowsCellularAccess = true
operationConfiguration.qualityOfService = .userInitiated
operation.configuration = operationConfiguration

publicCloudDatabase.add(operation)
publicCloudDatabase.save(progressRecord) { record, error in
    if let error = error {
        print("\(error)")
    } else {
        print("Success")
    }
}

I'm seeing in multiple posts, like this or this, that I need to fetch the existing data from the server first and then modify it before uploading it again because I shouldn't be creating a new record every time. I'm wondering if this only applies to a private container because it really doesn't make sense to fetch other people's content onto your device and modifying it before uploading it. If so, why am I getting this error and how do I resolve it.

It also doesn't make sense to me if this was a case then to only download my data within a record and modifying it before uploading it because 1)I'm not really modifying my existing data, I'm adding new data to the server and 2) the error says that the record already exists which means it's still going to say that the record already exists.


Solution

  • It sounds like you aren't expecting a record to be there and this is an attempt to save the record for the first time. In that case, I think the issue is you are combining a convenience method ".save" with the method of CKModifyRecordsOperation()

    So you are effectively double saving here. Everything down to the publicCloudDatabase.add(operation) action sets up the action, and then kicks off the action, saving the record identified in the "let operation = " step.

    So if you stop there, then you are good to go.

    But because you then call: publicCloudDatabase.save(progressRecord) { record, error in ///etc

    That actually kicks off a separate, completely different save - hence the error!

    But because you don't have a completion block on your operation, you never 'see' the results back.

    Try adding this this:

    operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
        if let error = error {
             //handle error 
        } else {
            //success
        }
    }
    

    above this row:

    publicCloudDatabase.add(operation)
    

    That should hopefully do the trick! (don't forget to kill the .save section)

    Check out the Apple documentation as well: https://developer.apple.com/documentation/cloudkit/ckmodifyrecordsoperation