Beginning iCloud in iOS 5 Tutorial Part 1

Update 10/24/12: If you’d like a new version of this tutorial fully updated for iOS 6 and Xcode 4.5, check out iOS 5 by Tutorials Second Edition! Note from Ray: This is the ninth iOS 5 tutorial in the iOS 5 Feast! This tutorial is a free preview chapter from our new book iOS 5 […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Enabling iCloud in your App

In this tutorial, we’ll be creating a simple app that manages a shared iCloud document called “dox”. The app will be universal and will be able to run on both iPhone and iPad, so we can see changes made on one device propagated to the other.

There are three steps to use iCloud in an app, so let’s try them out as we start this new project.

1. Create an iCloud-enabled App ID

To do this, visit the iOS Developer Center and log onto the iOS Provisioning Portal. Create a new App ID for your app similar to the below screenshot (but replace the bundle identifier with a unique name of your own).

Note: Be sure to end your App ID with “dox” in this tutorial, because that is what we will be naming the project. For example, you could enter com.yourname.dox.

Creating an App ID

After you create the App ID, you will see that Push Notifications and Game Center are automatically enabled, but iCloud requires you to manually enable it. Click the Configure button to continue.

Configuring an App ID to use iCloud

On the next screen, click the checkbox next to Enable for iCloud and click OK when the popup appears. If all works well, you will see a green icon next to the word Enabled. Then just click Done to finish.

Clicking Enable for iCloud in the iOS Provisioning Portal

2) Create a provisioning profile for that App ID

Still in the iOS Provisioning Portal, switch to the Provisioning tab, and click New Profile. Select the App ID you just created from the dropdown, and fill out the rest of the information, similar to the below screenshot:

Creating a Provisioning Profile

After creating the profile, refresh the page until it is ready for download, and then download it to your machine. Once it’s downloaded, double click it to bring it into Xcode, and verify that it is visible in Xcode’s Organizer:

Viewing Provisioning Profiles in Xcode Organizer

3) Configure your Xcode project for iCloud

Start up Xcode and create a new project with the iOS\Application\Single View Application template. Enter dox for the product name, enter the company identifier you used when creating your App ID, set the device family to Universal, and make sure Use Automatic Reference Counting is checked (but leave the other checkboxes unchecked):

Creating a new app in Xcode

Once you’ve finished creating the project, select your project in the Project Navigator and select the dox target. Select the Summary tab, and scroll way down to the Entitlements section.

Once you’re there, click the Enable Entitlements checkbox, and it will auto-populate the other fields based on your App ID, as shown below:

Setting entitlements in Xcode

This is what the fields here mean:

  • The Entitlements File points to a property list file which, much like the info.plist file, includes specifications about application entitlements.
  • The iCloud Key-Value Store represents the unique identifier which points to the key-value store in iCloud.
  • The iCloud Containers section represents “directories” in the cloud in which your application can read/write documents. Yes, you have read correctly, I said applications (plural), for a user’s container can be managed by more than one application. The only requirement is that applications have to be created by the same team (as set up in the iTunes Developer Center).
  • The Keychain Access Groups includes keys needed by applications which are sharing keychain data.

You don’t have to change anything from the defaults for this tutorial, so you’re ready to go! If you like you can edit the same settings by editing the file dox.entititlements which is included in your project.

Checking for iCloud Availability

When building an application which makes use of iCloud, the best thing to do is to check the availability of iCloud as soon as the application starts. Although iCloud is available on all iOS 5 devices, the user might not have configured it.

To avoid possible unattended behaviors or crashes, you should check if iCloud is available before using it. Let’s see how this works.

Open up AppDelegate.m, and add the following code at the bottom of application:didFinishLaunchingWithOptions (before the return YES):

NSURL *ubiq = [[NSFileManager defaultManager] 
  URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
    NSLog(@"iCloud access at %@", ubiq);
    // TODO: Load document... 
} else {
    NSLog(@"No iCloud access");
}

Here we use a new method you haven’t seen yet called URLForUbiquityContainerIdentifier. This method allows you to pass in a container identifier (like you set up earlier in the iCloud Containers section) and it will return to you a URL to use to access files in iCloud storage.

You need to call this on startup for each container you want to access to give your app permission to access the URL. If you pass in nil to the method (like we do here), it automatically returns the first iCloud Container set up for the project. Since we only have one container, this makes it nice and easy.

Compile and run your project (on a device, because iCloud does not work on the simulator), and if all works well, you should see a message in your console like this:

iCloud access at file://localhost/private/var/mobile/Library/Mobile%20
  Documents/KFCNEC27GU~com~razeware~dox/

Note that the URL this returns is actually a local URL on the system! This is because the iCloud daemon transfers files from the central servers to a local directory on the device on your behalf. Your application can then retrieve files from this directory, or send updated versions to this directory, and the iCloud daemon will synchronize everything for you.

This directory is outside of your app’s directory, but as mentioned above the act of calling URLForUbiquityContainerIdentifier gives your app permission to access this directory.

iCloud API Overview

Before we proceed further with the code, let’s take a few minutes to give an overview of the APIs we’ll be using to work with iCloud documents.

To store documents in iCloud, you can do things manually if you’d like, by moving files to/from the iCloud directory with new methods in NSFileManager and the new NSFilePresenter and NSFileCoordinator classes.

However doing this is fairly complex and unnecessary in most cases, because iOS 5 has introduced a new class to make working with iCloud documents much easier: UIDocument.

UIDocument acts as middleware between the file itself and the actual data (which in our case will be the text of a note). In your apps, you’d usually create a subclass of UIDocument and override a few methods on it that we’ll discuss below.

UIDocument implements the NSFilePresenter protocol for you and does its work in the background, so the application is not blocked when opening or saving files, and the user can continue working with it. Such a behavior is enabled by a double queue architecture.

The first queue, the main thread of the application, is the one where your code is executed. Here you can open, close and edit files. The second queue is on the background and it is managed by UIKit. For example let’s say we want to open a document, which has been already created on iCloud.

Imagine we have an instance of UIDocument and we want to open a file stored in iCloud. We’d send a message like the following:

[doc openWithCompletionHandler:^(BOOL success) {
    // Code to run when the open has completed
}];

This triggers a ‘read’ message into the background queue. You can’t call this method directly, for it gets called when you execute openWithCompletionHandler. Such an operation might take some time (for example, the file might be very big, or not downloaded locally yet).

In the meantime we can do something else on the UI so the application is not blocked. Once the reading is done we are free to load the data returned by the read operation.

This is exactly where UIDocument comes in handy, because you can override the loadFromContents:ofType:error method to read the data into your UIDocument subclass. Here’s a simplified version what it will look like for our simple notes app:

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName 
  error:(NSError **)outError
{    
    self.noteContent = [[NSString alloc] 
        initWithBytes:[contents bytes] 
        length:[contents length] 
        encoding:NSUTF8StringEncoding];           
    return YES;    
}

This method is called by the background queue whenever the read operation has been completed.

The most important parameter here is contents, which is typically an NSData containing the actual data which you can use to create or update your model. You’d typically override this method to parse the NSData and pull out your document’s information, and store it in some instance variables in your UIDocument subclass, like shown here.

After loadFromContents:ofType:error completes, you’ll receive the callback you provided in the openWithCompletionHandler: block, as shown in the diagram below:

Order of operations when loading an UIDocument

To sum up, when you open a file you receive two callbacks: first in your UIDocument subclass when data has been read, and secondly when open operation is completely finished.

The write operation is pretty similar and it exploits the same double queue. The difference is that when opening a file we have to parse an NSData instance, but while writing we have to convert our document’s data to NSData and provide it to the background queue.

To save a document, you can either manually initiate the process by writing code, or via the autosaving feature implemented in UIDocument (more on this below).

If you want to save manually, you’d call a method like this:

[doc saveToURL:[doc fileURL] 
  forSaveOperation:UIDocumentSaveForCreating 
  completionHandler:^(BOOL success) {
            // Code to run when the save has completed
}];

Just like when opening a file, there is a completion handler which is called when the writing procedure is done.

When asked to write the background queue asks for a snapshot of the contents of our UIDocument subclass. This is accomplished by overriding another method of UIDocument – contentsForType:error.

Here you should return an NSData instance which describes the current model to be saved. In our notes application, we’ll be returning an NSData representation of a string as follows:

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError 
{
    return [NSData dataWithBytes:[self.noteContent UTF8String] 
                          length:[self.noteContent length]];
    
}

The rest is taken care of in the background queue, which manages the storing of data. Once done the code in the completion handler will be executed.

For sake of completeness we should mention that in both reading and writing, instead of NSData you can use NSFileWrapper. While NSData is meant to manage flat files NSFileWrapper can handle packages, that is a directory with files treated as a single file. We’ll cover using NSFileWrapper later in this tutorial.

As we mentioned earlier, the save operation can be called explicitly via code or triggered automatically. The UIDocument class implements a saveless model, where data is saved automatically at periodic intervals or when certain events occur. This way there is no need for the user to tap a ‘save’ button anymore, because the system manages that automatically, e.g. when you switch to another document.

Under the hood the UIKit background queue calls a method on UIDocument called hasUnsavedChanges which returns whether the document is “dirty” and needs to be saved. In case of positive response the document is automatically saved. There is no way to directly set the value for such a method but there are two ways to influence it.

The first way is to explicitly call the updateChangeCount: method. This notifies the background queue about changes. As an alternative you can use the undo manager which is built in the UIDocument class. Each instance of this class (or subclasses) has in fact a property undoManager. Whenever a change is registered via an undo or redo action the updateChangeCount: is called automatically.

It is important to remember that in either case the propagation of changes might not be immediate. By sending these messages we are only providing ‘hints’ to the background queue, which will start the update process when it’s appropriate according to the device and the type of connection.

Contributors

Over 300 content creators. Join our team.