KVO with NSPrivateQueueConcurrencyType - objective-c

I would like to determine if an attribute changed in core data and update my UI. The attribute may change as the result of a background fetch running in a managed object context of type NSPrivateQueueConcurrencyType.
I added a listener: [myCoreDataEntity addObserver:self forKeyPath:myCoreDataAttribute options:NSKeyValueObservingOptionNew context:nil]
But the event never fires. Any idea why? The object is changed in another context - could this be the reason? (When a save: is done on the parent context, nothing still fires).
I can use manual KVO, but since the object has not been saved yet to the parent context, refreshing the UI does not work since it is point at the context in NSMainQueueConcurrencyType when the object was changed in a context associated with NSPrivateQueueConcurrencyType

You're not getting notifications because you're observing the wrong object. The NSEntityDescription never changes at run time. It's a representation of the way the entity was defined in the data model. Instances of NSManagedObject that use the entity description can and do change at run time, though. If you want to know whether an attribute changed on a specific managed object, you need to observe that specific object.
If you need to get notified any time any managed object changes the value for that attribute, your best option is to write a custom setter for that attribute and handle it there. You might also find NSManagedObjectContextObjectsDidChangeNotification useful, but that will fire for any attribute change.

Assuming that myCoreDataEntity in your example is a managed object, the issue is that automatic external change notifications are [disabled by Core Data for managed objects] for modeled properties1:
NSManagedObject disables automatic key-value observing (KVO) change notifications for modeled properties, and the primitive accessor methods do not invoke the access and change notification methods. For unmodeled properties, on OS X v10.4 Core Data also disables automatic KVO; on OS X v10.5 and later, Core Data adopts to NSObject’s behavior.
You can turn them on for specific properties, or for all properties in your managed object subclass or in a category on the managed object subclass:
Single property:
- (BOOL) automaticallyNotifiesObserversFoMyCoreDataAttribute {
return YES;
}
All properties (Not recommended):
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
return YES;
}
The reason it does not send out automatic change notifications is primarily performance. Automatic change notifications do add some overhead, though on recent hardware it's fairly minor, even when working with thousands of objects. As always, profile to see what works for you.

Related

Cocoa Bindings, should I just be using KVO instead?

