CurrencyConverter Revisited
George Wright
gwright@labyrinth.net.au

Back to the List of Tutorials

Introduction
Browsing through the developer documentation after installing the 'Xcode' disk that came with my Panther Mac OSX 10.3, I noticed two new versions of the venerable old Currency Converter tutorial. You may be familiar with the original document on the "Developer Disk". It was called 'ObjCTutorial'. I got my first taste of Cocoa, Project Builder and Interface Builder (IB) by repeating that tutorial until I could do the whole project without reference to the documentation. I later modified it and created my own version which became the 'More Programming In OSX' article published in AUSOM News March 2003. The same 'ObjCTutorial' - updated for Xcode, which replaces Project Builder, - can be found at /Developer/Documentation/Cocoa/Conceptual/ObjCTutorial/index.html. It is an excellent introduction to Cocoa programming and essential work for novices like myself. Besides introducing the message-sending syntax of Cocoa it also builds the concept of structuring a project using the Model, View and Controller (MVC) sections.
The Cocoa Controller Layer
The second version of CurrencyConverter was a surprise for me. It follows on documentation relating to a new programming feature of Cocoa, the Controller Layer, and its implementation in Interface Builder as bindings. The discussion begins at /Developer/Documentation/Cocoa/Conceptual/ControllerLayer/index.html. Apple's commitment to the MVC program structure has been enhanced by the use of a new Controller palette in Interface Builder that supports building controller objects. The Foundation framework helps programmers write model classes while the Application Kit framework helps writing view classes. However, before Mac OS X v10.3 (Panther), programmers were themselves responsible for writing the controller layer to mediate between the model and view layers. Cocoa Bindings eliminates the need to reference a nib file's user interface objects by IBOutlet, to manually extract values from them, and to manually set values in them. In the new Controller Layer mediation is done by Key-Value Observing, Key-Value Coding and Key-Value Binding. There are several new classes including NSObjectController and NSArrayController.
The Model-View-Controller paradigm assigns three distinct roles to the objects in an application. The model supplies the data, the view presents the data to the user, and the controller mediates between the two. Key-value observing allows objects to observe other objects. View objects observe controller objects that in turn observe model objects. When the state of an object changes, key-value observing provides details of the changes to observers. It is then the responsibility of the observers to decide what to do with that information.
When we build the new Currency Converter using the Cocoa Controllers palette of IB, there will be no need for a 'Convert' button in the Aqua interface because the controller layer automatically performs the conversion and updates the data display.
Tutorial: Currency Converter With Bindings
I have rewritten the tutorial because the version on the Xcode CD had errors. I am referring to the CD accompanying 10.3. Errors were corrected later in the version you can download from the Apple Developer Connection (ADC) site and you may have it installed already. I have attempted to simplify the tutorial here. Download the full version from http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Tasks/ccwithbindings.html

Below is a view of the application in action. Notice there's no button to click.

The NewCurrencyConverter application with no button!
Create a New Project
1. Start the Xcode application. Choose New Project from the File menu.
2. Select Cocoa Document-based Application in Xcode's project Assistant window, and click the Next button. Document-based applications have built in support for File > New to create a new document.
3. Enter the project name "NewCurrencyConverter" and a destination folder.
4. Open MyDocument.nib in IB by double-clicking its icon in Xcode. (In left panel of Xcode click the Nib Files disclosure triangle).
Build the User Interface (View)
If you are not familiar with IB you might need some practice on tutorials such as those on my web site.
5. After deleting the 'Your document contents here' message, drag three text fields and three labels from the Text palette onto the window.
6. Change the three labels to "Exchange Rate", "Dollars to Convert", and "Amount in Other Currency".
7. Arrange the objects and change their labels and the window title to match the picture above.
Create the Models
8. Select the Classes tab in the MyDocument.nib window.
9. Select NSObject and choose Subclass NSObject from the Classes menu.
10. Change the name of the new class to "NewConverter".
11. With NewConverter class selected in the MyDocument.nib window, choose Create Files for NewConverter from the Classes menu.
12. Switch to Xcode, select NewConverter.h and click the Show Editor button in the toolbar. Add the following declarations of instance variables (dollarsToConvert and exchangeRate) and method (amountInOtherCurrency) to NewConverter.h:

/* NewConverter */
#import <Cocoa/Cocoa.h>
@interface NewConverter : NSObject
{
double dollarsToConvert;
double exchangeRate;

}
- (double)amountInOtherCurrency;

@end

13. Add the following amountInOtherCurrency method implementation to NewConverter.m.

#import "NewConverter.h"

@implementation NewConverter

- (double)amountInOtherCurrency {
return (double)(dollarsToConvert * exchangeRate);
}
@end
Bindings
Cocoa Bindings provides a class method you can use to trigger a change notification to update a dependent key. You can set up a dependency between changing the values of the dollarsToConvert and exchangeRate keys to trigger an update of the value of the amountInOtherCurrency key. You typically set up this dependency in the initialize class method belonging to the model class:
14. Add the implementation of the +initialize method to NewConverter.m:

