Search code examples
objective-ccocoacore-datacocoa-bindingskey-value-observing

Automatically create new entity when bound property changes


I have an entity-based NSObjectController that is configured to use a "MyProperty" object it creates from a fetch request, like so:

@interface MyPropertyController : NSObjectController
@property (strong) NSString *propertyName;
@end

@implementation MyPropertyController

- (instancetype)initWithPropertyName:(NSString *)propName moc:(NSManagedObjectContext *)moc {
    if (self = [super init]) {
        self.propertyName = propName;

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"propertyName == %@", argumentArray:@[propName]];
        self.managedObjectContext = moc;
        self.entityName = @"MyProperty";
        self.fetchPredicate = predicate;
    }
    return self;
}

- (id)newObject {
    MyProperty *prop = [super newObject];
    prop.propertyName = self.propertyName;
    return prop;
}

@end

In the UI I bind MyProperty.numberValue to a slider, so that the user can change the value of the sider:

MyPropertyController *controller = [[MyPropertyController alloc] initWithPropertyName:@"some user-specified name" moc:self.managedObjectContext];
NSSlider *slider = [[NSSlider alloc] init];
[slider bind:NSValueBinding toObject:controller withKeyPath:@"content.numberValue" options:@{ NSNullPlaceholderBindingOption : @0 }];

This all works find as long as the instance of MyProperty exists when the user is dragging the slider. However, I want to automatically create the instance of the property if the user drags the slider and the MyProperty instance does not yet exist.

Is this possible? The only way I've been able to accomplish it has been to use a custom NSValueTransformer that uses the reverseTransformedValue to manually create and insert an instance of the class, but I can't help but feel that there should be a proper way to do this in the NSObjectController itself.


Solution

  • You can maybe solve this problem by using a container object as object for your object controller, which always exists. And this container object hold the entity in a property.

    You can then overload the setValue:forKeyPath: and ensure that the entity gets created before you set the new value.

    Another idea would be using a NSProxy for your entity, but this would be difficult to get it right with KVO.