[self.toolController bind:#"fillColor" toObject:self.fillColorWell withKeyPath:#"color" options:kvoDict];
versus
[self.fillColorWell addObserver:self.toolController forKeyPath:#"color" options:NSKeyValueObservingOptionNew context:nil];
and in my toolController class, in my implementation for -observeValueForKeyPath:...
if( [keyPath isEqual:#"color"] ) {
self.fillColor = [object selectedObject];
}
Why would I pick one method over another to get the view to update to my model property?
For bindings the only code you have to write is for the bind itself and thats it. With KVO you would have to write to code to handle the notification. If your binding UI and using Interface Builder then you don't need any code at all, which can be useful / a time saver for the simpler things + you don't have to generic write boiler plate code to keep things is sync which you would to respond to the KVO notification.
I have read otherwise, but its my understanding (and I did a quick new project to verify this) that bindings are in both directions. So say if you bind a text field to an NSString, the variable changes when the textfield gets updated and you can change the variable and the text field updates. KVO would only notify you on the object you have specified the update for.
Some say bad things about bindings and that its good that they aren't part of iOS etc etc, but they work for the simple cases and so maybe you should just go with bindings until you find case where they are inappropriate. But having said if you want at some point to take your code over to iOS...
Hope thats a good enough answer for you :)

Change notification for self in NSMutableDictionary subclass

I've subclassed NSMutableDictioary following mainly this great blog post.
Now in that subclass the dictionary itself subscribes to all its values in order to detect any changes. This bit works just fine and value changes are observed internally in that subclass.
However value changes should then be propagated to observers of the dictionary, i.e. observers of the dictionary (subclass) are supposed to get notified that the dictionary has changed.
Except I cannot figure out how to trigger that KVO notification - using
[self didChangeValueForKey:#"self"];
doesn't seem to have any effect so I'm kinda stuck.
How can an instance of the NSMutableDictionary subclass initiate a change notification for itself?
As already hinted by Ken KVO allows you to observe properties - not an object per se!
The Key-Value Observing Programming Guide states, that
Key-value observing provides a mechanism that allows objects to be notified of changes to specific properties of other objects.
In other words: self can never be observed.

KVO produces error/not receiving notification

I have a class (call it classA) that contains a property named info (a model class, containing lots of info), wich in turn contains a property named name (a string). I want another class (classB) to receive a KVO notification when the string name changes in classA.
This is what I'm doing now on classB:
[classA addObserver: self forKeyPath: #"info.name" options: 0 context: nil];
There are two ways the value name changes on classA: when it is set directly like classA.info.name = ... and when info is set like classA.info = ...
When name is changed directly KVO works perfectly. However, when the info property is set and name changes indirectly, I get this error:
Cannot update for observer <classB> for the key path "info.name" from <classA>, most likely because the value for the key "info" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the classA class.
What should I change on classA to make this work?
The cause of this issue comes from the fact that you are implementing the setter -setInfo: for info and calling willChangeValueForKey: | didChangeValueForKey: inside it.
I've seen many instances in which it is believed that the will|didChange... calls need to be implemented for the KVO notification to be fired. This is true only when the setter is not explicitly called. When you do call the setter, the KVO mechanism takes care of firing the notification.
Most of the times leaving these calls in the setter is harmless and just causes extra notifications, but as seen in this case, it does cause a problem when updating a keypath -- as opposed to a key.
In short, if you do implement a setter, do not call will|didChangeValueForKey: inside of it.
Options cannot be 0, I think.
Its an old API, back before Apple got diligent about API design, and you have to give it something.

Providing your own setter for a CoreData property / attribute

I have an entity with several properties, one of them called lastModificationDate. Whenever any of the object's properties is set, I'd like to update the lastModificationDate.
If I were not using Core Data, I would just provide my own setter for the properties and update lastModificationDate. However, I'm not sure if I should mess around with CoreData's properties.
What's the best way to do this?
Overriding the setters can easily be done, you have to make sure you fire the right notifications for everything else to work (including KVO).
- (void) setThing:(NSObject *)myThing {
self.lastUpdateDate = [NSDate date];
[self willChangeValueForKey:#"thing"];
[self setPrimitiveThing:myThing];
[self didChangeValueForKey:#"thing"];
}
This being said, if all you need to do is the code I showed (essentially setting the value and updating the last update date), you are much better off using Key-Value Observing and reacting to the notifications. It's easier and cleaner.
You shouldn't override property mutators (setters) if you're working with an NSManagedObject subclass because those implementations are provided at runtime (hence #dynamic instead of #synthesize). You could if you really wanted to, but it's messier and there's no reason to. Use Key Value Observing (KVO) instead. It'll let you know when a value is changed.
Apple's KVO documentation is great: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i

How to stop Core Data application from loading managedObjectContext automatically?

I am building a core data application that checks for a saved user login upon launch and sets up the model, store, coordinator and context afterwards. The only problem I have is that as soon as the user clicks on any view in the interface, the application tries to get the managedObjectContext causing an exception seeing as I haven't created the stores yet.
Is there any way to stop it from doing this?
Cheers.
If you're using the Coredata boiler plate stuff provided by Apple, you will notice the managedObjectContext object is loaded lazily when its property is accessed.
Simply tell your view controller to access the context via its property (i.e. self.managedObjectContext) instead of accessing the variable directly, and the context, object model and persistent store coordinator will be created appropriately.
PS: This is just a guess as you didn't post any of your related code here.
Why are you showing views that depend on the managed object context without either creating it already or arranging for it to be created on access?
The usual pattern is to have your managed object context getter look something like this:
- (NSManagedObjectContext *)managedObjectContext {
if (!_managedObjectContext) {
// create context, and store it in _managedObjectContext
}
return _managedObjectContext;
}
(in this code, _managedObjectContext is an ivar in the class to hold the context). That way the context gets created automagically when needed. Apple's standard sample code does just this for you.