Currency Converter Bindings Example help

I am trying to translate the Cocoa Application Tutorial Using Bindings into ASOC. I have three text fields that have their values bound to an object controller, and I have the following code:

script ConverterAppDelegate
	property parent : class "NSObject"
	property NSSet : class "NSSet"
	property exchangeRate : 7.3
	property dollarsToConvert : 100
	on amountInOtherCurrency()
		return (exchangeRate * dollarsToConvert)
	end amountInOtherCurrency
	on keyPathsForValuesAffectingAmountInOtherCurrency()
		return NSSet's setWithObjects_("exchangeRate", "dollarsToConvert")
	end keyPathsForValuesAffectingAmountInOtherCurrency
	on applicationWillFinishLaunching_(aNotification)
	end applicationWillFinishLaunching_
end script

When I start the program, the amountInOtherCurrency text field has the correct value, $730, in it, and if I change the amount in the dollarsToConvert field, it works fine. However, if I change the amount in the exchangeRate field I get the following error:

2010-05-11 09:41:03.688 Converter[5085:a0f] *** -[ConverterAppDelegate amountInOtherCurrency]: Can’t make «class ocid» id «data kptr0000000000392E0002000000» into type number. (error -1700)

Any ideas on what is going on here?

Further weirdness: if I change the order of the variables in the amountInOtherCurrency() method, so that it reads return (dollarsToConvert * exchangeRate), then changing either text field results in the error instead of just the exchangeRate field giving the error.

I have fixed the problem with the following change, though I’m mightily confused as to why exchangeRate and dollarsToConvert are being interpreted differently:

on amountInOtherCurrency()
		return ((exchangeRate as real) * (dollarsToConvert as real))
	end amountInOtherCurrency

Even though I’ve got it working, I’d still be interested in hearing if anyone knows what was going on.

My guess:

When you start the app, exchangeRate is a real, as set by the property declaration. So when it comes to exchangeRate * dollarsToConvert, normal AS coercion rules coerce dollarsToConvert to also be a real.

When you reverse the order, dollarsToConvert is an integer, and exchangeRate can’t be coerced from a pointer containing, effectively, a real to an integer, hence the error. Change both to reals and try again.

What’s the keyPathsForValuesAffectingAmountInOtherCurrency() in applicationWillFinishLaunching_ for?

I thought I needed to call that function, but I just tried it without that, and it still works. In the tutorial, they had that method inside an initialize: method, so that’s why I thought it was necessary. So, what does call that method?
And is this the best way to deal with a bound property that is dependent on other bound values?

Ok, I tried making both values real in the property declaration but That still doesn’t fix it. Messing around with various combinations, it seems like which ever property is first in the return statement has to be real – if it is, then changing that one works and the other one doesn’t.


The KVO mechanism. When an object registers as an observer of a value, this handler is called to see what other key paths it should watch. I presume it’s called just at registration time (that’s what adding a log statement suggests).

And is this the best way to deal with a bound property that is dependent on other bound values?

Hard question to answer…

I think if you’re using bindings, the easiest thing is to automatically coerce variables whenever you use them in an AS contenxt (ie, not to call methods on them or use them as parameters to a method).

the handler

on keyPathsForValuesAffectingAmountInOtherCurrency()

is called during initialization time to set the key paths.
The basic name is keyPathsForValuesAffecting, the suffix AmountInOtherCurrency is the keyword for this particular observer.

A corresponding handler

on amountInOtherCurrency()

is called like a callback function whenever one of the key paths are going to change.

An explicit call of keyPathsForValuesAffectingAmountInOtherCurrency() is not needed