iCloud Fundamentals (Key-Value and Document Storage)
From the perspective of users, iCloud is a simple feature that automatically makes their personal content available on all their devices. To allow your app to participate in this “magic,” you design and implement your app somewhat differently than you would otherwise; in particular, you need to learn about your app’s roles when it participates with iCloud.
These roles, and the specifics of your iCloud adoption process, depend on your app. You design how your app manages its data, so only you can decide which iCloud supporting technologies your app needs and which ones it does not.
This chapter gets you started with the fundamental elements of iCloud key-value and document storage that all developers need to know.
First, Provision Your Development Devices
To start developing an iCloud app, you must create an App ID and provisioning profile, described in App Distribution Quick Start. Then enable the iCloud service you want to use, described in Adding iCloud Support in App Distribution Guide. For a list of the app services that are available for your platform and type of developer program membership, see Supported Capabilities.
iCloud Data Transfer Proceeds Automatically and Securely
For most iCloud services, your app does not communicate directly with iCloud servers. Instead, the operating system initiates and manages uploading and downloading of data for the devices attached to an iCloud account. For all iCloud services, the high-level process for using those services is as follows:
Configure the access to your app’s iCloud containers. Configuration involves requesting entitlements and programmatically initializing those containers before using them.
Design your app to respond appropriately to changes in the availability of iCloud (such as if a user signs out of iCloud) and to changes in the locations of files (because instances of your app on other devices can rename, move, duplicate, or delete files).
Read and write using the APIs of the technology you are using.
The operating system coordinates the transfer of data to and from iCloud as needed.
The iCloud services encrypt data prior to transit and iCloud servers continue to store the data in an encrypted format, using secure tokens for authentication. For more information about data security and privacy concerns related to iCloud, see iCloud security and privacy overview.
The iCloud Container, iCloud Storage, and Entitlements
To save data to iCloud, your app places data in special file system locations known as iCloud containers. An iCloud container (also referred to as a ubiquity container) serves as the local representation of the corresponding iCloud storage. It is separate from the rest of your app’s data, as shown in Figure 1-1.
To enable access to any iCloud containers, you request the appropriate entitlements.
Request Access to iCloud Using Xcode Capabilities
The Capabilities tab of your Xcode project manages the creation of the entitlements and containers your app needs to access iCloud. After enabling the iCloud capability, Xcode creates an entitlements file (if one does not already exist) and configures it with the entitlements for the services you selected. As needed, Xcode can also handle any additional configuration, such as the creation of your app’s associated containers.
When you enable iCloud Documents, Xcode configures your app to access the iCloud container whose name is based on the app’s bundle ID. Most apps should only need access to the default container. If your apps share data among each other, configure your targets to share containers, described in Specifying Custom Containers. When an app has access to multiple container IDs, the first ID in the access list is special because it is the app’s primary iCloud container. In OS X, it is also the container whose contents are displayed in the NSDocument
open and save dialogs.
For information about how to choose the correct iCloud technology for your app, see Choose the Proper iCloud Storage API.
Configuring a Common iCloud Container for Multiple Apps
In the Xcode target editor’s Summary pane, you can request access to as many iCloud containers as you need for your app. This feature is useful if you want multiple apps to share documents. For example, if you provide a free and paid version of your app, you might want users to retain access to their iCloud documents when they upgrade from the free version to the paid version. In such a scenario, configure both apps to write their data to the same iCloud container.
Designate one of your iCloud-enabled apps as the primary app. That app’s iCloud container becomes the common container.
For example, in the case of a free and paid app, you might designate the paid app as the primary app.
Enable the iCloud capability for each app.
Configure the primary app with only the default container identifier.
For each secondary app, enable the “Specify custom container identifiers” option and add the container identifier of the primary app to the list of containers.
When reading and writing files in both your primary and secondary apps, build URLs and search for files only in the common storage container. To retrieve the URL for the common storage container, pass the container identifier of your primary app to the URLForUbiquityContainerIdentifier:
method of NSFileManager
. Do not pass nil
to that method because doing so returns the app’s default container, which is different for each app. Explicitly specifying the container identifier always yields the correct container directory.
For more information about how to configure app capabilities, see Adding Capabilities in App Distribution Guide.
Configuring Common Key-Value Storage for Multiple Apps
If you provide a free and paid version of your app, and want to use the same key-value storage for both, you can do that.
Designate one of your iCloud-enabled apps as the primary app.
That app’s iCloud container becomes the common container. For example, in the case of a free and paid app, you might designate the paid app as the primary app.
Enable the iCloud capability for each app.
Enable the key-value storage option for both apps.
Xcode automatically adds entitlements to each app and assigns an iCloud container based on the app’s bundle ID.
For all but the primary app, change the iCloud container ID manually in the app’s
.entitlements
file.Set the value of the
com.apple.developer.ubiquity-kvstore-identifier
key to the ID of your primary app.
iCloud Containers Have Minimal Structure
The structure of a newly created iCloud container is minimal—having only a Documents
subdirectory. For document storage, you can arrange files inside the container in whatever way you choose. This allows you to define the structure as needed for your app, such as by adding custom directories and custom files at the top level of the container, as indicated in Figure 1-2.
You can write files and create subdirectories within the Documents
subdirectory. You can create files or additional subdirectories in any directory you create. Perform all such operations using an NSFileManager
object using file coordination. See The Role of File Coordinators and Presenters in File System Programming Guide.
The Documents
subdirectory is the public face of an iCloud container. When a user examines the iCloud storage for your app (using Settings in iOS or System Preferences in OS X), files or file packages in the Documents
subdirectory are listed and can be deleted individually. Files outside of the Documents
subdirectory are treated as private to your app. If users want to delete anything outside of the Documents
subdirectories of your iCloud containers, they must delete everything outside of those subdirectories.
To see the user’s view of iCloud storage, do the following, first ensuring that you have at least one iCloud-enabled app installed:
In iOS, open Settings. Then navigate to iCloud > Storage & Backup > Manage Storage.
In OS X, open System Preferences. Then open the iCloud preferences pane and click Manage.
A User’s iCloud Storage Is Limited
Each iCloud user receives an allotment of complimentary storage space and can purchase more as needed. Because this space is shared by a user’s iCloud-enabled iOS and Mac apps, a user with many apps can run out of space. For this reason, to be a good iCloud citizen, it’s important that your app saves to iCloud only what is needed in iCloud. Specifically:
DO store the following in iCloud:
User documents
App-specific files containing user-created data
Preferences and app state (using key-value storage, which does not count against a user’s iCloud storage allotment)
Change log files for a SQLite database (a SQLite database’s store file must never be stored in iCloud)
DO NOT store the following in iCloud:
Cache files
Temporary files
App support files that your app creates and can recreate
Large downloaded data files
There may be times when a user wants to delete content from iCloud. Provide UI to help your users understand that deleting a document from iCloud removes it from the user’s iCloud account and from all of their iCloud-enabled devices. Provide users with the opportunity to confirm or cancel deletion.
One way to prevent files and directories from being stored in iCloud is to add the .nosync
extension to the file or directory name. When iCloud encounters files and directories with that extension in the local container directory, it does not transfer them to the server. You might use this extension on temporary files that you want to store inside a file package, but that you do not want transferred with the rest of that package’s contents. Although items with the .nosync
extension are not transferred to the server, they are still bound to their parent directory. When you delete the parent directory in iCloud, or when you evict the parent directory and its contents locally, the entire contents of that directory are deleted, including any .nosync
items.
The System Manages Local iCloud Storage
iCloud data lives on Apple’s iCloud servers, but the system maintains a local cache of data on each of the user’s devices, as shown in Figure 1-3. Local caching of iCloud data allows users to continue working even when the network is unavailable, such as when they turn on airplane mode.
Because the local cache of iCloud data shares space with the other files on a device, in some cases there is not sufficient local storage available for all of a user’s iCloud data. The system addresses this issue by maintaining an optimized subset of files and other data objects locally. At the same time, the system keeps all file-related metadata local, thereby ensuring that your app’s users can access all their files, local or not. For example, the system might evict a file from its iCloud container if that file is not being used and local space is needed for another file that the user wants now; but updated metadata for the evicted file remains local. The user can still see the name and other information for the evicted file, and, if connected to the network, can open it.
Your App Can Help Manage Local Storage in Some Cases
Document-based apps usually do not need to manage the local availability of iCloud files and should let the system handle eviction of files. There are two exceptions:
If a user file is not currently needed and unlikely to be needed soon, you can help the system by explicitly evicting that file from the iCloud container by calling the
NSFileManager
methodevictUbiquitousItemAtURL:error:
.Conversely, if you explicitly want to ensure that a file is available locally, you can initiate a download to an iCloud container by calling the
NSFileManager
methodstartDownloadingUbiquitousItemAtURL:error:
. For more information about this process, see App Responsibilities for Using iCloud Documents.
Prepare Your App to Use iCloud
When users launch your iCloud-enabled app for the first time, invite them to use iCloud. The choice should be all-or-none. In particular, it is best practice to:
Use iCloud exclusively or use local storage exclusively; in other words, do not attempt to mirror documents between your iCloud container and your app’s local data container.
Don’t prompt users again about whether they want to use iCloud vs. local storage, unless they delete and reinstall your app.
Early in your app launch process—in the application:didFinishLaunchingWithOptions:
method (iOS) or applicationDidFinishLaunching:
method (OS X)—check for iCloud availability by getting the value of the ubiquityIdentityToken
property of NSFileManager
, as shown in Listing 1-1.
Listing 1-1 Obtaining the iCloud token
NSFileManager* fileManager = [NSFileManager defaultManager]; |
id currentiCloudToken = fileManager.ubiquityIdentityToken; |
Access this property from your app’s main thread. The value of the property is a unique token representing the currently active iCloud account. You can compare tokens to detect if the current account is different from the previously used one, as explained in Handle Changes in iCloud Availability. To enable comparisons, archive the newly acquired token in the user defaults database, using code like that shown in Listing 1-2. This code takes advantage of the fact that the ubiquityIdentityToken
property conforms to the NSCoding
protocol.
Listing 1-2 Archiving iCloud availability in the user defaults database
if (currentiCloudToken) { |
NSData *newTokenData = |
[NSKeyedArchiver archivedDataWithRootObject: currentiCloudToken]; |
[[NSUserDefaults standardUserDefaults] |
setObject: newTokenData |
forKey: @"com.apple.MyAppName.UbiquityIdentityToken"]; |
} else { |
[[NSUserDefaults standardUserDefaults] |
removeObjectForKey: @"com.apple.MyAppName.UbiquityIdentityToken"]; |
} |
If the user enables airplane mode on a device, iCloud itself becomes inaccessible but the current iCloud account remains signed in. Even in airplane mode, the ubiquityIdentityToken
property contains the token for the current iCloud account.
If a user signs out of iCloud, such as by turning off Documents & Data in Settings, the value of the ubiquityIdentityToken
property changes to nil
. To detect when a user signs in or out of iCloud, register as an observer of the NSUbiquityIdentityDidChangeNotification
notification, using code such as that shown in Listing 1-3. Execute this code at launch time or at any point before actively using iCloud.
Listing 1-3 Registering for iCloud availability change notifications
[[NSNotificationCenter defaultCenter] |
addObserver: self |
selector: @selector (iCloudAccountAvailabilityChanged:) |
name: NSUbiquityIdentityDidChangeNotification |
object: nil]; |
After obtaining and archiving the iCloud token and registering for the iCloud notification, your app is ready to invite the user to use iCloud. If this is the user’s first launch of your app with an iCloud account available, display an alert by using code like that shown in Listing 1-4. Save the user’s choice to the user defaults database and use that value to initialize the firstLaunchWithiCloudAvailable
variable during subsequent launches. This code in the listing is simplified to focus on the sort of language you would display. In an app you intend to provide to customers, you would internationalize this code by using the NSLocalizedString
(or similar) macro, rather than using strings directly.
Listing 1-4 Inviting the user to use iCloud
if (currentiCloudToken && firstLaunchWithiCloudAvailable) { |
UIAlertView *alert = [[UIAlertView alloc] |
initWithTitle: @"Choose Storage Option" |
message: @"Should documents be stored in iCloud and |
available on all your devices?" |
delegate: self |
cancelButtonTitle: @"Local Only" |
otherButtonTitles: @"Use iCloud", nil]; |
[alert show]; |
} |
Although the ubiquityIdentityToken
property lets you know if a user is signed in to an iCloud account, it does not prepare iCloud for use by your app. In iOS, apps that use document storage must call the URLForUbiquityContainerIdentifier:
method of the NSFileManager
method for each supported iCloud container. Always call the URLForUbiquityContainerIdentifier:
method from a background thread—not from your app’s main thread. This method depends on local and remote services and, for this reason, does not always return immediately. Listing 1-5 shows an example of how to initialize your app’s default container on a background thread.
Listing 1-5 Obtaining the URL to your iCloud container
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { |
myContainer = [[NSFileManager defaultManager] |
URLForUbiquityContainerIdentifier: nil]; |
if (myContainer != nil) { |
// Your app can write to the iCloud container |
dispatch_async (dispatch_get_main_queue (), ^(void) { |
// On the main thread, update UI and state as appropriate |
}); |
} |
}); |
This example assumes that you have previously defined myContainer
as an instance variable of type NSURL
prior to executing this code.
Handle Changes in iCloud Availability
There are times when iCloud may not be available to your app, such as when the user disables the Documents & Data feature or signs out of iCloud. If the current iCloud account becomes unavailable while your app is running or in the background, your app must remove references to user-specific iCloud files and data and to reset or refresh user interface elements that show that data, as depicted in Figure 1-4.
To handle changes in iCloud availability, register to receive the NSUbiquityIdentityDidChangeNotification
notification. The handler method you register must do the following:
Retrieve the new value from the
ubiquityIdentityToken
property.Compare the new value to the previous value, to find out if the user signed out of the account or signed in to a different account.
If the values are different, the previously used account is now unavailable. Discard any changes, empty your iCloud-related data caches, and refresh all iCloud-related user interface elements.
If you want to allow users to continue creating content with iCloud unavailable, store that content in your app’s local data container. When the account is again available, move the new content to iCloud. It’s usually best to do this without notifying the user or requiring any interaction from the user.
Choose the Proper iCloud Storage API
Apple provides the following iCloud storage APIs, each with a different purpose:
Key-value storage is for discrete values such as preferences, settings, and simple app state.
Use iCloud key-value storage for small amounts of data: stocks or weather information, locations, bookmarks, a recent documents list, settings and preferences, and simple game state. Every app submitted to the App Store or Mac App Store should take advantage of key-value storage.
iCloud document storage is for user-visible file-based content, Core Data storage, or for other complex file-based content.
Use iCloud document storage for apps that work with file-based content, such as word-processing documents, diagrams or drawings, or games that need to keep track of complex game state.
CloudKit storage is for storing data as individual records in a private or public database accessible by all your app’s users.
Use CloudKit in situations where key-value storage and document storage are insufficient for your needs. To learn more about CloudKit, read Designing for CloudKit.
Many apps benefit from using key-value storage with other types of storage. For example, say you develop a task management app that lets users apply keywords for organizing their tasks. You could employ iCloud document storage to store the task information and use key-value storage to save the user-entered keywords.
If your app uses Core Data, either for documents or for a shoebox-style app like iPhoto, use iCloud document storage. To learn how to adopt iCloud in your Core Data app, see Designing for Core Data in iCloud.
If your app needs to store passwords, do not use iCloud storage APIs for that. The correct API for storing and managing passwords is Keychain Services, as described in Keychain Services Reference.
Use Table 1-1 to help you pick the iCloud storage scheme that is right for each of your app’s needs.
Element | iCloud document storage | Key-value storage | CloudKit |
---|---|---|---|
Purpose | User documents, complex private app data, and files containing complex app- or user-generated data. | Preferences and configuration data that can be expressed using simple data types. | Complex private app data and files, structured data, user-generated data, data that you want to share among users. |
Entitlement keys |
|
|
|
Data format | Files and file packages | Property-list data types only (numbers, strings, dates, and so on) | Records, represented as collections of key-value pairs where values are a subset of property-list data types, files, or references to other records. |
Capacity | Limited only by the space available in the user’s iCloud account. | Limited to a total of 1 MB per app, with a per-key limit of 1 MB. | Limited only by the space available in the user’s iCloud account (private database) and the app’s allotted storage quota (public database). |
Detecting availability | Call the | Key-value storage is effectively always available. If a device is not attached to an account, changes created on the device are pushed to iCloud as soon as the device is attached to the account. | The public database is always available. The private database is available only when the value in the |
Locating data | Use an | Use the shared | Use a |
Managing data | Use the | Use the default | Use the classes of the CloudKit framework to manage data. |
Resolving conflicts | Documents, as file presenters, automatically handle conflicts; in OS X, | The most recent value set for a key wins and is pushed to all devices attached to the same iCloud account. The timestamps provided by each device are used to compare modification times. | When saving records, assign an appropriate value to the |
Data transfer | In iOS, perform a coordinated read to get a local copy of an iCloud file; data is then automatically pushed to iCloud in response to local file system changes. In OS X, iCloud files are always automatically pushed to iCloud in response to local file system changes. | Automatic, in response to local file system changes. | Use operation objects (or the convenience methods of the |
Metadata transfer | Automatic, in response to local file system changes. | Not applicable (key-value storage doesn’t use metadata). | Not applicable |
User interface | None provided by iOS. Your app is responsible for displaying information about iCloud data, if desired; do so seamlessly and with minimal changes to your app’s pre-iCloud UI. In OS X, | Not applicable. Users don’t need to know that key-value data is stored in iCloud. | Your app is responsible for providing the interface needed to display data obtained from records. |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-12-17