I'm currently working with an NSPersistentDocument
subclass that uses NSOperation
to import data in the background. As per the documentation, I'm observing the NSManagedObjectContextDidSaveNotification
after saving in the background task and propagating the notification to the NSManagedObjectContext
in the main thread using -mergeChangesFromContextDidSaveNotification:
.
Everything works fine, but it presents a weird workflow for a user who's importing data into a new document. They need to save an empty document before doing the import (otherwise the -save:
fails because the document hasn't configured a URL for the NSPersistentStoreCoordinator
.) I don't see a way around this other than some kind of "new document setup" wizard that ensures -writeToURL:ofType:forSaveOperation:originalContentsURL:error:
gets called before the import.
Also, it appears that an import task in the background precludes the use of an NSUndoManager
on the main thread. (I'm assuming that it's unsafe to share the managed object context's undo manager across the threads.) From a user's point-of-view, there's no way to undo all the new objects created during the import.
I've read both the Core Data Programming Guide and Marcus Zarra's book, but I'm still new to this aspect of the framework. Hopefully, I've overlooked something: if not, I'll adapt my app to these restrictions (the benefits of Core Data far outweigh these user interface limitations.)
Thanks for your time!
--
Based on Peter Hosey's suggestion below, I added the following code to create a temporary persistent store prior to the import:
NSPersistentStoreCoordinator *persistentStoreCoordinator = [self.managedObjectContext persistentStoreCoordinator];
if ([[persistentStoreCoordinator persistentStores] count] == 0) {
// create an in-memory store to use temporarily
NSError *error;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error];
if (! persistentStore) {
NSLog(@"error = %@", error); // TODO: better error handling
}
}
Then, after a file is selected in the save panel, the temporary persistent store is migrated to a SQLite store at the selected URL:
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error
{
NSPersistentStoreCoordinator *persistentStoreCoordinator = [self.managedObjectContext persistentStoreCoordinator];
for (NSPersistentStore *persistentStore in [persistentStoreCoordinator persistentStores]) {
if (persistentStore.type == NSInMemoryStoreType) {
// migrate the in-memory store to a SQLite store
NSError *error;
NSPersistentStore *newPersistentStore = [persistentStoreCoordinator migratePersistentStore:persistentStore toURL:absoluteURL options:nil withType:NSSQLiteStoreType error:&error];
if (! newPersistentStore) {
NSLog(@"error = %@", error); // TODO: better error handling
}
}
}
return [super writeToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation originalContentsURL:absoluteOriginalContentsURL error:error];
}
I'm nobody's Core Data expert, but from what I can tell from the docs, you'll want to start with an in-memory store until the user (in their own time) saves the document. Then, send the coordinator a
migratePersistentStore:toURL:options:withType:error:
message to change over from the in-memory store to the new truly-persistent store. See that document for some essential details (particularly regarding the fate of the store you migrate).