This isn't an easy problem. The thing is, Core Data doesn't know anything about numKm
as a property. How is it supposed to know that numKm
corresponds to a particular CarProperty
object?
The fundamental problem you're describing is key-value coding compliance. Cocoa's going to look for a method called numKm
on the properties
object. Not finding one, it'll try sending [properties valueForKey:@"numKm"];
Since valueForKey:
doesn't know what to do with numKm
, you get an error, but not before it calls [properties valueForUndefinedKey:@"numKm"]
But here's the catch: properties
is an NSSet
generated by Core Data, so you can't subclass it to override valueForUndefinedKey:
. What you can do is create your own object that's KVC-compliant for your arbitrary properties and use that instead.
One solution is to subclass NSDictionary
and make it act as a proxy. The primitive methods are count
, objectForKey:
and keyEnumerator
. If you override these three methods, you can create an NSDictionary
that's linked to your Car
object and returns the appropriate CarProperty
objects. For example:
@interface PropertyProxy : NSDictionary {
}
@property (nonatomic, readonly, assign) Car *car;
- (id)initWithCar:(Car *)car
@end
@implementation PropertyProxy
@synthesize car = _car;
- (id)initWithCar:(Car *)car {
if (!(self = [super init]))
return nil;
_car = car;
return self;
}
- (NSInteger)count {
return [car.properties count];
}
- (id)objectForKey:(NSString *)key {
return [[car.properties filteredSetUsingPredicate:[NSPredicate predicateWithFormt:@"key == %@", key]] anyObject];
}
- (NSEnumerator *)keyEnumerator {
return [[car valueForKeyPath:@"properties.key"] objectEnumerator];
}
@end
Then, in your Car
class, do this:
@interface Car : NSManagedObject {
// other stuff
}
@property (nonatomic, readonly) NSDictionary *carProperties;
// other stuff
@end
@implementation Car
// other stuff
- (NSDictionary *)carProperties {
return [[[PropertyProxy alloc] initWithCar:self] autorelease];
}
@end
(Disclaimer: I just typed this into my web browser, so no guarantees this actually compiles :-))
As you can see, it's not the easiest thing in the world to do. You'll be able to set up key paths like this:
representedObject.carProperties.numKm;
Keep in mind that, while this is key-value coding compliant, it is not key-value observing compliant. So if numKm
changes, you won't be able to observe that. You would need to do some extra work to make that happen.