Reputation: 24563
I have the following coredata entities:
@interface Car (CoreDataProperties)
@property (nullable, nonatomic, retain) NSSet<Part *> *parts;
@end
@interface Part (CoreDataProperties)
@property (nullable, nonatomic, retain) Car *car;
@end
This is a one to many relationship between car and parts. In one of my view controllers I display a car view with all parts. I want to listen for changes to the parts of the car.
I thought this would be easy enough with KVO.
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.car addObserver:self forKeyPath:@"parts" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
}
then:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([@"parts" isEqualToString:keyPath]) {
// do what I need to do with new parts
}
}
However when I pull changes from a server to update a car parts I am getting unexpected callbacks to the observeValueForKeyPath method even when I do not change any parts.
Wondering if faulting could be the problem: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html#//apple_ref/doc/uid/TP40001075-CH18-SW7
Was hoping to use KVO, but maybe with coredata objects its just not a great idea. Am I doing something wrong or should I use an alternative?
If I should not be using KVO, I believe my other options are: 1. Listen for MOC changes. This is not great as I don't really know what changed on the object. 2. Implement a FetchedResultsController on parts with a predicate to find only the car I am interested in. Seems like a bit of overkill, but I think this will give me what I need.
Upvotes: 1
Views: 5634
Reputation: 2027
NSFetchedResultsController
is the preferred way to observe changes in some data subset.
And for single object you can observe changes in NSManagedObjectContext
.
Subscribe to NSManagedObjectContextObjectsDidChangeNotification
:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(contextDidChange:)
name:NSManagedObjectContextObjectsDidChangeNotification
object:context];
When notification arrives, process its userInfo
:
- (void)contextDidChange:(NSNotification *)notification
{
NSManagedObjectContext *context = notification.object;
NSDictionary *userInfo = notification.userInfo;
NSArray *invalidatedAll = userInfo[NSInvalidatedAllObjectsKey];
NSSet *invalidated = userInfo[NSInvalidatedObjectsKey];
NSSet *deleted = userInfo[NSDeletedObjectsKey];
NSSet *updated = userInfo[NSUpdatedObjectsKey];
NSSet *refreshed = userInfo[NSRefreshedObjectsKey];
// context reset
if (invalidatedAll) {
// probably you better have to dismiss your VC here.
return;
}
// invalidated
if ([invalidated containsObject:self.car]) {
// it make sense to dismiss here too.
return;
}
// deleted
if ([deleted containsObject:self.car]) {
// and here.
return;
}
// refreshed
if ([refreshed containsObject:self.car]) {
// update your interface.
return;
}
// updated
if ([updated containsObject:self.car]) {
// update your interface.
return;
}
}
To receive notifications about Car
object when related Parts
updated, see this answer.
You can get list of changed attributes from changedValues property.
Don't forget to unsubscribe:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSManagedObjectContextObjectsDidChangeNotification
object:context];
}
UPDATE:
Here's that conception implemented as a UIViewController
category:
BTDependentVC
Upvotes: 5