• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Is willChangeValueForKey: always needed?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is willChangeValueForKey: always needed?


  • Subject: Re: Is willChangeValueForKey: always needed?
  • From: Chris Hanson <email@hidden>
  • Date: Thu, 16 Nov 2006 22:11:45 -0800

On Nov 16, 2006, at 4:22 PM, David Catmull wrote:

Does didChangeValueForKey: always have to be preceded by a call to willChangeValueForKey:?

Yes.

I have a case where the value in question is calculated based on other values (which tab is selected, and whether a certain text field has text), and the key is simply the name of the method that calculates the value. So when one of those other values changes, it's too late to say that the calculated value "will" change.

You should make this key's value on the values of the other keys.

Also, forgive me if I'm misinterpreting you here, but it sounds like your application isn't necessarily leveraging the Model-View- Controller pattern.

Instead of having a calculated controller or model key take the values to use for its calculation directly from views/controls, you should have your views/controls bound to some sort of model- or controller- layer attributes, and then have whatever needs to take this calculated value bound to its corresponding key.

For example, if I were writing a currency converter application, I would bind my three text fields -- Amount, Exchange Rate, and Converted Amount -- to the amount, exchangeRate, and convertedAmount of a controller object. In that object's +initialize method would use +[NSObject setKeys:triggerNotificationsForDependentKey:] to indicate that any changes in either the amount or exchangeRate attribute will result in a change in the convertedAmount attribute, and I would put the logic to actually calculate the convertedAmount in the getter method for that attribute.

I'm wondering about this because I have a situation where my bindings don't seem to be updating immediately, causing a unit test to fail.

Note that properties manipulated programmatically through object controllers may not update immediately, in programmatic terms. See all of the questions on the list where someone invokes - [NSArrayController addObject:] in code and then attempts to get the object that was just added from the controller.


- The unit test calls setValue:forKeyPath: on an NSObjectController used to store the settings for my window's controls

A controller mediates between model and view objects, it shouldn't be storing things on its own.


- My window controller object is observing that keypath, and in response it calls will/didChangeValueForKey:@"canGo"
- The "Go" button's enabled property is bound to the window controller's canGo key
- The unit test calls [goButton isEnabled] and unexpectedly gets NO
- The unit test calls [controller canGo] and gets YES as expected


I thought perhaps willChangeValueForKey: might be caching the "old" value to see if it really does change, but the canGo method doesn't seem to get called there.

The user experience that this test is trying to simulate does in fact work correctly - when you type something into the text field and the correct tab is active, the button becomes enabled.

There are a couple of issues with the above. The first is, as I pointed out, is that you're expecting a property of a view to change immediately after programmatically manipulating the model/controller level object it takes the value of the property from.


The other issue is that you're trying to test your interface's behavior from a unit test. Rather than trying to simulate user interaction to test your model/view/controller interaction, you should take a look at just checking the connections between the various layers. You can check that your views are connected to your controller and model objects very easily using the -infoForBinding: method for bindings and the -target & -action methods for target/ action. You can check that your controllers are bound or otherwise have appropriate references to your model objects the same way.

I've posted a couple of articles describing this strategy, which I call "trust, but verify" (TBV) on my weblog:

  Trust, but verify.
  http://chanson.livejournal.com/118380.html

  Unit testing Cocoa user interfaces: Target-Action
  http://chanson.livejournal.com/148204.html

Cocoa is really amenable to this kind of unit testing. I hope to have some time soon to post another article on how to use -infoForBinding: to test the configuration of Cocoa bindings in a trust-but-verify fashion.

Hope this helps!

  -- Chris

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Is willChangeValueForKey: always needed?
      • From: David Catmull <email@hidden>
    • Re: Is willChangeValueForKey: always needed?
      • From: Chris Hanson <email@hidden>
References: 
 >Is willChangeValueForKey: always needed? (From: David Catmull <email@hidden>)

  • Prev by Date: Re: NSScrollView bug or misunderstood behavior?
  • Next by Date: Chinese character handwriting recognition
  • Previous by thread: Re: Is willChangeValueForKey: always needed?
  • Next by thread: Re: Is willChangeValueForKey: always needed?
  • Index(es):
    • Date
    • Thread