How do I subscribe to objects being added and removed from a NSMutableDictionary using ReactiveCocoa? Also, I'd like to broadcast a notification when it changes. My guess is that broadcasting can be done using RACMulticastConnection but how do I tie this up with the dictionary change? I'm trying to use ReactiveCocoa for the first time in my project and stuck on the first thing I wanted to do :(
RACObserve is a wrapper around key-value observing, and inherits the same features and flaws.
Unfortunately, NSMutableDictionary is not automatically observable. There are two ways to work around that:
Subclass it and add KVO support.
Create a real model object, with properties instead of dictionary keys. Then you'll get KVO on those properties, as long as you use setters instead of direct ivar modification.
I'm not sure exactly what you mean by "[broadcasting] a notification when it changes," or why it'd be valuable. Notifications are way too global for my taste, and I'd promote using more limited observation instead (like KVO).
However, assuming you definitely want to do this, it's simple enough to post a notification in response to a new signal value:
#weakify(self);
[RACObserve(self, dictionary) subscribeNext:^(NSDictionary *dictionaryValue) {
#strongify(self);
[NSNotificationCenter.defaultCenter postNotificationName:SomeNotificationName object:self];
}];
If you want KVO's change dictionary (which includes information about the added/removed values), you'll need to replace RACObserve with +rac_valuesAndChangesForKeyPath:options:observer:.
every time you set or remove the key-value,reset the dict,so you can observer the dict.
just like:
[RACObserve(self, testDict) subscribeNext:^(id x) {
NSLog(#"RACObserve testDict:%#",x);
}];
[self.testDict setObject:value forKey:key];
self.testDict=self.testDict;
Related
[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 :)
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.
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.
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
I feel that i don't fully understand difference between KVO and NSNotification... They seem to be so similar...
Could you make some example showing when is best to use one method and when the other ?
I don't speak about Bind and IB, but i mean add Observer programmatically in my code with NSNotificationCenter or KVO
[self.preferenceController addObserver:self
forKeyPath:#"color"
options:NSKeyValueObservingOptionOld
context:#"Color-change"
];
KVO only works on values, NSNotification can be used for value changes but it can be used for anything and can carry a much greater payload.
For example, you could have an NSNotification posted whenever a file has finished downloading and the userInfo could contain the length of time it took, the number of bytes downloaded and the filesystem path that the file has been saved to.