Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot delete CKRecords with `.parent` set

Tags:

ios

cloudkit

I am using CloudKit Sharing and am having an issue deleting records. I create two records: an Entry and an Asset. If I set the .parent of the Asset to point to the Entry, then when I attempt to delete both the Entry and the Asset in the same batch, it fails with a reference violation error:

<CKError 0x600002ac2190: \"Reference Violation\" (31/2025); server message = \"Record delete would violate validating reference ([a1]), rejecting update\"

Details

I create a parent record (Entry) and a child record (Asset), and set the Asset's .parent to the Entry:

let eid = CKRecord.ID(recordName: "e1", zoneID: CKRecordZone.ID(zoneName: "test", ownerName: CKCurrentUserDefaultName))
let e = CKRecord(recordType: "Entry", recordID: eid)
e["title"] = "Entry"

let aid = CKRecord.ID(recordName: "a1", zoneID: CKRecordZone.ID(zoneName: "test", ownerName: CKCurrentUserDefaultName))
let a = CKRecord(recordType: "Asset", recordID: aid)
a["position"] = 1
a.setParent(e)

Then I save both in the same call:

let op = CKModifyRecordsOperation(recordsToSave: [e,a], recordIDsToDelete: nil)
op.modifyRecordsCompletionBlock = { (records, recordIDs, error) in
  print("Returned from modify")
  print("records: \(records)")
  print("error: \(error)")
}
CKContainer.default().privateCloudDatabase.add(op)

The operation completes successfully and the records are properly created in CloudKit.

But, when I attempt to delete both:

let op = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [eid, aid])
op.modifyRecordsCompletionBlock = { (records, recordIDs, error) in
  print("Returned from modify")
  print("records: \(records)")
  print("deleted: \(recordIDs)")
  print("error: \(error)")  
}
CKContainer.default().privateCloudDatabase.add(op)

I get the above error.

Work Arounds

I realize that I could delete the Asset first, and when that returns, delete the Entry. But, my remote management code batches many things together and I don't want to re-work it figure out which things to do first, and I want to minimize the number of remote calls I need.

I've also discovered that if I add another field to the Asset record that references the Entry with a .deleteSelf action, this all works. So, in the above code where I create the Asset, if I add the following (while keeping the setParent() call:

a["entryRef"] = CKRecord.Reference(record: e, action: .deleteSelf)

Then all works correctly.

But, why should I need to create another field I don't need? I would think that sending the deletions in a single call would let CloudKit handle the references properly, without the need for this extra field.

Has anyone experienced this or found a way to work around it without needing the extra reference field? Using CKRecord.References imposes a limit of 750 references on the parent, and I'd rather not have that limit.

like image 934
coping Avatar asked Jan 22 '26 12:01

coping


1 Answers

I realize this question is relatively old, but I ran into the same situation a bit ago, stumbled on this question, and only after some very intense observation did I find an answer which I think is worth sharing:

CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [aid, eid])

Instead of supplying the parent (eid) first in the deletion array, you should supply the child (aid) first. CloudKit will then successfully process the parent-child deletion in a single batch.

I have a similar situation where my deletions are being collected without regard to the hierarchical order, so when this error is thrown, I rearrange the deleted record ID's by keeping all batch atomic failed operations (i.e., successful deletes) at the top of the array (keeping those same order!) and then move any reference violations to the bottom. Finally, I rerun the query in a loop until everything is resolved.

If there's several hierarchical records out of order, it might take some back and forth with CloudKit to finally get them together, but as long as the atomic batch failures are kept in order and each cycle resolves a reference violation, it'll eventually get there.

I've filed feedback, but seeing as this has been an issue for a LONG time, I don't see it being solved any time soon.

like image 156
Dandy Avatar answered Jan 24 '26 02:01

Dandy