evermeer / EVCloudKitDao

Simplified access to Apple's CloudKit
Other
645 stars 65 forks source link

How to determined whether can I do CouldKit Operations now. #89

Closed foolbear closed 7 years ago

foolbear commented 7 years ago
  1. Network status
  2. iCloud account login status
  3. iCloud Drive enabled
  4. iCloud Drive enabled for the App

Or not check these elements(except #1), just do and get result from error handling.

evermeer commented 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.

foolbear commented 7 years ago

This guide is useful, thanks!