49

Is there a way for me to check and see if a user is logged into iCloud when they open the app up? I want to be able to direct them to the settings page if they are not logged in, and if they are logged into iCloud and have used the app before - I want to skip the sign in page....

I looked into Apple's iCloud and Cloudkits documentation but was unable to find anything that would be of assistance! Is this even possible to do?

pmoney13
  • 1,075
  • 3
  • 11
  • 19
  • http://stackoverflow.com/questions/11509601/how-to-detect-if-a-user-is-signed-in-to-icloud This makes me think there is no solution to this. – pmoney13 Sep 01 '15 at 16:01

5 Answers5

88

If you just want to know if the user is logged in to iCloud, the synchronous method can be used:

if FileManager.default.ubiquityIdentityToken != nil {
    print("iCloud Available")
} else {
    print("iCloud Unavailable")
}

If the ubiquityIdentityToken is nil and you'd like to know why iCloud isn't available, you can use the asynchronous method:

CKContainer.default().accountStatus { (accountStatus, error) in
    switch accountStatus {
    case .available:
        print("iCloud Available")
    case .noAccount:
        print("No iCloud account")
    case .restricted:
        print("iCloud restricted")
    case .couldNotDetermine:
        print("Unable to determine iCloud status")
    }
}

Note that this requires the use of CloudKit, which requires the CloudKit entitlement:

<key>com.apple.developer.icloud-services</key>
<array>
    <string>CloudKit</string>
</array>

If you want to use the asynchronous method but don't care about why, you should check that accountStatus is available, rather than checking that it is not noAccount:

CKContainer.default().accountStatus { (accountStatus, error) in
    if case .available = accountStatus {
        print("iCloud Available")
    } else {
        print("iCloud Unavailable")
    }
}
Joseph Duffy
  • 4,566
  • 9
  • 38
  • 68
49

Here you go - hopefully self explanatory. For more look at the Apple docs for the NSFileManager function below.

func isICloudContainerAvailable()->Bool {
        if let currentToken = NSFileManager.defaultManager().ubiquityIdentityToken {
            return true
        }
        else {
            return false
        }
    }

See extract below: An opaque token that represents the current user’s iCloud identity (read-only) When iCloud is currently available, this property contains an opaque object representing the identity of the current user. If iCloud is unavailable for any reason or there is no logged-in user, the value of this property is nil.

Duncan Groenewald
  • 8,496
  • 6
  • 41
  • 76
  • 3
    It's possible call iCloud Login from my own app? For example if `isICloudContainerAvailable` return false then open the login. – baquiax Apr 01 '16 at 18:08
  • Didn't work for me. I used ONE iCloud account with 2 different devices. Was expecting to get 1 same key for both devices but was getting 2 different keys. – Tung Fam Nov 11 '16 at 14:53
  • 2
    Update for 2017: it looks like [this solution may not work anymore](http://stackoverflow.com/questions/41602477/why-is-icloud-account-ckcontainer-not-being-found). I'd have to go with the asynchronous methods in [Joseph Duffy's answer](http://stackoverflow.com/a/39053572/54423) (below) from now on. – Anthony C Jan 28 '17 at 20:16
  • 1
    In fact Apple now says you should not use the token for identifying the logged in/out status. From docs: "CloudKit clients should not use this token as a way to identify whether the iCloud account is logged in. Instead, use accountStatus(completionHandler:) or fetchUserRecordID(completionHandler: )." – orschaef May 05 '20 at 13:38
  • I have an iOS test device running 12.4.8 and can confirm that `ubiquityIdentityToken` is `nil` even though 1. signed into iCloud 2. iCloud drive is on and 3. my app is switched on in iCloud. It's reproducible 100% of the time. Testing `CKContainer.default().accountStatus` yields `.available` on the same device. Guess that means we're going to have to implement CloudKit just to check iCloud status. – monstermac77 Oct 15 '20 at 06:01
9

I think this async method is preferred so that you don't block while you are checking.

        CKContainer.defaultContainer().accountStatusWithCompletionHandler { (accountStat, error) in
          if (accountStat == .Available) {
              print("iCloud is available")
          }
          else {
              print("iCloud is not available")
          }
        }
RawMean
  • 8,374
  • 6
  • 55
  • 82
  • Isn't in async by default..? I think it is. – durazno Jun 03 '16 at 06:06
  • @Duranzo the accepted answer is sync. This answer is async. – RawMean Jun 03 '16 at 13:51
  • 1
    From Apple docs for ubiquityIdentityToken: "Accessing the value of this property is relatively fast so you can check the value at launch time from your app’s main thread." – Morgan Jun 28 '16 at 12:39
  • @joseph-duffy Your edit deviated from the original content of the post, I've canceled it. Feel free to post your own answer if you want to add new information or demonstrate usage in a new version of the language. Thanks. – Eric Aya Aug 20 '16 at 11:08
  • @EricAya The current answer is wrong according to the question; if the `accountStat` variable is either `CouldNotDetermine` or `Restricted`, `print("iCloud is available")` will be executed, even though iCloud is not available. I did write the answer in Swift 3, which I'm happy to convert to 2.2. Should I still post a new answer? I didn't feel it would add to the question to post another. – Joseph Duffy Aug 20 '16 at 11:11
  • @JosephDuffy If an answer is wrong, *downvote* and, if you want, comment or post your own answer; but don't fix the code, ever. Wether your fix is good or bad is irrelevant, just don't change other answer's code. This is not what editing is for. Thanks. // I can't decide for you if you should post an answer. But I do now that your edit was not acceptable. Please don't edit like this. :) – Eric Aya Aug 20 '16 at 11:15
7

There are two methods for checking iCloud functionalities, which are provided for two different needs.

  1. Checking for iCloudDrive availability
  2. Checking for iCloud CKContainer availability

Checking for iCloudDrive availability

From Apples documentation:

FileManager.default.ubiquityIdentityToken -> An opaque token that represents the current user’s iCloud Drive Documents identity.

In iCloud Drive Documents, when iCloud is available, this property contains an opaque object representing the identity of the current user. If iCloud is unavailable or there is no logged-in user, the value of this property is nil.

To check for this iCloud functionality we can retrieve that token and check for nil.

// Request iCloud token
let token = FileManager.default.ubiquityIdentityToken
if token == nil {
    print("iCloud (Drive) is not available")
} else {
    print("iCloud (Drive) is available")
}

To assure beeing notified, if iCloudDrive availabilty changes during the app runs -> register to the NotificationCenter for NSUbiquityIdentityDidChange notification.

Checking for iCloud CKContainer availability

To check, wether the users iCloud account is available for accessing the CKContainer (and its private database), we can use an async request on the default container.

// Check iCloud account status (access to the apps private database)
CKContainer.default().accountStatus { (accountStatus, error) in

  if accountStatus == .available {
      print("iCloud app container and private database is available")
  } else {
      print("iCloud not available \(String(describing: error?.localizedDescription))")
  }   
}

To be informed about changes while the app is running, you can use the CKAccountChanged notification.

Community
  • 1
  • 1
LukeSideWalker
  • 7,399
  • 2
  • 37
  • 45
0

Here Solution for Swift 5.x

func isICloudContainerAvailable()->Bool {
    if let _ = FileManager.default.ubiquityIdentityToken {
            return true
    } else {
        return false
    }
}
pooopy
  • 311
  • 2
  • 9