How I access managedObjectOriginal_ in Swift using Generated Classes?

I'm using Core Data automatic NSManagedObject subclass generation, with a custom Class Definition. I want to update a non-managed property when a managed property value changes. So, I'm trying to use the override accessor enhancements from iOS 10.0 (https://developer.apple.com/library/archive/releasenotes/General/WhatNewCoreData2016/ReleaseNotes.html):


- (void)setDepartment:(Department *)value
    // invoke the dynamic implementation of setDepartment
    [self managedObjectOriginal_setDepartment:value];
    NSLog(@"invoked %@", NSStringFromSelector(_cmd));


Problem is that my custom class is in Swift, so I'm at a loss on how to do this override. I've tried


managedObjectOriginal_setProperty(property)


But it gives me a compiler error. Is there any way to use this in Swift?

Replies

You don’t have to do anything special to access the non-managed properties, they’re just regular properties.


The reason why things may feel confusing is that the mechanisms required to make CoreData work involve the system generating intermediate classes with generated methods with the appropriate names that the various specifications are expecting. When you specify “pineapples” for an attribute in the entity description, everything for that pineapples gets generated depending on what happens when the system checks to see whether you implemented custom code for ”pineapples”.


For non-managed properties, Core Data doesn’t do anything. At all. Because there’s nothing in the entity description to tell it to do anything. So there’s nothing special generated and nothing special you do to interact with the property.

Sorry I guess I wasn't clear - my real problem is that I essentially want to do something akin to a Swift property observer on a managed property - the key part from the linked document above is:


NSManagedObject will now also allow subclasses to override accessors and still invoke the implementation that otherwise would have been dynamically generated.


So I have a managed property A, and a second, non-managed property B, and when A changes I want to update B.

I added an ObjC extension to NSManagedObject to make it easier to call these "managedObjectOriginal_" methods:

Code Block objc
@implementation NSManagedObject (CoreDataOriginalImplementations)
- (void)setValueViaOriginalCoreDataMethod:(id)value forKey:(NSString *)key {
    NSString *capitalizedFirstLetter = [[key substringToIndex:1] capitalizedString];
    NSString *remainder = [key substringFromIndex:1];
    NSString *newSelectorName = [NSString stringWithFormat:@"managedObjectOriginal_set%@%@:",
                                 capitalizedFirstLetter, remainder];
    
    SEL newSelector = NSSelectorFromString(newSelectorName);
    typedef void (*CoreDataSetter)(id, SEL, id);
    CoreDataSetter impl = (CoreDataSetter)[self methodForSelector:newSelector];
    
    impl(self, newSelector, value);
}
- (id)getValueViaOriginalCoreDataMethodForKey:(NSString *)key {
    NSString *newSelectorName = [NSString stringWithFormat:@"managedObjectOriginal_%@",
                                 key];
    SEL newSelector = NSSelectorFromString(newSelectorName);
    typedef id (*CoreDataGetter)(id, SEL);
    CoreDataGetter impl = (CoreDataGetter)[self methodForSelector:newSelector];
    return impl(self, newSelector);
}
@end


Then you can call it in Swift like this:

Code Block swift
class Person: NSManagedObject {
var department: Department? {
get {
let coreDataValue = self.getValueViaOriginalCoreDataMethod(for: "department") as? Department
/* Do other stuff here */
return coreDataValue
}
set {
/* Do other stuff here */
self.setValueViaOriginalCoreDataMethod(newValue, forKey: "department")
}
}
}