Reputation: 308
I have run into an issue when saving a CKShare. When executing the share everything works as it should, but if the share is cancelled and tried to share again I receive the error:
"Server Record Changed" (14/2004); server message = "client oplock error updating record"
The following is my sharing code:
func share(_ deck: Deck) {
let zoneID = CKManager.defaultManager.sharedZone?.zoneID
if let deckName = deck.name {
selectedDeckForPrivateShare = deckName
}
var records = [CKRecord]()
let deckRecord = deck.deckToCKRecord(zoneID)
records.append(deckRecord)
let reference = CKReference(record: deckRecord, action: .none)
for index in deck.cards {
let cardRecord = index.cardToCKRecord(zoneID)
cardRecord["deck"] = reference
cardRecord.parent = reference
records.append(cardRecord)
}
let share = CKShare(rootRecord: deckRecord)
share[CKShareTitleKey] = "\(String(describing: deck.name)))" as CKRecordValue?
records.append(share)
let sharingController = UICloudSharingController { (controller, preparationCompletion) in
let modifyOP = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
modifyOP.qualityOfService = .userInteractive
modifyOP.modifyRecordsCompletionBlock = { (savedRecords,deletedRecordIDs,error) in
preparationCompletion(share, CKContainer.default(), error)
controller.delegate = self
if let error = error {
print(error)
}
}
self.privateDatabase.add(modifyOP)
}
sharingController.delegate = self
self.present(sharingController, animated: true, completion: {
})
}
The two methods deckToCKRecord()
and cardToCKRecord()
are what I use to convert from Core Data to CKRecord so that they can be shared. Thank you.
Upvotes: 0
Views: 406
Reputation: 336
It's possible that even though the sharing operation is cancelled (since you weren't explicit on what you mean by cancelled), the CKShare record you set up is actually created and saved in your private database. You simply cancelled changing the participation status (e.g. invited) of the other user. Therefore when you go to repeat this procedure, you are trying to re-save the same CKShare, albeit set up again thus likely modifying the change tag. CloudKit sees this and returns a "Server Record Changed" error.
If I'm correct, you could handle this one of two ways. First, set the value of the CKModifyRecordsOperation
variable savePolicy
to .allKeys
, for example, right where you are setting the qualityOfService
var. This will mandate that even if CloudKit finds the record on your server, it will automatically overwrite it. If you are sure that the uploaded version should ALWAYS win, this is a good approach (and only results in a single network call).
A second more general approach that most people use is to first make a CKFetchRecordsOperation
with the root record's share
var recordID. All CKRecord
objects have a share
var which is a CKReference
, but this will be nil if the object hasn't been shared already. If I am right, on your second try after cancelling, you will find the root record's share reference to contain the CKShare you set up on the first try. So grab that share, then with it, initiate the rest of your parent and other references as you did before!
Upvotes: 4