Wednesday, October 19, 2011

iCloud Storage APIs

The iCloud storage APIs let your application write user documents and data to a central location and access those items from all of a user’s computers and iOS devices. Making a user’s documents ubiquitous using iCloud means that a user can view or edit those documents from any device without having to sync or transfer files explicitly. Storing documents in a user’s iCloud account also provides a layer of security for that user. Even if a user loses a device, the documents on that device are not lost if they are in iCloud storage. There are two ways that applications can take advantage of iCloud storage, each of which has a different intended usage: iCloud document storage—Use this feature to store user documents and data in the user’s iCloud account. iCloud key-value data storage—Use this feature to share small amounts of data among instances of your application. Most applications will use iCloud document storage to share documents from a user’s iCloud account. This is the feature that users think of when they think of iCloud storage. A user cares about whether documents are shared across devices and can see and manage those documents from a given device. In contrast, the iCloud key-value data store is not something a user would see. It is a way for your application to share very small amounts of data (tens of kilobytes) with other instances of itself. Applications can use this feature to store important state information. A magazine application might save the issue and page that the user read last, while a stocks application might store the stock symbols the user is tracking. The summary for using the iCloud storage APIs in your application is as follows: Use the iOS Provisioning Portal to enable iCloud storage for your iOS application.Enabling this feature requires that you have an updated provisioning profile on your development systems. Xcode 4 handles this step for you automatically. Add an Entitlements file to your application and use it to configure the iCloud features your application uses Implement iCloud document storage as follows: Use the UIDocument class to read and write the contents of your user documents; alternatively, incorporate support for file presenters and file coordinators into the custom classes that you use to manage user documents. The iCloud service uses the file coordination mechanism to notify your application when a document changes. (The UIDocument class implements support for file presenters and file coordinators for you and is recommended.) Use the NSFileManager class to move the appropriate documents to the user’s iCloud storage. It is recommended that you give users the option to move documents to iCloud Use the NSMetadataQuery interfaces to search for documents in a user’s iCloud storage; Be prepared to handle version conflicts that might arise when two devices try to update a document at the same time; To implement iCloud key-value data storage, use the NSUbiquitousKeyValueStore class to read and write keys and values. The sections that follow provide more details about how to implement different aspects of iCloud storage for your application. For additional information about using specific classes and interfaces, see the corresponding reference documentation. Storing and Using Documents in iCloud Documents in iCloud provide the central location from which updates can be delivered to a user’s computers and iOS devices. All documents must be created in your application’s sandbox initially and moved to a user’s iCloud account later. A document targeted for iCloud storage is not moved to iCloud immediately, though. First, it is moved out of your application sandbox and into a local system-managed directory where it can be monitored by the iCloud service. After that transfer, the file is transfered to iCloud and to the user’s other devices as soon as possible. While in iCloud storage, changes made on one device are stored locally and then pushed to iCloud using a local daemon, as shown in Figure 1. To prevent large numbers of conflicting changes from occurring at the same time, applications are expected to use file coordinator objects to perform all changes. File coordinators mediate changes between your application and the daemon that facilitates the transfer of the document to and from iCloud. In this way, the file coordinator acts like a locking mechanism for the document, preventing your application and the daemon from modifying the document simultaneously. Figure 1 Pushing document changes to iCloud From an implementation perspective, the easiest way to manage documents in iCloud is to use the UIDocument class. This class does most of the heavy lifting required to read and write files that are stored in iCloud. Specifically, the UIDocument class handles the creation and use of file coordinators to modify the document. This class also seamlessly integrates document changes coming from other devices. The class even helps handle the potential conflicts that can arise when two devices do manage to update the same file in conflicting ways. You are not required to use the UIDocument class to manage your application’s documents, but using it requires less effort on your part. Important: Applications that store documents to iCloud must specify one or more containers in which to store those documents. You specify which containers your application supports using the com.apple.developer.ubiquity-container-identifiers entitlement. The sections that follow discuss the basic mechanics of how you move documents to iCloud and manage them (with and without the help of theUIDocument class). Moving a Document to iCloud Storage To move a document to iCloud storage: Create and save the file locally in your application sandbox. If you are not using the UIDocument class to manage your file, create a file presenter to be responsible for it. Create an NSURL object that specifies the destination of the file in a user’s iCloud storage.You must put files in one of the container directories associated with your application. Call the URLForUbiquityContainerIdentifier:method of NSFileManager to get the root URL for the directory, and then append any additional directory and filenames to that URL. (Applications may put documents in any container directory for which they have the appropriate entitlement. Call the setUbiquitous:itemAtURL:destinationURL:error: method of NSFileManager to move the file to the specified destination in iCloud. When moving documents to iCloud, you can create additional subdirectories inside the container directory to manage your files. It is strongly recommended that you create a Documents subdirectory and use that directory for storing user documents. In iCloud, the contents of theDocuments directory are made visible to the user so that individual documents can be deleted. Everything outside of the Documents directory is grouped together and treated as a single entity that a user can keep or delete. You create subdirectories in a user’s iCloud storage using the methods of the NSFileManager class just as you would any directory. After you move a document to iCloud, it is not necessary to save a URL to the document’s location persistently. If you manage a document using a UIDocument object, that object automatically updates its local data structures with the document’s new URL. However, it does not save that URL to disk, and neither should your custom file presenters. Instead, because documents can move while in a user’s iCloud storage, you should use an NSMetadataQuery object to search for documents. Searching guarantees that your application has the correct URL for accessing the document. Searching for Documents in iCloud To locate documents in iCloud storage, your application must search using an NSMetadataQuery object. Searching is a guaranteed way to locate documents both in a user’s iCloud storage and in your application sandbox. You should always use query objects instead of saving URLs persistently because the user can delete files from iCloud storage when your application is not running. Using a query to search is the only way to ensure an accurate list of documents. In iOS 5.0, the NSMetadataQuery class supports the following search scopes for your documents: Use the NSMetadataQueryUbiquitousDocumentsScope constant to search for documents in iCloud that reside somewhere inside aDocuments directory. (For any given container directory, put documents that the user is allowed to access inside a Documents subdirectory.) Use the NSMetadataQueryUbiquitousDataScope constant to search for documents in iCloud that reside anywhere other than in aDocuments directory. (For any given container directory, use this scope to store user-related data files that your application needs to share but that are not files you want the user to manipulate directly.) Use the NSMetadataQueryLocalDocumentsScope constant to search the Documents directory in your application’s sandbox. To use a metadata query object to search for documents, create a new NSMetadataQuery object and do the following: Set the search scope of the query to an appropriate value (or values). Add a predicate to narrow the search results. To search for all files, specify a predicate with the format NSMetadataItemFSNameKey == *. Register for the query notifications and configure any other query parameters you care about, such as sort descriptors, notification intervals, and so on.The NSMetadataQuery object uses notifications to deliver query results. At a minimum, you should register for theNSMetadataQueryDidUpdateNotification notification, but you might want to register for others in order to detect the beginning and end of the results-gathering process. Call the startQuery method of the query object. Run the current run loop so that the query object can generate the results.If you started the query on your application’s main thread, simply return and let the main thread continue processing events. If you started the query on a secondary thread, you must configure and execute a run loop explicitly. Process the results in your notification-handler methods.When processing results, always disable updates first. Doing so prevents the query object from modifying the result list while you are using it. When you are done processing the results, reenable updates again to allow new updates to arrive. When you are ready to stop the search, call the stopQuery method of the query object. Working with Documents in iCloud When your application needs to read or write a document in iCloud, it must do so in a coordinated manner. Your application might not be the only application trying to manipulate the local file at any given moment. The daemon that transfers the document to and from iCloud also needs to manipulate it periodically. To prevent your application from interfering with the daemon (and vice versa), iOS provides a coordinated locking mechanism that works with the files and directories you target for iCloud storage. At the heart of the iCloud locking mechanism are file coordinators and file presenters. Whenever you need to read and write a file, you do so using a file coordinator, which is an instance of the NSFileCoordinator class. The job of a file coordinator is to coordinate the reads and writes performed by your application and the sync daemon on the same document. For example, your application and the daemon may both read the document at the same time but only one may write to the file at any single time. Also, if one process is reading the document, the other process is prevented from writing to the document until the reader is finished. In addition to coordinating operations, file coordinators also work with file presenters to notify applications when changes are about to occur. Afile presenter is any object that conforms to the NSFilePresenter protocol and takes responsibility for managing a specific file (or directory of files) in an application. The job of a file presenter is to protect the integrity of its own data structures. It does this by listening for messages from other file coordinators and using those messages to update its internal data structures. In most cases, a file presenter may not have to do anything. However, if a file coordinator declares that it is about to move a file to a new URL, the file presenter would need to replace its old URL with the new one provided to it by the file coordinator. The UIDocument class is an example of a file presenter that tracks changes to its underlying file or file package. Here is a checklist of the things your application must do to work with documents in iCloud: Manage each document in iCloud using a file presenter. The recommended way to do this is to use the UIDocument class, but you may define custom file presenters if you prefer. You can use a single file presenter to manage a file package or a directory of files. After creating a file presenter, register it by calling the addFilePresenter: class method of NSFileCoordinator. Registration is essential. The system can notify only registered presenter objects. Before deleting a file presenter, unregister it by calling the removeFilePresenter: method of NSFileCoordinator. All file-related operations must be performed through a file coordinator object. To read or write a document, or move or delete it, follow these steps: Create an instance of the NSFileCoordinator class and initialize it with the file presenter object that is about to perform the file operation. Use the methods of the NSFileCoordinatorobject to read or write the file: To read all or part of a single file, use the coordinateReadingItemAtURL:options:error:byAccessor: method To write to a file or delete it, call the coordinateWritingItemAtURL:options:error:byAccessor: method. To perform a sequence of read or write operations, use thecoordinateReadingItemAtURL:options:writingItemAtURL:options:error:byAccessor: method. To write to multiple files, or to move a file to a new location, use thecoordinateWritingItemAtURL:options:writingItemAtURL:options:error:byAccessor: method. You perform the actual file-related operations in the block that you pass to these methods. You should perform operations as quickly as possible to avoid blocking other applications that might be trying to access the file at the same time. When you are done with the operations, release the file coordinator object. Handling File-Version Conflicts When multiple instances of your application (running on different computers or iOS devices) try to modify the same document in iCloud, a conflict can occur. For example, if the devices are not connected to the network and the user makes changes on both, both devices try to push their changes to iCloud when they are reconnected to the network. At this point, iCloud has two different versions of the same file and has to decide what to do with them. Its solution is to make the most recently modified file the current file and to mark any other versions of the file as conflict versions. Your application is notified of conflict versions through its file presenter objects. It is the job of the file presenter to decide how best to resolve any conflicts that arise. Applications are encouraged to resolve conflicts quietly whenever possible, either by merging the file contents or by discarding the older version if the older data is no longer relevant. However, if discarding or merging the file contents is impractical or might result in data loss, your application might need to prompt the user for help in choosing an appropriate course of action. For example, you might let the user choose which version of the file to keep, or you might offer to save the older version under a new name. Applications should always attempt to resolve conflict versions as soon as possible. When conflict versions exist, all of the versions remain in a user’s iCloud storage (and locally on any computers and iOS devices) until your application resolves them. The current version of the file and any conflict versions are reported to your application using instances of the NSFileVersion class. To resolve conflicts: Get the current file version using the currentVersionOfItemAtURL: class method of NSFileVersion. Get an array of conflict versions using the unresolvedConflictVersionsOfItemAtURL: class method of NSFileVersion. For each conflict version object, perform whatever actions are needed to resolve the conflict. Options include: Merging the conflict versions with the current file automatically, if it is practical to do so. Ignoring the conflict versions, if doing so does not result in any data loss. Prompting the user to select which version (current or conflict) to keep. This should always be your last option. Update the current file as needed. If the current file version remains the winner, you do not need to update the current file. If a conflict version is chosen as the winner, use a coordinated write operation to overwrite the contents of the current file with the contents of the conflict version. If the user chooses to save the conflict version under a different name, create the new file with the contents of the conflict version. Set the resolved property of the conflict version objects to YES.Setting this property to YES causes the conflict version objects (and their corresponding files) to be removed from the user’s iCloud storage. Storing Key-Value Data in iCloud An application can use the NSUbiquitousKeyValueStore class to share small amounts of data with other instances of itself running on other computers and iOS devices. This class provides a similar service as the NSUserDefaults class in that it allows you to save simple data types (numbers, strings, dates, arrays, and so on) persistently and retrieve that data later. The main difference is that theNSUbiquitousKeyValueStore class writes that data to the user’s iCloud storage so that it can be retrieved by the application running on different iOS devices or computers. Important: Applications that use the NSUbiquitousKeyValueStore class must also request the com.apple.developer.ubiquity-kvstore-identifierentitlement. If you configure multiple applications with the same value for this entitlement, all of them share the same key-value data. For more information about configuring iCloud entitlements, see “Requesting Entitlements for iCloud Storage.” The amount of available space in a single key-value store is limited to 64 KB and the data for a single key cannot exceed 4 KB. Thus, you can use this storage to record small details about your application but should not use it to store user documents or other large data archives. Instead, you should use the information in the key-value store to improve the user experience for your application. For example, a magazine application might store the current issue and page number that the user is reading. That way, if the user opens the application on another device, the version of your application on that device can open to the same issue and page as the previous device. The NSUbiquitousKeyValueStore class should not be used as a replacement or supplement for the NSUserDefaults class. An application should always write all of its configuration data to disk using the NSUserDefaults class. It should then write the data it intends to share to the key-value data store using the NSUbiquitousKeyValueStore class. This guarantees that your application always has a valid copy of the data available locally. Requesting Entitlements for iCloud Storage To protect the data each application creates, applications must request specific entitlements at build time in order to use iCloud storage. These entitlements are tied to the application’s provisioning profile and are used to differentiate your application’s documents and data from that of other applications. There are two entitlements an application can request, depending on which iCloud features it uses: To use iCloud document storage, request the com.apple.developer.ubiquity-container-identifiers entitlement. The value of this key is an array of container identifier strings. (The first string in the array must not contain any wildcard characters.) To use the iCloud key-value data store, request the com.apple.developer.ubiquity-kvstore-identifier entitlement. The value of this key is a single container identifier string. A container identifier string must be of the form ., where is the unique ten-character identifier associated with your developer account. The contents of are at your discretion, but it is recommended that you use a reverse-DNS string similar to your application’s bundle identifier. In practice, the string can be anything that makes sense to your development team. You can even use the same container identifier string for multiple applications if you want them to share the same storage space. Applications using iCloud document storage can specify multiple containers for storing documents and data. The value of thecom.apple.developer.ubiquity-container-identifiers key is an array of strings. The first string in this array must be the main container identifier to associate with your application. If your company develops multiple applications, though, you can include additional container identifiers for your other applications. An application that supports multiple containers may read and write the documents and data in all of those containers, and searches always return a merged set of results. Listing 1 shows the XML from an entitlements file that requests the keys for an iPad application. In this example, the iPad application can read and write its own documents (which it stores in the container directory identified using the A1B2C3D4E5.com.example.MyApp.ipad) and it can read and write files from the iPhone version of the same application (which may or may not use a different file format). Listing 1 iCloud keys in the entitlements.plist file com.apple.developer.ubiquity-container-identifiers A1B2C3D4E5.com.example.MyApp.ipad A1B2C3D4E5.com.example.MyApp.iphone com.apple.developer.ubiquity-kvstore-identifier A1B2C3D4E5.com.example.MyApp.ipad Note: The strings you specify in your entitlements property-list file are also the strings you pass to theURLForUbiquityContainerIdentifier:method when requesting the location of a directory in the user’s iCloud storage. Using iCloud Storage Responsibly Applications that take advantage of iCloud storage features should act responsibly when storing data in there. The space available in each user’s account is limited and is shared by all applications. In addition, users can see how much space is consumed by a given application and choose to delete documents and data associated with your application. For these reasons, it is in your application’s interest to be responsible about what files you store. Here are some tips to help you manage documents appropriately: Rather than storing all documents, let a user choose which documents to store in an iCloud account. If a user creates a large number of documents, storing all of those documents in iCloud could overwhelm that user’s available space. Providing a way for a user to designate which documents to store in iCloud gives that user more flexibility in deciding how best to use the available space. Remember that deleting a document removes it from a user’s iCloud account and from all of that user’s computers and devices. Make sure that users are aware of this fact and confirm any delete operations. For your application to refresh the local copy of a document, use theevictUbiquitousItemAtURL:error: method of NSFileManager. When storing documents in iCloud, place them in a Documents directory whenever possible. Documents inside a Documents directory can be deleted individually by the user to free up space. However, everything outside that directory is treated as data and must be deleted all at once. Never store caches or other files that are private to your application in a user’s iCloud storage. A user’s iCloud account should be used only for storing user data and content that cannot be re-created by your application. iCloud Backup Users can now opt to have their applications and application data backed up directly to their iCloud account, making it easier to restore applications to their most recent state. Having data backed up in iCloud makes it easy for a user to reinstall that data to a new or existing iOS device. However, because the amount of space in a user’s iCloud account is limited, applications should be even more selective about where they store files. The placement of files in your application’s home directory determines what gets backed up and what does not. Anything that would be backed up to a user’s computer is also backed up wirelessly to iCloud. Thus, everything in the Documents directory and most (but not all) of your application’s Library directory. To minimize the amount of data stored in the user’s iCloud account, developers are encouraged to put more files in the Library/Caches directory, especially if those files can be easily re-created or obtained in another way. Note:Any documents that your application stores explicitly in iCloud (using the iCloud storage APIs) are not backed up with your application. (Those documents are already stored in the user’s iCloud account and therefore do not need to be backed up separately.) For information about how to store user documents in iCloud, see “Storing and Using Documents in iCloud.”

No comments:

Post a Comment

Followers