Brock Boland
Brock Boland

Reputation: 16720

How do I indicate to CoreData that an object has changed?

In my CoreData model, I have an entity called Contact. It has an attribute called profileImage, with the type set to Transformable.

In the Contact class (a subclass of NSManagedObject), I've changed profileImage from being a generic id to being an UploadedImage:

@property (nonatomic, retain) UploadedImage * profileImage;

The UploadedImage class has a few properties of its own.

The problem is that CoreData doesn't know when the properties on the UploadedImage object have changed. If only those properties are changed, the willSave method is never called on the Contact object when the managed object is saved. It works as expected if I change any other property on the Contact object: it just doesn't "see" changes to anything on the UploadedImage object within the Contact object.

What's the correct way to indicate that the managed object needs to be saved? Should I manually call willChangeValueForKey: and didChangeValueForKey: for the key 'profileImage' on my Contact object?

Edit to clarify a couple things

Upvotes: 4

Views: 2038

Answers (3)

Ivorius
Ivorius

Reputation: 365

Expanding upon Scott's solution (no copy necessary!), here is a method for Swift that works with any field:

static func markDirty<C, T>(_ obj: C, _ keyPath: ReferenceWritableKeyPath<C, T>) {
    obj[keyPath: keyPath] = obj[keyPath: keyPath]
}

Used like so:

NSManagedObject.markDirty(playlist, \.rules)

Unfortunately it's not possible right now to define it as an instance method, due to limitations with generics.

Upvotes: 0

Scott Berrevoets
Scott Berrevoets

Reputation: 16946

Unless there's another way I'm not aware of, it sounds like marking the NSManagedObject as dirty is the way to go. You can do that using a separate dirtyFlag attribute, or by simply using the profileImage attribute itself.

One way to automate this would be KVO. You observe all properties of UploadedImage in the Contact class, and then just call self.profileImage = self.profileImage. I'm not sure if this is optimized by the compiler. If it is, you can also call willChangeValueForKey: and didChangeValueForKey:, which should trigger it. If Core Data noticed that the object didn't actually change, you can try to implement NSCopying and assign a copy of the original UploadedImage.

Upvotes: 3

Johnathon Sullinger
Johnathon Sullinger

Reputation: 7414

I don't believe Core Data will persist changes made to the subclass. The contract in Core data is between the parent entity and Core Data, so Core Data only responds to changes made to the properties in the entity class.

You can get around this by making that property a NSDictionary in the Entity and in the subclass add the image object to the dictionary.

Upvotes: 0

Related Questions