Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save CoreData-entities in NSUserDefaults

Imagine an CoreData entity (e.g. named searchEngine).
NSManagedObjectContext manages some "instances" of this entity.
The end-user is going to be able to select his "standard searchEngine" with a NSPopupButton.
The selected object of NSPopupButton should be binded to the NSUserDefaults.
The problem:

1) @try{save}

a) If you try to save the selected "instance" directly to NSUserDefaults there comes something like this:

-[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value ' (entity: searchEngine; id: 0x156f60  ; data: {
    url = "http://google.de/";
    someAttribute = 1;
    name = "google";
})' of class 'searchEngine'.

b) If you try to convert the "instance" to NSData comes this:

-[searchEngine encodeWithCoder:]: unrecognized selector sent to instance 0x1a25b0

So any idea how to get this entities in a plist-compatible data?

2) @try{registerDefaults}

Usually the registerDefaults: method is implemented in + (void)initialize. The problem here is that this method is called before CoreData loads the saved entities from his database. So I can't set a default to a no-existing object, right?

I know, long questions... but: try{[me provide:details]} ;D

like image 289
papr Avatar asked Feb 01 '09 00:02

papr


3 Answers

If you need to store a reference to a specific managed object, use the URI representation of its managed object ID:

NSURL *moIDURL = [[myManagedObject objectID] URIRepresentation];

You can then save the URL to user defaults.

To retrieve the managed object, you use:

NSManagedObjectID *moID = [myPersistentStoreCoordinator managedObjectIDForURIRepresentation:moIDURL];
NSManagedObject *myManagedObject = [myContext objectWithID:moID];

The only caveat is that you must ensure that the original managed object ID is permanent -- this is not a problem if you've already saved the object, alternatively you can use obtainPermanentIDsForObjects:error:.

like image 113
mmalc Avatar answered Mar 04 '23 09:03

mmalc


Here's the cleanest and shortest way to currently do this using the setURL and getURL methods added in 4.0 to avoid extra calls to NSKeyedUnarchiver and NSKeyedArchiver:

Setter:

 + (void)storeSomeObjectId:(NSManagedObjectID *)objectId
 {
     [[NSUserDefaults standardUserDefaults] setURL:[objectId URIRepresentation] 
                                            forKey:@"someObjectIdKey"];
     [[NSUserDefaults standardUserDefaults] synchronize];
 }

Getter:

 + (SomeManagedObject *)getObjectByStoredId
 {
     NSURL *uri = [[NSUserDefaults standardUserDefaults] URLForKey:@"someObjectIdKey"];
     NSManagedObjectID *objectId = [self.persistentStoreCoordinator managedObjectIDForURIRepresentation:uri];
     SomeManagedObject *object = [self.managedObjectContext objectWithID:objectId];
 }
like image 29
Matt Foley Avatar answered Mar 04 '23 10:03

Matt Foley


You wouldn't want to try and archive a core data entity and store it. Instead, you would store the key or some other known attribute and use it to fetch the entity when the application starts up.

Some example code (slightly modified from the example posted in the Core Data Programming Guide):

NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
    entityForName:@"SearchEngine" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"engineName LIKE[c] '%@'", selectedEngineName];
[request setPredicate:predicate];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}

This way you save the name in the user defaults and fetch the entity when necessary.

like image 26
Jason Coco Avatar answered Mar 04 '23 10:03

Jason Coco