I have what I assume is a fairly standard setup, with one scratchpad MOC which is never saved (containing a bunch of objects downloaded from the web) and another permanent MOC which persists objects. When the user selects an object from scratchMOC to add to her library, I want to either 1) remove the object from scratchMOC and insert into permanentMOC, or 2) copy the object into permanentMOC. The Core Data FAQ says I can copy an object like this:
NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];
(In this case, context2 would be permanentMOC.) However, when I do this, the copied object is faulted; the data is initially unresolved. When it does get resolved, later, all of the values are nil; none of the data (attributes or relationships) from the original managedObject are actually copied or referenced. Therefore I can't see any difference between using this objectWithID: method and just inserting an entirely new object into permanentMOC using insertNewObjectForEntityForName:.
I realize I can create a new object in permanentMOC and manually copy each key-value pair from the old object, but I'm not very happy with that solution. (I have a number of different managed objects for which I have this problem, so I don't want to have to write and update copy: methods for all of them as I continue developing.) Is there a better way?
You need to make sure that you're saving the context that managedObject lives in. In order to fetch the same object in a different context, it needs to be present in the persistent store.
An object space to manipulate and track changes to managed objects.
To save an object with Core Data, you can simply create a new instance of the NSManagedObject subclass and save the managed context. In the code above, we've created a new Person instance and saved it locally using Core Data.
From your perspective, the context is the central object in the Core Data stack. It's the object you use to create and fetch managed objects, and to manage undo and redo operations. Within a given context, there is at most one managed object to represent any given record in a persistent store.
First, having more than one NSManagedObjectContext
on a single thread is not a standard configuration. 99% of the time you only need one context and that will solve this situation for you.
Why do you feel you need more than one NSManagedObjectContext
?
That is actually one of the few use cases that I have seen where that makes sense. To do this, you need to do a recursive copy of the object from one context to the other. The workflow would be as follows:
-dictionaryWithValuesForKeys
and -[NSEntityDescription attributesByName]
to do this.-setValuesForKeysWithDictionary
)-[NSEntityDescription relationshipsByName]
As mentioned by another, you can download the sample code from my book from The Pragmatic Programmers Core Data Book and see one solution to this problem. Of course in the book I discuss it more in depth :)
The documentation is misleading and incomplete. The objectID methods do not themselves copy objects they simply guarantee that you've gotten the specific object you wanted.
The context2
in the example is in fact the source context and not the destination. You're getting a nil because the destination context has no object with that ID.
Copying managed objects is fairly involved owing to the complexity of the object graph and the way that the context manage the graph. You do have to recreate the copied object in detail in the new context.
Here is some example code that I snipped from the example code for The Pragmatic Programmer's Core Data: Apple's API for Persisting Data on Mac OS X. (You might be able to download the entire project code without buying the book at the Pragmatic site.) It should provide you with a rough idea of how to go about copying an object between context.
You can create some base code that copies objects but the particulars of each object graph's relationships usually mean that you have to customize for each data model.
Had the same problem myself and found this article on created disconnected entities that could later be added to the context: http://locassa.com/temporary-storage-in-apples-coredata/
The idea is that you have an NSManagedObject because you're going to be storing objects in the database. My hurdle was that many of these objects are downloaded via HTTP API and I want to throw out most of them at the end of the session. Think of a stream of user posts, and I only want to save the ones that were favorited or saved as a draft.
I create all of my posts using
+ (id)newPost {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
return post;
}
and then the posts are inserted to the local managed object context when they are favorited
+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
// Set the post's isFavorite flag
post.isFavorite = [NSNumber numberWithBool:isFavorite];
// If the post is being favorited and not yet in the local database, add it
NSError *error;
if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
[self.managedObjectContext insertObject:post];
}
// Else if the post is being un-favorited and is in the local database, delete it
else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
[self.managedObjectContext deleteObject:post];
}
// If there was an error, output and return NO to indicate a failure
if (error) {
NSLog(@"error: %@", error);
return NO;
}
return YES;
}
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With