Closed foolbear closed 7 years ago
If you do test for one of the above, then it's still not guaranteed that any CloudKit action after that works. It could be that the status has changed in the mean time. So I think you should always handle these errors from all CloudKit actions. But I do think it's also a good thing to try to detect these at least after app startup so that yo can give special instructions. I do have some sample code for that in the demo app.
So for instance for the account status You could call this function from your root viewController:
func reactToiCloudloginChanges() {
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSUbiquityIdentityDidChange, object: nil, queue: nil) { _ in
EVLog("The user’s iCloud login changed: should refresh all user data.")
Async.main {
self.viewController?.removeFromParentViewController()
}
return
}
}
Of course you should customise the actions to be taken. Here I just remove the view and go back to the splash screen and log a warning.
Besides this react to user change I also test if I can get the user by executing this function:
func getUser(_ retryCount:Double = 1) {
Async.main {
self.loginLabel.isHidden = true
}
EVCloudKitDao.publicDB.requestDiscoverabilityPermission({ (granted) -> Void in
if !granted {
Helper.showError("Discoverability has been denied. You will not be able to be found by other user. You can change this in the settings app, iCloud, iCloud drive, discover by email")
}
}) { (error) -> Void in
Helper.showError("Unable to request discoverability.") //TODO: auto open app settings?
}
EVCloudKitDao.publicDB.discoverUserInfo({ (user) -> Void in
EVLog("discoverUserInfo : \(showNameFor(user))")
Async.main {
let storyboard = UIStoryboard(name: "Storyboard", bundle: nil);
self.viewController = storyboard.instantiateViewController(withIdentifier: "menuViewController")
self.present(self.viewController!, animated: false, completion: nil)
}
}, errorHandler: { error in
switch EVCloudKitDao.handleCloudKitErrorAs(error, retryAttempt: retryCount) {
case .retry(let timeToWait):
EVLog("ERROR in getUserInfo: Can retry after \(timeToWait)")
Async.background(after: timeToWait) {
self.getUser(retryCount + 1)
}
case .fail:
EVLog("ERROR in getUserInfo: \(error.localizedDescription)");
EVLog("You have to log in to your iCloud account. Open the Settings app, Go to iCloud and sign in with your account. (It could also be that your project iCloud entitlements are wrong)")
Helper.showError("Could not get user: \(error.localizedDescription)")
default: // For here there is no need to handle the .Success, and .RecoverableError
break
}
})
}
Again you should again customise the actions to your needs.
For the Network status you could use Reachability. I don't have any specific handling for that implemented. My CloudKit actions will just respond with an error.
iCloud Drive is not an issue for CloudKit. You could us CloudKit without iCloud Drive. I have no experience how you should handle that status.
This guide is useful, thanks!
Or not check these elements(except #1), just do and get result from error handling.