views:

1832

answers:

2

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

+2  A: 

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.

Jason Coco
Mmh. Do you know why `array != nil` but `[array count] < 0` ... it doesn't want to work...
papr
If array != nil then there is no error but there also may be no records returned ([array count] == 0)... however, [array count] < 0 can *never* occur since the count message returns an NSUInteger type. The compiler most likely strips any such test ([array count] < 0).
Jason Coco
Yeah. I meant ([array count] == 0). Sorry for that. I only wanted to express that there is nothing in this array. ;)
papr
+3  A: 

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:.

mmalc
I think this is a neat solution. One small addition, as the documentation for NSUserDefaults says, you need to archive NSURL objects as NSData. So becareful not to save NSURL as it is. I just made this mistake and kept getting nil when I tried to retrieve the object.Note also that NSUserDefaults has a setURL:forKey: method. I don't think this is available for iPhone OS at the moment though.
tilish