I am working on a shopping list app, and I have a couple of cases where I need to watch for changes to an entity, and respond by making changes to other entities. For example:
When a new store entity is added, I need to create related aisle entities.
When the quantity is changed on a food item to 0 or >0, I need to set a related state flag on the item (ftr, the flag has more than two states, I can't just drop the flag and test for 0).
Based on what I've read so far, the choices seem to be between using key-value observing & registering for a NSManagedObjectContextObjectsDidChangeNotification. What I'm not sure of is which is more suited to my scenarios (or if I should just override the appropriate methods in the related entity classes). Any pointers and/or documentation on when to use these would be much appreciated.
If the flag is a sort of derived property from the quantity information, then your best approach might be to override the setter for the quantity in your managed object subclass and update the flag there. Since the flag is a model property, it makes sense to keep responsibility for its value in the model, rather than in a view controller or other non-model object that's using KVO or responding to notifications.
You can to use NSFetchedResultsController instead of notifications. Also, you can modify your entity to have class methods to add these new records, and manage whatever other modification you need to do.
Related
I'm working on caching for an app using a subclass of NSCache. It's working well, though I'm having a hard time wrapping my mind around how to update items in the cache when necessary and propagate those changes throughout the app. Say there is a model class, Article. An instance of Article is cached, and a few view controllers in the app are observing relevant properties on this instance with KVO, so when any changes are made to the properties of this Article, the changes automatically propagate.
But say another request for that Article is made, and the cached version is now stale. A request is made to the network and a new, updated instance is serialized, so the cache and the view controllers displaying the old instance need to be updated. How can I propagate this change to the view controllers? Is there a way to observe not just a property, but observe the reference itself to get notified when it changes? In other words, do something like oldArticle = newArticle and have an observer on the oldArticle fire?
I have a few ideas of how to handle it, but none I particularly like. I could individually transfer the property values of the new Article to the old one so the relevant observers are fired - yuck. I could use an object proxy, and set up an observer on the proxy when the object that it references changes. I could use NotificationCenter or set up some delegates or something to notify the view controllers of the update. But is there a simpler way to do something as described above?
One approach to observing the whole article is to make the cache a mutable dictionary (maybe yours is already) where the keys are something essential to the article (like an id) and the values are articles. Observing view controllers can then observe both the cache's dictionary (where the key path is article id) -- to observe whole-article updates -- and any specific values they wish on the article itself.
Sticking with KVO, another approach is to give article and updateWith: (or copyFrom:) method that takes another article parameter, updating itself with the props from that parameter. The VCs in this case will keep watching the same object, and their existing update logic will work.
I would create an ArticleUpdateDelegate, and make your ArticleViewController conform. The VC then sets itself the delegate of your Article, so that whenever the article is updated, you notify the VC, which can then update its view, and do whatever else it needs.
I have a navigation property, which I added a custom validator to.
The validator triggers fine when saving the entity. However it does not trigger when adding/removing entities from the navigation property.
Should I subscribe to propertyChanged event, or is there another way to handle this ?
Also, why is there not a propertyChanged event per property instead of having to subscribe to any properties change ?
There are two kinds of navigation properties, scalar and nonscalar. A scalar property is something like 'Order.Customer' where an order has a single customer associated with it. Setting or changing the customer in this case will fire the entityAspect.propertyChanged event.
For a nonscalar property like say 'Customer.Orders', accessing the property returns an array of orders associated with the customer.
This is always the same array. Orders can be added or removed from it, but the returned array itself is always the same. Because the array itself is never changed a property change is NOT fired for these operations.
However you CAN watch the array itself for changes, see the arrayChanged event
in the breeze Api docs.
As for why there is no separate event that you can register to fire only when a specific property is changed, the reason is that the current mechanism supports your ability to do this while at the same time allowing for those use cases where you want to see 'all' of the changes to an entity without having to register what could potentially be tens of thousand of events.
Remember entity property level events, if they existed, would have to be registered on as many entities as are in your cache (100's or 1000's) times the number of properties on your entities (5-50).
Most of what is described here is fairly standard for entity change tracking in a number of environments across a variety of programming languages. We didn't try to reinvent the idea, but simply reimplemented a pretty common standard.
Is there a way to have changes to an object's attribute also "alert" related objects?
The structure we have is as follows:
Image has an attribute called content
Category has a one-to-one relationship to Image
It would be ideal if changes to attributes within the Image object could be detected by the related Category, in a way that the Category would be included in the NSUpdatedObjectsKey of NSManagedObjectContextObjectsDidChangeNotification. I've seen some suggestions indicating that adding a sentinel attribute such as needsUpdate to Category would be a good way to do this, but that seems like a cumbersome way of handling this.
My reasoning for doing this is that I need to reload a tableview whenever a Category changes, or whenever it's associated Image changes, at the moment in my observation method for NSManagedObjectContextObjectsDidChangeNotification I check updated/deleted/inserted objects for Image instances or Category instances, however Image instances are used elsewhere in the app and may have no relationship to a Category instance, in which case it would be a waste to reload the tableview. I could manually loop through the updated/deleted/inserted objects to see if they are Image instances associated with a Category, but that doesn't seem like the best place to do it.
I found that this question is similar to what I am attempting, however it has no answer.
Please let me know if additional information is needed, or if my question is too convoluted.
Edit: Modified to hopefully make it more apparent that I'm interested in Category being aware of changes within the Image object's attributes, rather than a change in the relationship itself.
I would suggest learning about KVO (Key Value Observing) as that framework is designed specifically for this purpose. With KVO you can listen to a specific object for changes on its attributes and then react to them.
However, I question why you are not using a NSFetchedResultsController for this. That is what the NSFetchedResultsController is designed for.
I am using Core Data to model an entity which has both attributes and relationships. I would like to make one of the attributes dependent on two other relationships.
The Core Data FAQ and several other examples use +(NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key for this purpose. However, this only seems to be called for all of my relationship properties, but not for any of the attributes. In addition keyPathsForValuesAffecting<key> never gets called for any of my attributes.
Is this the normal behaviour or am I missing something? How can I calculate one attribute based on other attributes or properties, eg. a distance, by setting a startLocation and a stopLocation?
It looks like a found the solution to my own problem after a couple of days and it turns out I was missing something.
After rereading the discussion of the method keysPathsForValuesAffectingValueForKey: in the NSKeyValueObserving Protocol Reference, I realized the meaning of the following sentence:
When an observer for the key is registered with an instance of the receiving class, key-value observing itself automatically observes all of the key paths for the same instance, and sends change notifications for the key to the observer when the value for any of those key paths changes.
In short, your instance should have an observer observing changes to your attribute <key>:
[myInstance addObserver:myObserver forKeyPath:attributeKey options:nil context:nil];
As soon as you have an observer registered, the protocol will call keysPathsForValuesAffectingValueForKey for your specific attribute key. If this method returns a non-empty set of key paths, KVO will emit a change notification for your attribute, if a change is made to any of these key paths, in addition to notifying you of any direct change to your attribute.
Relationship keys do get called automatically, because Core Data already uses observers to keep inverse relationships up to date.
In the particular case where you want to have an attribute depend on another attribute or relationship within the same entity, you will have to:
Add an observer in the awakeFromInsert: method using addObserver:forKeyPath:options:context:
Implement keyPathsForValuesAffectingValueForKey: or keyPathsForValuesAffecting<key>
Implement observeValueForKeyPath:ofObject:change:context for your attribute key path to act on the relevant change notifications, i.e. updating your attribute value.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
KVO and NSMutableArray
Like the title says, I need to be able to detect a change in a mutable array. The reason for this would be to automatically refresh my mapview for an app I'm making as soon as the user adds a new location.
I imagine it would be something like asking if the array is equal to the array's count plus one or the array's count minus one but I don't know how to implement that.
If you need more information before answering please don't hesitate to ask in the comments.
You can use Key-Value Observing for this. For more details check Introduction to Key-Value Observing Programming Guide.
Key-value observing provides a mechanism that allows objects to be
notified of changes to specific properties of other objects. It is
particularly useful for communication between model and controller
layers in an application. (In OS X, the controller layer binding
technology relies heavily on key-value observing.) A controller object
typically observes properties of model objects, and a view object
observes properties of model objects through a controller. In
addition, however, a model object may observe other model objects
(usually to determine when a dependent value changes) or even itself
(again to determine when a dependent value changes).
You can observe properties including simple attributes, to-one
relationships, and to-many relationships. Observers of to-many
relationships are informed of the type of change madeāas well as which
objects are involved in the change.
Check this blog about KVO on how to use it. Also check this tutorial. Check this question as well, Key-Value-Observing a to-many relationship in Cocoa
There are many answers to this question.
A popular way would be to use NSNotficationCenter to post notifications everytime your model changes.
You can also use delegation to invoke a method when an object is inserted or Key-Value Observing.
It much depends on your design. If you specify, maybe you can get a more concrete answer.
you could use KVO for that. Personally I don't like NotificationCenter but I guess that personal taste.
see KVO and NSMutableArray
Yet another solution would be Don't Do That. If you the one making the change to the mutable array, then you know you are making a change to the mutable array. If you aren't, then don't vend a mutable array; wrap the array in a class so that any changes must come in through a method that you control.