Apple are very clear, in every video they state that "The difference between a CloudKit app which handles errors and one that doesn't is the difference between a working app and one that doesn't". However, I can't find anywhere a proper list of what each error means, which Operations throw what or any example code of CloudKit error handling done well for various CKOperations. Even worse, many examples don't handle errors at all and I can't find any documentation from Apple either.
Does anyone have a full and complete example they can share? Any lists of which Operations can throw what?
This post shows a list of errors and a short description of each error. I created this new post because I'm specifically looking for full and complete examples of error handling as Apple recommends. The other post has an incomplete example and is asking specific questions. I already highlighted this post in the comments because it contains a short description of each error type which is useful.
Rob,
Here an example...
func files_saveNotes(rex: Int) {
var localChanges:[CKRecord] = []
let newRecordID = CKRecordID(recordName: sharedDataAccess.returnRexID(index2seek: rex))
let newRecord = CKRecord(recordType: "Note", recordID: newRecordID)
let theLinkID = CKReference(recordID: sharedDataAccess.iCloudID, action: .deleteSelf)
let thePath = sharedDataAccess.fnGet(index2seek: rex)
newRecord["theLink"] = theLinkID
newRecord["theNo"] = rex as CKRecordValue?
newRecord["thePath"] = thePath as CKRecordValue?
let miam = sharedDataAccess.fnGetText(index2seek: rex)
let range = NSRange(location: 0, length: miam.length)
let dataMiam = try? miam.data(from: range, documentAttributes: [NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType])
newRecord["theRTF"] = dataMiam as? CKRecordValue
localChanges.append(newRecord)
let records2Erase:[CKRecordID] = []
let saveRecordsOperation = CKModifyRecordsOperation(recordsToSave: localChanges, recordIDsToDelete: records2Erase)
saveRecordsOperation.savePolicy = .allKeys
saveRecordsOperation.perRecordCompletionBlock = { record, error in
if error != nil {
//print(error!.localizedDescription)
}
// deal with conflicts
// set completionHandler of wrapper operation if it's the case
}
saveRecordsOperation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
self.theApp.isNetworkActivityIndicatorVisible = false
guard error == nil else {
if let ckerror = error as? CKError {
if ckerror.code == CKError.requestRateLimited {
let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
DispatchQueue.main.async {
Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveNotes), userInfo: nil, repeats: false)
}
} else if ckerror.code == CKError.zoneBusy {
let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
DispatchQueue.main.async {
Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveNotes), userInfo: nil, repeats: false)
}
} else if ckerror.code == CKError.limitExceeded {
let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
DispatchQueue.main.async {
Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveNotes), userInfo: nil, repeats: false)
}
} else if ckerror.code == CKError.notAuthenticated {
NotificationCenter.default.post(name: Notification.Name("noCloud"), object: nil, userInfo: nil)
} else if ckerror.code == CKError.networkFailure {
NotificationCenter.default.post(name: Notification.Name("networkFailure"), object: nil, userInfo: nil)
} else if ckerror.code == CKError.networkUnavailable {
NotificationCenter.default.post(name: Notification.Name("noWiFi"), object: nil, userInfo: nil)
} else if ckerror.code == CKError.quotaExceeded {
NotificationCenter.default.post(name: Notification.Name("quotaExceeded"), object: nil, userInfo: nil)
} else if ckerror.code == CKError.partialFailure {
NotificationCenter.default.post(name: Notification.Name("partialFailure"), object: nil, userInfo: nil)
} else if (ckerror.code == CKError.internalError || ckerror.code == CKError.serviceUnavailable) {
NotificationCenter.default.post(name: Notification.Name("serviceUnavailable"), object: nil, userInfo: nil)
}
} // end of guard statement
return
}
if error != nil {
//print(error!.localizedDescription)
} else {
//
}
}
saveRecordsOperation.qualityOfService = .background
privateDB.add(saveRecordsOperation)
theApp.isNetworkActivityIndicatorVisible = true
}
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