Using Core Data with iCloud Release Notes
- Using Core Data with iCloud
- Use the SQLite Store for Large Datasets and Fine-Grained Change Notification
- Use the Atomic Store for Small Data Sets When Changes are “Wholesale”
- Guidance for Library-style Applications
- Guidance for Document-based Applications
- Schema Migration
- Limitations on Functionality
Using Core Data with iCloud
When you create an application that uses Core Data and iCloud, there are two orthogonal choices you need to make:
What sort of application do you want to create?
What store type do you want to use?
There are broadly speaking two types of Core Data-based application that integrate with iCloud:
Library-style applications, where the application usually has a single persistent store, and data from the store is used throughout the application.
Examples of this style of application are Music and Photos.
Document-based applications, where different documents may be opened at different times during the lifetime of the application.
Examples of this style of application are Keynote and Numbers.
If you’re already using Core Data in iOS, then you probably have a library-style application. For this, you are encouraged to use the SQLite store with Core Data’s iCloud integration.
UIKit provides support for document-based applications that use Core Data through the
UIManagedDocument class—a concrete subclass of the
UIDocument class that represents a document. For library-style applications, you generally set up the Core Data stack as you would if you were not using iCloud. For both library-style and document-based applications, however, you must appropriately configure the persistent store(s).
How you configure the store depends on what type of store you want to use. There are two store types to choose from:
Atomic (the binary store type) or a custom atomic store derived from
Use the SQLite Store for Large Datasets and Fine-Grained Change Notification
You usually use the SQLite store, because it offers efficient per-record change propagation and corresponding change notifications.
In iOS 5, Core Data includes enhancements to support efficient integration of the SQLite store with iCloud. The principal new feature is that each change you commit to a persistent store is recorded in discrete transaction log files that are stored in iCloud, rather than keeping the persistent store file in the cloud directly. This means that change propagation through iCloud is efficient (the alternative would be to push the whole SQLite file for each change). There are a few consequences to this, though:
You must tell Core Data where to store the transaction logs associated with changes to the store.
You do this by providing values for the
NSPersistentStoreUbiquitousContentURLKeykeys when configuring a store. The content name is used to identify the store across different devices so that transactions are synced consistently with the potentially many instances of that persistent store file across all the devices. For this reason, you must make sure you provide a unique name for each store—for example, a UUID.
When you remove a store from iCloud (either to make local to the device, or to delete it completely), you must delete the corresponding transaction logs directory.
You do this as a coordinated operation using
NSFileManager. If you specified a custom and unique
NSPersistentStoreUbiquitousContentURLKey, simply deleting that directory will remove all transaction log data associated with the store; if your
NSPersistentStoreUbiquitousContentURLKeyis shared, then you should only remove the
You must not store the SQLite file itself in a location where changes to it will be propagated by iCloud.
This includes the entire parent directory that contains the SQLite store file.
This is discussed later in the context of application types.
Use the Atomic Store for Small Data Sets When Changes are “Wholesale”
With an atomic store, changes are not recorded in separate transaction log files. There are therefore potential performance implications because iCloud must propagate changes to the whole store file. You should usually only use an atomic store for small data sets. Moreover, instead of receiving notifications of changes to individual records and having the ability to merge these changes, you are simply told that the store has changed via the standard
NSFilePresenter and (in document-based applications)
UIDocument conflict resolution methods. You must then accept or reject the changes wholesale.
If you use an atomic store, you must put the persistent store file in a location where changes to it will be propagated by iCloud. All saves and reads must be performed in a file coordination block.
Guidance for Library-style Applications
In a library style application, you usually have a single Core Data stack with a single persistent store coordinator and a single persistent store.
If you use the SQLite store, you:
Provide values for the
NSPersistentStoreUbiquitousContentNameKeyand optionally the
NSPersistentStoreUbiquitousContentURLKeykeys in the options dictionary when you add the store with
addPersistentStoreWithType:configuration:URL:options:error:. You can choose whatever name you want; the content URL, however, must identify a directory in the ubiquity container.
You must put the store itself in the local application sandbox, or inside a directory with a
.nosyncsuffix in the ubiquity container.
You can observe
NSPersistentStoreDidImportUbiquitousContentChangesNotification notifications posted by the persistent store; this allows you to react to changes on a per-record basis.
If you use the atomic store:
You put the store directly in the ubiquity container.
To handle changes propagated from iCloud, you implement suitable
Guidance for Document-based Applications
In a document-based application, you represent the document using an instance of
UIManagedDocument. The document instance automatically aggregates managed object models it finds in the application’s main bundle (subclasses can override the
managedObjectModel property to customize this behavior) and manages the Core Data stack for you.
A managed document uses two managed object contexts. The first context is created on a private queue so that changes can be saved asynchronously. The second is a child of the first, created on the main queue. The child context is the context that the document returns from the
managedObjectContext method. This has a couple of implications:
If you want to register for change notifications, make sure you register for those posted by the appropriate context.
To save changes, you should usually rely on
UIDocument’s autosaving logic. If you want to force a save at a given point, however, you should generally not invoke the
save:method on the child context—this will simply propagate changes to the parent context and not actually commit them to the store. Instead, you should use
saveToURL:forSaveOperation:completionHandler:method, which commits changes from the child to the parent, then from the parent to the store.
An instance of
UIManagedDocument uses a SQLite store by default; you can specify a different store type by creating a subclass of an instance
UIManagedDocument and overriding
persistentStoreTypeForFileType: to return the appropriate type.
Documents that use a SQLite store
If you use a SQLite store, you need to provide a unique identifier for the store. You set the identifier using the
NSPersistentStoreUbiquitousContentNameKey in the
setPersistentStoreOptions: dictionary for the managed document. You should also provide a ubiquitous content URL using the
NSPersistentStoreUbiquitousContentURLKey. (The default URL is based on the bundle identifier. If you provide a custom URL for the transaction logs, you can share documents across applications with different identifiers. Typically you specify a directory called
TransactionLogs in the ubiquity container.) So that you can retrieve it later if necessary (for example, when deleting a document),
UIManagedDocument saves the store’s ubiquitous content name in a property list file named
DocumentMetadata.plist in the document package.
To summarize details about the document structure and location:
The document package contains a property list file,
The root element in the
DocumentMetadata.plistproperty list is a dictionary. One of the dictionary keys is
NSPersistentStoreUbiquitousContentNameKey. The corresponding value is the name of the directory that contains the transaction logs for the document.
Transaction logs for a document are stored in a directory: <Value of NSPersistentStoreUbiquitousContentURLKey>/<Value of NSPersistentStoreUbiquitousContentNameKey>.
To ensure that the persistent store itself is not synced by iCloud: when you set a value for the
UIManagedDocument puts the persistent store in a
.nosync directory inside the document package. If you make use of additional content (using the
writeAdditionalContent:toURL:originalContentsURL:error: method), you must make sure that the document directory is not a package. Typically you give the document directory an extension that is not recognized as a document extension.
To respond to changes propagated from iCloud, you observe
NSPersistentStoreDidImportUbiquitousContentChangesNotification notifications posted by the document's persistent store; this allows you to respond to changes on a per-record basis.
To find documents, you use a metadata query as you would for other ubiquitous content. The query returns the
DocumentMetadata.plist file inside the document packages.
To open a document that uses the SQLite store, you must retrieve the value of the
NSPersistentStoreUbiquitousContentNameKey from the
DocumentMetadata.plist file and set that content name key in the
persistentStoreOptions dictionary before you open the document with
To delete a document that uses the SQLite store, you must also delete the transaction logs directory for the document. You do this as a coordinated operation using
Core Data applications will not sync with other versions using different schemas. With the SQLite store, Core Data only supports lightweight migration on iCloud. When using light weight migration, different versions of the application will not sync with each other. When an older version upgrades, it will catch up and previously too-new versions will merge in the older version’s changes.
Because atomic stores overwrite the entire file, if you update the schema the store will not be able to be read by clients using a previous schema.
Limitations on Functionality
When used with iCloud, there are several limitations with the SQLite store at this time:
Ordered relationships are not supported.
You cannot simply move or copy an existing database file to iCloud. Instead, you should use
migratePersistentStore:toURL:options:withType:error:method to migrate the database to the required location.
Schema migration using mapping models is not supported (lightweight migration is supported).