I have an oplock error that occurs from time saving to CloudKit. I can't seem to be able to find out why, but I suspect it happens when I manually edit CKRecords from the CloudKit dashboard and then fetch and modify that record from the app. Does anyone have an explanation to what oplock means and where I should start looking?
This is the error
Error Error saving record <CKRecordID: 0x79c42d60; 418deec9-ee5e-46b8-8877-606c14a5fe92:(_defaultZone:__defaultOwner__)> to server: client oplock error updating record
This is my code, note that the error appears even if I don't change the record
[self.publicDB saveRecord:self.currentUser completionHandler:^(CKRecord *record, NSError *error)
{
if(error)
{
NSLog(@"Error %@", error.localizedDescription);
}
else
{
NSLog(@"Saved access to Cloudkit");
}
}];
You are getting this error because the version of the User record on the server is newer than what you're trying to update it with. If you take the error code rawValue (14) and map it to the CKErrorCode enum, you see it maps to:
CKErrorCode.ServerRecordChanged /* The record was rejected because the version
on the server was different */
This is why @shawnynicole answer above solves the issue with a fetchRecordWithId
right before the attempt to save the user record.
I've found if very helpful to parse all the CloudKit error codes and add to the message description as per Apple's comments...otherwise you get error messages like: "client oplock error updating record" !!
The CKError code & comments can be found in the CloudKit headers:
public enum CKErrorCode : Int {
case InternalError /* CloudKit.framework encountered an error. This is a non-recoverable error. */
case PartialFailure /* Some items failed, but the operation succeeded overall */
case NetworkUnavailable /* Network not available */
case NetworkFailure /* Network error (available but CFNetwork gave us an error) */
case BadContainer /* Un-provisioned or unauthorized container. Try provisioning the container before retrying the operation. */
case ServiceUnavailable /* Service unavailable */
case RequestRateLimited /* Client is being rate limited */
case MissingEntitlement /* Missing entitlement */
case NotAuthenticated /* Not authenticated (writing without being logged in, no user record) */
case PermissionFailure /* Access failure (save or fetch) */
case UnknownItem /* Record does not exist */
case InvalidArguments /* Bad client request (bad record graph, malformed predicate) */
case ResultsTruncated /* Query results were truncated by the server */
case ServerRecordChanged /* The record was rejected because the version on the server was different */
case ServerRejectedRequest /* The server rejected this request. This is a non-recoverable error */
case AssetFileNotFound /* Asset file was not found */
case AssetFileModified /* Asset file content was modified while being saved */
case IncompatibleVersion /* App version is less than the minimum allowed version */
case ConstraintViolation /* The server rejected the request because there was a conflict with a unique field. */
case OperationCancelled /* A CKOperation was explicitly cancelled */
case ChangeTokenExpired /* The previousServerChangeToken value is too old and the client must re-sync from scratch */
case BatchRequestFailed /* One of the items in this batch operation failed in a zone with atomic updates, so the entire batch was rejected. */
case ZoneBusy /* The server is too busy to handle this zone operation. Try the operation again in a few seconds. */
case BadDatabase /* Operation could not be completed on the given database. Likely caused by attempting to modify zones in the public database. */
case QuotaExceeded /* Saving a record would exceed quota */
case ZoneNotFound /* The specified zone does not exist on the server */
case LimitExceeded /* The request to the server was too large. Retry this request as a smaller batch. */
case UserDeletedZone /* The user deleted this zone through the settings UI. Your client should either remove its local data or prompt the user before attempting to re-upload any data to this zone. */
}
I was having this error too.
I resolved it by making sure that I always call fetchRecordWithID
first, then call saveRecord
inside the fetchRecordWithID
completion handler.
Take note that fetchRecordWithID
will return CKErrorCodeUnknownItem
for new records. Be sure to handle the error appropriately. I bypass the error so that my code will continue on to saveRecord
, which will insert the record into CloudKit, (given there are no errors with saveRecord
of course).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With