Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: Observing changes in related entities

I have a sample managed object model depicted in the image below. What I would like to happen is this: When the object that is the value for the currency relationship in a Bar object is changed, to automatically have that same object be set as the currency relationship in all Foos that are related to that Bar through the foos relationship.

enter image description here

Am I right to understand that this can be done through KVO? My inclination was to start by adding this to the Foo.m:

+ (NSSet *)keyPathsForValuesAffectingCurrency {
    return [NSSet setWithObject:@"bar.currency"];
}

but I can't figure out if this is right or how I would proceed further. Thanks for any advice.

like image 430
Mark Leonard Avatar asked May 30 '11 07:05

Mark Leonard


People also ask

How do you observe Core Data?

The simplest way to observe changes in a Core Data store is by listening for one of the various notifications that are posted when changes occur within Core Data. For example, you can listen for the NSManagedObjectContext.

What is entity Core Data?

An entity is represented by an instance of the NSEntityDescription class. This class provides access to a wide range of properties, such as its name, the data model it is defined in, and the name of the class the entity is represented by.

What is relationship in Core Data?

Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.


2 Answers

There are several possible solutions:

  1. Override the setter of setCurrency in Bar to also change the currency in Foo
  2. Use KVO(ADC) to update the relationship of Foo if the currency of Bar changes. Be careful with KVO in combination with Core Data (undo handling and faulting are tricky)
  3. Reconsider your datamodel if this is the best solution for your problem
like image 23
Martin Brugger Avatar answered Nov 08 '22 19:11

Martin Brugger


What you want to accomplish is certainly possible and KVO can take care of this. You would just have to implement a couple of things.

1. Register for observations of the key affecting your property

You can add your object as an observer when your object is awakening.

- (void) awakeFromInsert {
  [super awakeFromInsert];
  [self addObserver:self forKeyPath:@"bar.currency" options:NSKeyValueObservingOptionNew context:nil];
}

// Repeat for awakeFromFetch

Do note that this works in this case since this is a to-one relationship.

2. Take action when the notification is fired

Update the value of currency when a notification for bar.currency is fired

- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context
{
  if( [keyPath isEqualToString:@"bar.currency"] ) {
    [self setValue:[self valueForKeyPath:@"bar.currency"] forKey:@"currency"];
  }

  // The rest of your KVO logic ...
}

Alternative Approach

As you pointed out, this can also be done by implementing dependent keys

+ (NSSet *)keyPathsForValuesAffectingCurrency {
  return [NSSet setWithObject:@"bar.currency"];
}

In this case, you'd instead have to observe @"currency". In your observeValue... method you'd have to check for this key instead. Finally, you'd have to set the value using setPrimitiveValue:forKey as opposed to setValue:forKey:. This is because the latter would cause a new notification and result in an endless loop.

like image 198
Dave FN Avatar answered Nov 08 '22 18:11

Dave FN