The iOS 5 Developer's Cookbook: iCloud Basics
- Setting Up an iCloud-Compatible Project
- How iCloud Works
- iCloud Container Folders
- Recipe: Trying Out iCloud
- Working with UIDocument
- Recipe: Subclassing UIDocument
- Metadata Queries and the Cloud
- Handy Routines
- Recipe: Accessing the Ubiquitous Key-Value Store
- Recipe: UIManagedDocument and Core Data
- Summary
Ready to emerge from the confines of a single device or desktop system? Welcome to iCloud. iCloud lets you share data beyond the limitations of individual hardware and into a central data-storage hub. With iCloud, your applications can access and update shared documents without explicit synchronization. Changes on one unit propagate out to all other registered systems, updating when that hardware next accesses the Internet. This completely passive coordination is yours for free via iCloud. All you need to do is set up some entitlements and incorporate its features in your applications. It’s surprisingly easy to use. This chapter introduces iCloud, shows you how it works, introduces the key classes you’ll use, and walks you through several recipes and code snippets that show you how to use it in your own projects.
Setting Up an iCloud-Compatible Project
To get started on your first iCloud project, you add a simple entitlement. Open the Project > TARGETS > Entitlements pane and check the Enable Entitlements box shown in Figure 18-1. This option builds a new entitlement file, adding a trio of entitlement types. These types include iCloud containers (the shared folders where you can add and read data), iCloud Key-Value Store (enabling a shared dictionary look-up system), and Keychain Access Groups (allowing secure-item sharing between applications).
Figure 18-1 Enable iCloud functionality by establishing entitlements for your application. You can use the + button to add additional containers to your application. The first-listed container, which you can see defaults to the application identifier, acts as the default ubiquitous store.
Although Apple briefly flirted with enabling iCloud access on an application-by-application basis during the iOS 5 beta period, it eventually decided to enable iCloud for all applications without special provisioning. Any application identifier added to the provisioning portal is automatically enabled with iCloud access. This decision means that when you check the Entitlements box, you’ve done everything needed to get started with iCloud.
Determining Your Team Identifier Prefix
Every cloud application uses a special internal prefix—and it’s not the one you’re probably thinking of, the one associated with application identifiers at the provisioning portal. iCloud service uses your developer identifier (the “Team Identifier Prefix”) to enable you to write to and access cloud-based data. This identifier is associated with your account and listed in the iOS Member Center in the Your Account page as your organization or individual ID.
You can look this identifier up by visiting developer.apple.com/ios, and clicking Member Center. Sign in with your developer credentials and view the Your Account tab. The Program Membership section lists the identifier between the account name and account type entries.
Outside of visiting the developer site, the easiest way to locate the prefix you’re looking for is to take a peek into your Team Provisioning Profile. Use the Organizer > Devices > Provisioning Profiles pane to locate the provision file. Right-click iOS Team Provisioning Profile and choose Reveal Profile in Finder. Open this file in TextEdit and search for the TeamIdentifier key. It should appear exactly once in the file. Directly below the key , you will find a single-value, which is your team identifier prefix.
<key>TeamIdentifier</key> <array> <string>YOURIDENTIFIER</string> </array>
Containers
This team identifier prefix is used for all shared iCloud data containers. You can see this prefix in action by examining the default ubiquity folder for your application. Consider the following call. When passed nil as an argument, it looks up the default ubiquitous (i.e., iCloud) container URL. The default container is always the first item listed in the com.apple.developer.ubiquity-container-identifiers entitlement.
NSURL *ubiquity = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSLog(@"Ubiquity: %@", ubiquity);
When executed, this code prints out the path to that container. The path includes a tilda-escaped version of the application identifier, complete with a team identifier prefix. The identifier ensures that containers are unique to each developer.
2011-08-29 12:17:41.094 HelloWorld[8069:707] Ubiquity: file://localhost/private/var/mobile/Library/Mobile%20Documents/YOURTEAMIDENTIFIER~com~sadun~HelloWorld/
The default value added to the initial entitlement includes just one container, associated with the application identifier of the current project.
<key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>$(TeamIdentifierPrefix)com.sadun.HelloWorld</string> </array>
You can extend this entitlement to add more identifiers, so long as those items are associated with your team prefix, e.g.
<key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>$(TeamIdentifierPrefix)com.sadun.HelloWorld</string> <string>$(TeamIdentifierPrefix)com.sadun.AnotherApp</string> <string>$(TeamIdentifierPrefix)com.sadun.SharedStorage</string> </array>
Adding more identifiers enables you to add cross-application document sharing for any application built by the same developer account. Adding, for example, com.sadun.AnotherApp to these entitlements allows the HelloWorld application to access the AnotherApp application’s default container.
The reverse domain-name identifiers do not have to be tied to a specific application. Although the entitlement domains default to the application name (com.sadun.HelloWorld), you can add shared items or replace the default container with a shared name (e.g., com.sadun.SharedStorage).
Retrieving Identifiers in Code
Your team prefix plays an important role in iCloud development. Outside of the default container (the first entitlement item), which you can access by passing nil to the ubiquity URL query, you cannot access other containers without your team identifier prefix. You can hard code the prefix as a string constant, or you can retrieve the prefix with a simple method call. This method extracts the prefix for you:
+ (NSString *) teamPrefix { NSURL *ubiquity = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; if (!ubiquity) return nil; NSArray *elements = [[ubiquity.path lastPathComponent] componentsSeparatedByString:@"~"]; if (!elements.count) return nil; return [elements objectAtIndex:0]; }
Once extracted, you can create a valid container identifier by combining the prefix to a container name string (e.g., MYPREFIX.com.sadun.SharedStorage).
+ (NSString *) containerize: (NSString *) anIdentifier { NSString *prefix = [self teamPrefix]; if (!prefix) return nil; return [NSString stringWithFormat:@"%@.%@", prefix, anIdentifier]; }
Calls to URLForUbiquityContainerIdentifier: should not return nil. If they do, go through the following checklist:
- Did you add an entitlement for the container identifier?
- Did you use the proper team id prefix in the identifier?
- Did you enable iCloud on the device?
Making sure that you can retrieve a ubiquity container URL forms the first step for iCloud development.