How to observe Core Data add/remove actions?

  • I have a set of Core Data objects, which consist of properties that need to be communicated to other (non-Core Data) parts of the application. In fact, the properties of the currently selected object are 'active' and need to be communicated.
    I currently observe the corresponding array controller for the key @"selection.myProperty" to do that.
    When an object is added or removed, I observe "<arrangedObjects....>", which is indeed triggered on add/remove actions. However, when I read the selected properties, I still get the 'old' selected properties, not the ones from the new object.

    So, what is the correct way to do this?


    Thanks in advance,
    Arthur C.
  • > When an object is added or removed, I observe "<arrangedObjects....>"

    Why observe @count? Why not observe just arrangedObjects?

    > which is indeed triggered on add/remove actions. However, when I
    > read the selected properties, I still get the 'old' selected
    > properties, not the ones from the new object.

    It's not really clear to me what you are doing here. Here are some
    general thoughts:

    What are the "selected properties" that you are trying to read? Are
    they the properties of the entity just added/removed? If so, are you
    sure that this entity is, in fact, selected? Array Controllers have a
    "Select Inserted Objects" option that may effect this.

    Are you trying to read the "selected properties" directly from the
    controller or are you using the change dictionary passed to
    observeValueForKeyPath:ofObject:change:context:?

    Did you give -addObserver:forKeyPath:options:context: any options? All
    of those defined in NSKeyValueObservingOptions could effect what
    you're trying to do here.

    A more concrete example of what you're trying to accomplish (and how
    you are trying to accomplish it) could really help us help you.

    Cheers,
    -Joshua Emmons
  • > A more concrete example of what you're trying to accomplish (and how > you are trying to accomplish it) could really help us help you.
    Okay (but it'll take more text to describe). I have a collection of Recipes (Core Data managed objects). Each recipe has 8 Items (also Core Data), with each item having several properties.
    The interface is set up as a master/detail interface, so you can select (and add/remove) a recipe. The contents of that recipe are shown in the 'detail' table view: columns for the properties, rows for the Items.

    Now, the property values of the 'items' in the selected Recipe are needed elsewhere in the program (non-Core Data part).
    So those objects need to be informed about:
    - when another recipe is selected
    - when a recipe is removed or added
    - changes in a property inside the (selected) recipe. From the interface, you can only make changes to the recipe that is selected, and only to a property from the Item that is selected.
    - some recipe properties are dependent on other properties (by triggerChangeNotificationsForDependentKey), and they need to be observed as well.

    A natural way for this seems to be to observe the corresponding array controllers. There is an array controller for Recipes, and an array controller selectedRecipe, which has a binding to Recipes.selection.

    Now I'm still puzzled on how to do the observations correctly. Especially, on what to do from - observeValueForKeyPath.
    Currently I try to read the new values directly from the array controller, or from [object selection], as there is a known bug in the 'change' dictionary here.


    Thanks in advance,
    Arthur C.
  • Arthur,

    > Now I'm still puzzled on how to do the observations correctly.

    Assuming you have an NSArrayController (let's call it recipeArrCont)
    set for you Recipe entity and another NSArrayController whose content
    set is bound to recipeArrCont.selected.items (we'll call it
    itemArrCont), it seems as though you'd want to call:

    [itemArrCont addObserver:self forKeyPath:@"arrangedObjects" options:0
    context:"Something"];

    Somewhere in your object's initialization routines. You would then get
    messages sent to:

    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:
    (id)object change:(NSDictionary *)change context:(void *)context
    {
        if (context == "Something") {
          //The array of Item entities changed for some reason.
        }
        else {
          [super observeValueForKeyPath:keyPath ofObject:object
    change:change context:context];
        }
    }

    Because you're bound though the recipeArrCont's selected keypath, this
    message will be sent any time the selected recipe changes as well as
    whenever the itemArrCont's arrangedObjects change (due to additions/
    subtractions/sorts/etc).

    Don't forget to unregister the observer!

    - (void) dealloc {
        [itemArrCont removeObserver:self forKeyPath:@"arrangedObjects"];
        [super dealloc];
    }

    > Especially, on what to do from - observeValueForKeyPath.

    Well... that depends largely on what you want to have happen. You've
    told us that this is an array of Item entities that have properties
    the object needs to know about, but that's it. If the goal is simply
    to be able to read the properties of each Item entity, you could
    retrieve those with something like:

    for(NSManagedObject *obj in [arrayController
    valueForKeyPath:@"arrangedObjects"]){
        NSLog(@"property1: %@", [obj valueForKey:@"property1"]);
        NSLog(@"property2: %@", [obj valueForKey:@"property2"]);
        NSLog(@"property3: %@", [obj valueForKey:@"property3"]);
    }

    Does that help at all? If not, details details details! ;-)

    Cheers,
    -Joshua Emmons
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today