How to use Core Data with iCloud

iCloud-IconUsing Core Data with iCloud seems to be one of the best kept secrets Apple have to offer. For something so relatively simple it’s a surprising brain teaser if you try to follow the scraps of documentation you find.

Let’s take a “human look” at how this works. First we’ll examine the principle, followed by code samples that actually work. It’s less complicated than it seems, so hang in there.

This approach is working fine in iOS 7.1 and 6.1. However it no longer works since iOS 8.

iCloud Core Data in Principle

[emember_protected]

Core Data and iCloud work best with the SQLIte because it’s not an atomically written store (i.e. it does’t have to be saved all at once, but can be saved in chunks). SQLite – much like MySQL – can be updated record by record using log files.

The idea is that you have a local store file, exactly like the one setup by default in the Master/Detail template. You then pass a parameter that will save the log files of each transaction to the iCloud folder. From here all devices read transactions that have not been processed and rebuild the local store files record by record.

Despite what the iOS 6 and earlier documentation may have said, since iOS 7 this is the official Apple recommended approach. In previous years you were encouraged to save the entire store file to the iCloud folder (with a .nosync suffix) but that’s no longer necessary.

To recap: use an SQLite store type and setup your app as you normally would for a “non-iCloud Core Data” project. Have your Team ID ready and an App ID that’s setup to use iCloud.

iCloud Core Data: The Code

In this example I’m starting from the Master Detail template as provided by Xcode 5.1. I’m calling it CDi – short for Core Data iCloud and perhaps an homage to Philips interactive CD project from the early nineties 😉

Let’s take a look at the custom initialiser for the NSPersistenStoreCoordinator. You’ll find it in AppDelegate.m towards the end of the file:

This project will work out of the box. To make it play with iCloud, all we need to do is tell our Persistent Store Coordinator to look at log files in the iCloud folder (a URL) and a name for our cloud store (an NSString – optional since iOS 7).

Let’s amend the above method with the details. These need to be created as an NSDictionary and passed as options like so:

The slightly tricky bit is how to find the URL for your ubiquitous folder. That – again – is kept under lock and key and not mentioned anywhere when this topic is discussed. Anyway… here it is, a convenient method which is called in the above code – just paste this at the end of your AppDelegate.m file:

It looks more complex than it is:

Your iCloud (or ubiquity) folder is made up of a private path on the device, your Team ID and your Bundle ID. The latter can be extracted from your project – just make sure it matches the reverse domain App ID that you’ve setup. Mine is com.versluis.buyme – I’m using it for all kinds of test jobs.

Your Team ID is a weird looking 10 digit value made up of capital letters and numbers. I’ll show you how to find it in the next article. Replace it appropriately.

The method returns a URL which we can pass in to the coordinator options.

We’re done in AppDelegate – let’s focus on MasterViewController next.

Notifications for Store Reconciliations

If Device A adds or changes a record from the store, it writes a new log entry into the CloudLog. In the background Core Data will notice this and reconcile the store file in Device B. Right now, we’d need to restart Device B for the changes to be displayed.

But that’s lame – we’d like to display new data live of course. To do this, we can subscribe to the following notification and listen to when new data has arrived in our store file. When this happens, we need to merge the changes into our managed object context so they can be displayed.

Here we setup the appropriate observer, together with a method that does the merging.

Add this to MasterViewController.m or your “listening” class:

Believe it or not – that’s all we need to do in code. And it didn’t take four scattered manuals to explain it. The Master/Detail Template uses an NSFetchedResultsController, so as soon as the managed object context has new data, it is notified and updates the table view.

Xcode Preflight Check

Before we launch this app for the first time, make sure Xcode is configured correctly:

  • Check the Bundle ID and make sure this App ID is configured to use iCloud.
  • In addition, in your Xcode Project under Capabilities, switch on iCloud.
  • Make sure the Ubiquity Container matches your App ID / Bundle ID.
  • [/emember_protected]

    Testing

    To test this, deploy the app on two devices. Initial deployment may take some time, especially on iOS 6 depending on the store size. On iOS 7 the store is setup asynchronously and will return faster – but I can’t overemphasise this enough: Have Patience, Young Skywalker!

    When both devices are up and running, add records to either device, then wait for them to show up on the other device. You can also delete and change them. It’s a lot of fun to watch!

    Note that since Xcode 5 you can use the Simulator for testing too: just make sure you’re logged into the same iCloud account as your device. Simulator will take much longer to read changes, iDevices take between 10 and 30 seconds to show new results.

    Working Project on GitHub

    You can get the source code for this exercise on GitHub. Make sure you replace the Bundle ID (in Xcode) and Team ID (in grabCloudURL) with your own values for this to work:

    Further Reading:

About Jay Versluis

Jay is a medical miracle known as Super Survivor. He runs two YouTube channels, five websites and several podcast feeds. To see what else he's up to, and to support him on his mission to make the world a better place, check out his Patreon Campaign.

2 thoughts on “How to use Core Data with iCloud

  1. Note on iOS 6 and the Simulator

    The iOS Simulator only understands iCloud related things as of iOS 7. Therefore, trying to run the above on the iOS 6 Simulator will end with an exception because the URL for the iCloud folder will be nil.

    It will work fine on a real device though – just thought I’d mention it.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.