John Scalo
29 July 2014
This is the second in a series of blog posts previewing iOS 8-specific features coming to Numerous.
In my last dispatch covering upcoming iOS 8 features I told you about Numerous running in the Today tab of the iOS Notification Center. The response to that article was amazing and resulted in a flood of new users. If you were part of that flood, thanks! This time I’m going to get more geeky and talk about iCloud sign-in, how iOS 8 makes this possible, and why it’s important for Numerous. There’s even a bit of code below, so watch out.
First let’s rewind a bit. As I detailed in an earlier article, Numerous allows users to sign in with Facebook, Twitter, or Google, but we chose not to allow a “traditional” sign-in flow that has the user give an email address and password. We took our share of 1-star dings thanks to that decision but mercifully since adding Google sign-in in v1.1, the number of gripes has gone way down.
Fast forward to WWDC 2014, where Apple announced CloudKit along with this tidbit:
Leverage the full power of iCloud and build apps with the new CloudKit framework. Now you can easily and securely store and efficiently retrieve your app data like structured data in a database or assets right from iCloud. CloudKit also enables your users to anonymously sign in to your apps with their iCloud Apple IDs without sharing their personal information.
Since we’re currently an iOS-only app and nearly every iOS user has an Apple ID, this seemed like the Holy Grail. Let’s dig in a little more to find out exactly how iOS 8 makes this possible.
The above quote is marketing copy from Apple’s website. It sounds exactly like what we want but doesn’t spell out how to get there. Using the fantastic site asciiwwdc.com I looked at the transcript from Session 208 Introducing CloudKit and here’s exactly what Apple’s engineer had to say about it:
How are we going to let you know, your client, your application, what user is logged in? Well, naively you might think let’s give them an email address. We’re not going to do that obviously. That’s private user identifiable information and we don’t want to give that out. So, instead what we do is on a container by container basis come up with a random ID. This is an identifier that is stable so that your application no matter what client it’s running on talking to this container will get the same identifier, but it’s not identifying the user via any personal information. … You can take this identifier and do with it what you will.
A ha! The engineer seems to be saying that each CloudKit storage container has a UUID which is “stable” across devices and app reinstalls for a given iCloud user.
(As an aside, this got me to thinking— the iCloud key-value store has been available since iOS 6. If my app just creates a UUID and sticks it there then don’t I get the same anonymous iCloud sign-in functionality without requiring iOS 8? The answer is yes, it does, but with one huge catch: it‘s not all that hard for the user to delete your app’s key-value store. There’s UI exposed to do this in Settings and Apple tells you how here. Is it likely that a user would delete my app’s iCloud storage? Not at all. But imagine the consequences: the user’s login token is lost to time and all the numbers they’ve created in the app are lost because they can no longer sign in. OK, fun thought experiment but let’s not do that.)
So in iOS 8 if your app is provisioned for CloudKit then you can access your app’s iCloud container simply by calling [CKContainer defaultContainer]
. According to the engineer’s statements above, this CKContainer should have a “random ID” associated with it that I can use to identify the iCloud user, right? Nope. No such property exists on CKContainer. It turns out that when the app’s iCloud container is created, a user record is auto-generated and attached to that container, and it’s that user record that we want. With that in mind, here’s the code:
[[CKContainer defaultContainer] fetchUserRecordIDWithCompletionHandler:^(CKRecordID *recordID, NSError *error) {
... handle errors ...
self.magicaliCloudUserID = recordID.recordName;
}];
And just to be double-extra sure, I verified that
So that’s it. With just a few lines of code we’ve bagged the Holy Grail: anonymous password-less sign-in using a service that nearly every iOS 8 user is guaranteed to have.
Here’s the final result in all its glory: