iCloud and UIDocument: Beyond the Basics, Part 3/4

This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer. Welcome back to our document-based iCloud app tutorial series! In this tutorial series, we are making a complete document-based iCloud app called PhotoKeeper, with features that go beyond just the basics. In the first and second parts of the […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Learn how to make a complete UIDocument + iCloud app!

Learn how to make a complete UIDocument + iCloud app!

This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.

Welcome back to our document-based iCloud app tutorial series!

In this tutorial series, we are making a complete document-based iCloud app called PhotoKeeper, with features that go beyond just the basics.

In the first and second parts of the series, we made a fully-functional, UIDocument-based app that works with local files with full CRUD support.

In this third part of the series, it’s finally time to dive into iCloud! We will get almost everything working on iCloud in this tutorial, except for some subtle bits which we’ll leave for the final part of the series.

This project continues where we left off last time, so if you don’t have it already grab the previous code sample and open up the project. It’s iCloud time, baby!

Checking if iCloud is Available

Before you can use iCloud, you first need to check if it is available by calling NSFileManager’s URLForUbiquityContainerIdentifier method.

This method also initializes iCloud for you, and returns the URL of the “iCloud directory” – that is, the directory that that the iCloud daemon checks for files to sychronize to the cloud.

This call might block so it’s important to call it from a background thread. Let’s try it out. Open PTKMasterViewController.m and make the following changes:

// Add new private instance variable
NSURL * _iCloudRoot;
BOOL _iCloudAvailable;

// Add to end of "Helpers" section
- (void)initializeiCloudAccessWithCompletion:(void (^)(BOOL available)) completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        _iCloudRoot = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        if (_iCloudRoot != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"iCloud available at: %@", _iCloudRoot);
                completion(TRUE);
            });            
        }            
        else {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"iCloud not available");
                completion(FALSE);
            });
        }
    });
}

The most important part here is that we call URLForUbiquityContainerIdentifier to figure out where the iCloud root directory is. If it returns nil, that means iCloud isn’t available.

Notice we take a block as a parameter – it’s a method we’ll call after URLForUbiquityContainerIdentifier completes to indicate whether or not iCloud is available. If you’re a bit rusty on blocks, you might want to check out our blocks tutorial.

Now let’s try this out. Go to the refresh method and replace it with the following:

- (void)refresh {
    
    [_objects removeAllObjects];
    [self.tableView reloadData];
    
    self.navigationItem.rightBarButtonItem.enabled = NO;
    [self initializeiCloudAccessWithCompletion:^(BOOL available) {
        
        _iCloudAvailable = available;

        // TODO

        if (![self iCloudOn]) {
            [self loadLocal];        
        }
        
    }];
}

The only difference here is we wait until we check if iCloud is available before refreshing our list of files.

Compile and run the app on your device – not the simulator, because iCloud doesn’t work on the simulator. You should see this in the console:

iCloud not available

You should see this whether you have iCloud enabled or not in Settings. What gives?

This is because for your app to use iCloud, you have to do several project configuration steps. Let’s get to it!

Configuring your Project for iCloud

The first step is to visit the iOS Developer Center and go to the iOS Provisioning Portal. Click App IDs in the sidebar and then New App ID. Create an App ID for your app, similar to the following:

Creating an App ID

After you are done, you will see your App ID in the list:

Configuring an App ID for iCloud

Note that iCloud is not enabled by default – you have to configure it. To do so, simply click the Configure button, check the checkbox for Enable for iCloud, and click Done.

Enabling an App ID for iCloud

Next you need to create a provisioning profile for your App ID. Click the Provisioning tab, click New Profile, and select the appropriate information like you can see below.

Creating a Provisioning Profile

After you finish creating the profile, refresh the page until it is available for download, and download it. Double click it to install it in Xcode. You should see it in the Organizer when you’re done:

Viewing a Provisioning Profile in Organizer

Next, you need to set up your Xcode project to use this provisioning profile. Click your project in the Project Navigator, select the PhotoKeeper target, adn go to the Build Settings tab. Search for code sign, and set the code signing identity to your new provisioning profile.

Using Provisioning Profile

To use iCloud, you need to set up some Entitlements that gives your app access to the iCloud directory. This used to be a pain but now it’s extremely simple. Just go to the Summary tab and click the checkbox for Enable Entitlements. It should autofill everything else, but sometimes I’ve seen it not fill out the iCloud Containers section, so you might have to click the plus button and add it if so.

Enabling iCloud Entitlements

The last step is you need to configure your app to recognize the file types for the documents the app is storing. This isn’t absolutely required at this point, but you will run into troubles if you don’t do it later so you might as well do it now.

Switch to the Info tab and click the plus button in the bottom right. In the pop-up, select Add Document Type.

Adding a Document Type

You will see that it has created a new entry for you in the Document Types list above. Fill it out similarly to this example:

Configuring a Document Type

Next click the plus button again but select Add Exported UTI this time.

Adding an Exported UTI

It will create a new entry for you in the Exported UTIs list above. Fill it out similarly to this example:

Configuring an Exported UTI

It is important that whatever you put for Identifier here matches what you put for Types in the Document Types. Also, I have had trouble with filename extensions that weren’t exactly 3 characters in length, so if you have any troubles in your app try working with 3 character extensions.

Phew – finally done! Compile and run your app, and if all works well you should now see something like this in the console:

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

This gives you the directory on the device where you can find and create iCloud files. The iCloud daemon will automatically pull down new files to this directory as they become available, update files that are there, watch for changes you put there, etc.

We’ll use this directory a lot in this tutorial, but before we go we have the first tricky bit about iCloud to discuss – turning iCloud on and off.

Contributors

Over 300 content creators. Join our team.