#import "NewConverter.h"
@implementation NewConverter

+ (void)initialize {
static BOOL tooLate = NO;
if ( !tooLate ) {
[NewConverter setKeys: [NSArray arrayWithObjects:@"dollarsToConvert", @"exchangeRate", nil]
triggerChangeNotificationsForDependentKey:@"amountInOtherCurrency"];
tooLate = YES;
}
}
- (double)amountInOtherCurrency {
return (double)(dollarsToConvert * exchangeRate);
}
@end
The tooLate condition is there to prevent the initialize method being called more than once.
If you want to get the documentation for the method setKeys:triggerChangeNotificationsForDependentKey: first in Xcode choose Help > Show Documentation Window. Then locate 'triggerChangeNotificationsForDependentKey' in NewConverter.m and Option+ double click it.
Instantiate NewConverter
Add your model, an instance of NewConverter, to the nib file:
15. Click the classes tab in the MyDocument.nib window in IB.
16. Select NewConverter and choose Instantiate NewConverter from the Classes menu.

Add a Controller
This is where you Instantiate an NSObjectController. The NewCurrencyConverter application has a single model object, so NSObjectController is the appropriate choice of controller. Add an object controller as follows:
17. Drag NSObjectController from the Controller palette to the nib file window. You may have to click the extension to the toolbar to see the Cocoa Controllers palette. It has three green objects on it. Choose the middle of the three.

Drag the middle object to the MyDocument.nib window
18. You can rename the controller by double-clicking its label in the MyDocument.nib window, and entering a new name. For this example, the object controller's name is left unchanged.
Establish the Model-Controller Relationship
¥ Connect the controller to the model object, the instance of NewConverter you created above, as follows:

Control-Drag a connection from the Controller to the Model instance
19. Control-drag a connection from the object controller to the NewConverter object in the MyDocument.nib window.
20. Connect the controller's only outlet which is called 'content'.
Controllers provide a two-way binding between model and view objects by tying together the data represented by both so that changes to the data in either object changes the data in the other object.

The most important attributes of a controller are the model keys it exposes to the views. These keys match the keys in the model class with which the controller is associated.
¥ Expose three model keys as follows:
21. Select the object controller in the nib file window and display the Attributes pane in the Info window.
22. Click the Add button. Change the newKey to dollarsToConvert. Similarly create keys for exchangeRate, and amountInOtherCurrency. (You can paste these keys copied from the initialize method above to help avoid typos).

Change newKey to amountInOtherCurrency

Bind Views to Controllers
To bind a user interface element, simply select it and display the Bindings pane in the Info window. The entries are explained below.

Binding exchangeRate field to the NSObjectController
Every binding provided by Cocoa Bindings includes at least three aspects: Bind to, Controller Key, and Model Key Path. The Bind to aspect identifies the controller to which the binding is attached. The Controller Key aspect specifies the access point in the controller object that the binding uses to get and set data. The Model Key Path aspect specifies the key path in the model object to which the binding applies. In this example, you need to bind the text fields to the corresponding model keys. Follow these steps to bind each text field:
23. Select the first text field, display the Bindings pane in the Info window (Command + 4) and click the disclosure triangle next to the 'value' binding.
24. Choose NSObjectController from the Bind to menu.
25. Choose selection from the Controller Key menu.
26. Choose the exchangeRate key from the Model Key Path menu. Refer to the picture above.
27. Select the second text field and do much the same in the Bindings panel except choose dollarsToConvert for the Model Key Path.
28. Similarly choose amountInOtherCurrency for the third text field.
29. Save IB. You may get a warning that the saved file will not work on pre Mac OSX 10.2. Choose the post 10.2 option. Return to Xcode and save. Notice there are no (IBAction): or (IBOutlet): statements in NewConverter.h and .m files. Their role has been taken over by the bindings in this case.
Build and Run
When you build and run the application and you should have a fully functional currency converter. Enter data in the Dollars to Convert and Exchange Rate text fields. When editing either of those fields has finished, the views send messages to the controller causing Cocoa Bindings to reset values of the dollarsToConvert and exchangeRate instance variables and also to update the value of the amountInOtherCurrency method. The dependency between these model keys was previously set up in the initialize method.
To improve the user interface add some formatting to the NSTextField:
30. Return to IB and select Cocoa-Text in the Palette panel. Drag a NSNumberFormatter to the exchangeRate field. Choose the 99.99% format.
31. Similarly drag a formatter to the dollarsToConvert field and choose a dollars format. For the third field choose a two decimal place number format. Save IB, return to Xcode, rebuild and run.
Acknowledgements
I have relied on the document "ccwithbindings.html" downloaded from the Apple Developer site and "ControllerLayer" documentation installed from the Xcode disk. Errors and lack of clarity are my own. In a later article I intend to work through the Enhanced Currency Converter with a very impressive master/detail display implemented without too much pain using Cocoa bindings. There are several excellent tutorials and articles on bindings in the CocoaBindings/Concepts/ folder of the documentation.

Back to the List of Tutorials