Using Persistent Stores

This article describes how you create a persistent store, and how you can migrate a store from one type to another, and manage store metadata. For more about persistent store types, the differences between them, and how you can configure aspects of their behavior, see “Persistent Store Features.”

Creating and Accessing a Store

Access to stores is mediated by an instance of NSPersistentStoreCoordinator. You should not need to directly access a file containing a store. From a persistent store coordinator, you can retrieve an object that represents a particular store on disk. Core Data provides an NSPersistentStore class to represent persistent stores.

To create a store, you use a persistent store coordinator. You must specify the type of the store to be created, optionally a configuration of managed object model associated with the coordinator, and its location if it is not an in-memory store. The following code fragment illustrates how you can create a read-only XML store:

NSManagedObjectContext *moc = <#Get a context#>;
NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
NSError *error = nil;
NSDictionary *options =
    [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:1]
                    forKey:NSReadOnlyPersistentStoreOption];
 
NSPersistentStore *roStore =
    [psc addPersistentStoreWithType:NSXMLStoreType
                    configuration:nil URL:url
                    options:options error:&error];

To retrieve a store object from a coordinator, you use the method persistentStoreForURL:. You can use a store to restrict a fetch request to a specific store, as shown in the following code fragment:

NSPersistentStoreCoordinator *psc = <#Get a coordinator#>;
NSURL *myURL = <#A URL identifying a store#>;
NSPersistentStore *myStore = [psc persistentStoreForURL:myURL];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setAffectedStores:[NSArray arrayWithObject:myStore]];

Changing a Store’s Type and Location

You can migrate a store from one type or location to another (for example, for a Save As operation) using the NSPersistentStoreCoordinator method migratePersistentStore:toURL:options:withType:error:. After invocation of this method, the original store is removed from the coordinator, thus store is therefore no longer a useful reference. The method is illustrated in the following code fragment, which shows how you can migrate a store from one location to another. If the old store type is XML, then the example also converts the store to SQLite

NSPersistentStoreCoordinator *psc = [aManagedObjectContext persistentStoreCoordinator];
NSURL *oldURL = <#URL identifying the location of the current store#>;
NSURL *newURL = <#URL identifying the location of the new store#>;
NSError *error = nil;
NSPersistentStore *xmlStore = [psc persistentStoreForURL:oldURL];
NSPersistentStore *sqLiteStore = [psc migratePersistentStore:xmlStore
    toURL:newURL
    options:nil
    withType:NSSQLiteStoreType
    error:&error];

Core Data follows the procedure below to migrate a store:

  1. Create a temporary persistence stack

  2. Mount the old and new stores

  3. Load all objects from the old store

  4. Migrate the objects to the new store

    The objects are given temporary IDs, then assigned to the new store. The new store then saves the newly assigned objects (committing them to the external repository).

    Core Data then informs other stacks that the object IDs have changed (from the old to the new stores), which is how things "keep running" after a migration.

  5. Unmount old store

  6. Return the new store

An error can occur if:

In the latter two cases, you get the same errors you would if you called addPersistentStore: or removePersistentStore: directly. if an error occurs when adding or removing the store, you should treat this as an exception since the persistence stack is likely to be in an inconsistent state.

If something fails during the migration itself, instead of an error you get an exception. In these cases, Core Data unwinds cleanly and there should be no repair work necessary. You can examine the exception description to determine what went wrong—there is a wide variety of possible errors, ranging from "disk is full" and "permissions problems" to "The SQLite store became corrupted" and "Core Data does not support cross store relationships".

Associate Metadata With a Store to Provide Additional Information and Support Spotlight Indexing

A store’s metadata provides additional information about the store that is not directly associated with any of the entities in the store.

The metadata is represented by a dictionary. Core Data automatically sets key-value pairs to indicate the store type and its UUID. You can add additional keys that may be either custom for your application, or one of the standard set of keys to support Spotlight indexing (if you also write a suitable importer) such as kMDItemKeywords.

You should be careful about what information you put into metadata. First, Spotlight imposes a limit to the size of metadata. Second, replicating an entire document in metadata is probably not useful. Note, though, that it is possible to create a URL to identify a particular object in a store (using URIRepresentation)—the URL may be useful to include as metadata.

Getting the Metadata

There are two ways to get the metadata for a store:

  1. Given an instance of a persistent store, you can get its metadata using the NSPersistentStoreCoordinator instance method metadataForPersistentStore:.

  2. You can retrieve metadata from a store without the overhead of creating a persistence stack using the NSPersistentStoreCoordinator class method, metadataForPersistentStoreOfType:URL:error:.

There is an important difference between these approaches. The instance method, metadataForPersistentStore:, returns the metadata as it currently is in your program, including any changes that may have been made since the store was last saved. The class method, metadataForPersistentStoreOfType:URL:error:, returns the metadata as it is currently represented in the store itself. If there are pending changes to the store, the returned value may therefore be out of sync.

Setting the Metadata

There are two ways you can set the metadata for a store:

  1. Given an instance of a persistent store, you can set its metadata using the NSPersistentStoreCoordinator instance method, setMetadata:forPersistentStore:.

  2. You can set the metadata without the overhead of creating a persistence stack using the NSPersistentStoreCoordinator class method, setMetadata:forPersistentStoreOfType:URL:error:.

There is again an important difference between these approaches. If you use setMetadata:forPersistentStore:, you must save the store (through a managed object context) before the new metadata is saved. If you use setMetadata:forPersistentStoreOfType:URL:error:, however, the metadata is updated immediately (and the last-modified date of the file changed). This difference has particular implications if you use NSPersistentDocument on OS X. If you update the metadata using setMetadata:forPersistentStoreOfType:URL:error: while you are actively working on the persistent store (that is, while there are unsaved changes), then when you save the document you will see a warning, “This document's file has been changed by another application since you opened or saved it.” To avoid this, you should instead use setMetadata:forPersistentStore:. To find the document’s persistent store, you typically ask the persistent store coordinator for its persistent stores (persistentStores) and use the first item in the returned array.

Because Core Data manages the values for NSStoreType and NSStoreUUID, you should make a mutable copy of any existing metadata before setting your own keys and values, as illustrated in the following code fragment.

NSError *error;
NSURL *storeURL = <#URL identifying the location of the store#>;
 
NSDictionary *metadata =
    [NSPersistentStoreCoordinator metadataForPersistentStoreWithURL:storeURL error:&error]
if (metadata == nil) {
    /* Deal with the error. */
}
else {
    NSMutableDictionary *newMetadata = [metadata mutableCopy];
    newMetadata[(NSString *)kMDItemKeywords] = @[ @"MyKeyWord"] ];
    // Set additional key-value pairs as appropriate.
    [NSPersistentStore setMetadata:newMetadata
                       forPersistentStoreWithURL:storeURL
                       error:&error];
}