Reputation: 63
I am trying to handle Cloudkit errors. I read this post: CloudKit - full and complete error handling example but i have a few questions:
1.Why is the error handling not working if i dont set .qualityOFService
? and is .userInitiated
correct, as on the post mentioned it is set to .background
?. Also, do i have to set it for the
loadDataAgain()
?
2.How can i make the error handling reusable that will take an error and another parameter(according to the viewcontroller that is called from)?
@objc func loadData() {
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: myRecordType.type, predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let queryOperation = CKQueryOperation(query: query)
queryOperation.resultsLimit = 5
//if the below line is missing errors will not be handled
queryOperation.qualityOfService = .userInitiated
queryOperation.recordFetchedBlock = { item in
self.array.append(item)
}
queryOperation.queryCompletionBlock = { cursor, error in
if error != nil{
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.loadData), 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.loadData), 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.loadData), userInfo: nil, repeats: false)
}
} else if ckerror.code == CKError.notAuthenticated {
//present relevant alert with action to reload ViewController
} else if ckerror.code == CKError.networkFailure {
//present relevant alert with action to reload ViewController
} else if ckerror.code == CKError.networkUnavailable {
//present relevant alert with action to reload ViewController
} else if ckerror.code == CKError.quotaExceeded {
//present relevant alert with action to reload ViewController
} else if ckerror.code == CKError.partialFailure {
//present relevant alert with action to reload ViewController
} else if (ckerror.code == CKError.internalError || ckerror.code == CKError.serviceUnavailable) {
//present relevant alert with action to reload ViewController
}
}
}
else{
if cursor != nil {
self.loadDataAgain(cursor!)
}
}
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
func loadDataAgain(_ cursor: CKQueryOperation.Cursor) {
let queryOperation = CKQueryOperation(cursor: cursor)
queryOperation.resultsLimit = 5
queryOperation.recordFetchedBlock = { item in
self.array.append(item)
}
queryOperation.queryCompletionBlock = { cursor, error in
if error != nil{
//have the same error handling as above
}
else{
if cursor != nil {
self.loadDataAgain(cursor!)
}
}
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
Database.share.publicDB.add(queryOperation)
}
Upvotes: 1
Views: 225
Reputation: 1084
With respect to your first question, see this answer that suggests errors aren't reported for lower quality of service settings:
it's because [the] system is waiting for a better network condition, while [if] you set
.default
or.userInitiated
the system expects a "real time" response
If you have a look at Apple's Energy Efficiency Guide for iOS Apps documentation section titled "About CloudKit and Quality of Service," Apple seems to confirm this approach, saying
CKOperation is a subclass of the NSOperation class. Although the NSOperation class has a default QoS level of NSQualityOfServiceBackground, CKOperation objects have a default QoS level of NSQualityOfServiceUtility. At this level, network requests are treated as discretionary when your app isn’t in use. On iPhones, discretionary operations are paused when Low Power Mode is enabled.
You'll need to explicitly set a different QOS for every CKOperation you create if you don't want the default .utility
. If you find yourself doing this a lot with related operations, I'd suggest looking at CKOperationGroup
and its default configuration property - if you set a QOS in the configuration it'll override the default in any CKOperation
s in the group.
Upvotes: 2