Vermont Recipes (1st Edition) Errata

 

By: Bill Cheeseman
April 8, 2003

This page lists corrections and changes to the text and code of the First Edition of Vermont Recipes and additional information that may be useful to readers, as of April 8, 2003. This page is no longer being updated.

Items that contain new material of general interest, as opposed to correction of typographical errors, are marked with an asterisk ("*").

Recipe 1: A Multidocument, Multiwindow Application

Pages 32-33—Apple's Cocoa Developer Documentation was substantially updated again in November 2002, shortly after Vermont Recipes was published, and it has been updated again since then. [2002-11-25, 2003-04-08]

Page 47—In the preamble to paragraph no. 1, change "MyDocument.nib" to "VRDocument.nib". [2002-12-28]

Page 103—In the text below the writeToFile: method, change two references to a "dictionaryFromMemory" method to "dictionaryFromModel". [2003-04-08]

Page 111—In paragraph no. 1, change the last statement from "checkboxValue = value" to "checkboxValue = inValue". [2002-12-28, 2003-04-08]

Page 119—In paragraph no. 3, the statement's closing square bracket is omitted. The third line of the statement should be "object:[self document]];". [2002-12-28]

Page 120—In the last paragraph at the bottom of the page, change "MyWindowController.m" to "VRWindowController.m".

Pages 131-132*—In this introduction to Cocoa's validateMenuItem: method, a menu item's localized title is used to distinguish it from other menu items. Later, in Recipe 13, Step 2, you will learn how to use a more common technique, assigning unique tags to each menu item, which allows you to avoid using localized strings. A still more common technique is to test against a menu item's associated action method, which allows you to avoid the maintenance hassle of keeping track of the tags. For example, the first branch of the test at page 131 would be "if ([menuItem action] == @selector(saveDocument:))". This is easy when you wrote the action method yourself. When the action method is built into the Cocoa frameworks, however, you have to find out what it is. You can usually learn what it is by looking at the action method connected to the menu item in the Interface Builder template used to create your project—in Vermont Recipes, the Cocoa Document-based Application template. In the example given, looking at the Connections pane of the NSMenuItem Info Panel for the Save menu item in the File menu in MainMenu.nib reveals that it is connected to NSDocument's saveDocument: action method. The Info Panel lists all of the other action methods available for documents, as well. [2003-04-08]

Page 149—In the first line of paragraph 12, change "compile for development" to "compile for deployment". [2003-04-08]

Recipe 2: Buttons

Page 161—The code described as being added in paragraphs nos. 4 and 6 is actually replacing code automatically provided when the files were generated. [2002-12-28]

Page 181—In the last line at the bottom of the page, change "after updateCheckbox:" to "after updateTwoStateCheckbox:". [2002-12-28]

Page 186—In the code block near the top of the page, change all three instances of "[self mySettings]" to "[self buttonModel]". [2002-12-28]

Page 186—In line 4 of the updateAllPegsCheckbox: method, change "[self updateMixedCheckbox:" to "[self updateMixedStateCheckbox:". [2003-04-08]

Page 203—In the code block just below the middle of the page, the second statement incorrectly combines two statements, omitting several words. Replace the second statement with these two statements: [2002-12-28]

[self setRockValue:[[dictionary objectForKey:VRRockValueKey] boolValue]];

[self setRecentRockValue:

      [[dictionary objectForKey:VRRecentRockValueKey] boolValue]];

Page 217*—In the partyAction: method, the party value is first set to "[sender selectedTag]", but the switch statement uses "[[sender selectedCell] tag]". It would be better to change the switch statement to use "[sender selectedTag]", as well. The selectedTag method returns -1 if no cell is selected, and the case statement will therefore bypass all of the listed cases, as it should (you could catch it in a case labeled default, if desired). As written, if no cell is selected, the selectedCell method will return nil, the tag method may therefore return 0, and case 0 in the switch statement may erroneously be executed. In the partyAction: method, this doesn't matter, because it is a radio button cluster's action method and one of the radio buttons is always selected. [2003-04-08]

Page 219—In the description method, all lines beginning "/n/t" should be changed to "\n\t". [2002-12-28]

Page 221—The second-to-last closing curly brace is omitted from the definition of the VRPartyValue method in the middle of the page. [2002-12-28]

Page 221—The statement at the bottom of the page is incorrect. Change it to "[self setPartyValue:[[dictionary objectForKey:VRPartyValueKey] VRPartyValue]];", calling the VRPartyValue category method listed in the middle of the page. [2002-12-28]

Page 230—The similar statement in the middle of the page is also incorrect. Change it to "[self setStateValue:[[dictionary objectForKey:VRStateValueKey] VRStateValue]];". [2002-12-28]

Page 239—In line 3 of paragraph 1, change "VRMainWindowController.m" to "VRMainWindowController.h". [2003-04-08].

Recipe 3: Sliders

Page 250—In the first line after the personalitySlider declaration in paragraph 1, change "MyWindowController.h" to "VRMainWindowController.h". [2003-04-08]

Page 255—In the restoreDataFromDictionary: method at the top, change the trailing colon to a semicolon. [2003-04-08]

Page 279—In the last line of paragraph 9, change "VFRQuantumValueKey" to "VRQuantumValueKey".[2003-04-08]

Recipe 4: Text Fields

Page 290—The three set methods on the page use notification variables named VRIntegerValueChangedNotification, VRDecimalValueChangedNotification, and VRTelephoneValueChangedNotification. Change these to VRTextFieldModelIntegerValueChangedNotification, VRTextFieldModelDecimalValueChangedNotification, and VRTextFieldModelTelephoneValueChangedNotification, respectively, to correspond to their declarations on page 298. [2002-12-28]

Page 294—The incorrect VRTelephoneValueChangedNotification variable is also used twice on this page. [2002-12-28]

Pages 298, 300, 304, and 305—Several #pragma mark directives are incorrectly commented out. This error is left over from a version of the Developer Tools in which a bug prevented the use of these directives in headers. [2002-12-28]

Page 305—In the restoreDataFromDictionary: method near the bottom, change the trailing colon to a semicolon. [2003-04-08]

Page 306—In the description method, change the three lines that end with "\n" so that they end with "\". [2003-04-08]

Page 312—In the second-to-last and last lines, change "of the Quantum Electron State slider" to "of the High button". [2003-04-08]

Recipe 5: Text Field Extras: Alert Sheets

Note on curly quotes and string constants*—Starting with Recipe 5, Vermont Recipes sometimes uses "curly" quotation marks (sometimes referred to as "smart" or "typographer's" quotation marks) in text that will appear on screen; for example, in alert sheets. Vermont Recipes counsels you to insert them in text either by using the Objective-C @"..." compiler directive or by typing them into the Localizable.strings file. This usage has two problems.

First, the process of producing Vermont Recipes page proofs inadvertently changed all curly quotation marks to straight quotation marks, and we failed to correct a few of them during the copy edit process. If you type the uncorrected passages into Project Builder as they appear in the book, the compiler will interpret an erroneous straight quotation mark as terminating a string, and compilation will grind to a halt when the next quotation mark is encountered. This Errata flags all of these erroneous straight quotation marks that we have found, but you should be alert for instances that we may have missed.

Second, on ASCII or MacRoman systems, a curly quotation mark is an 8-bit, or high-bit, character. It is sometimes said that the Objective-C @"..." compiler directive is designed to work only with 7-bit ASCII-encoded strings; for example, The Objective-C Programming Language, Appendix A, page 208 (Apple Computer, Inc. February 2003). It might be more accurate to say that the compiler interprets the string in a @"..." compiler directive using the default C string encoding of the computer's locale, and that in some locales MacRoman encoding, which can handle 8-bit ASCII, is not the default.

However you explain it, the fact is that the technique taught in Vermont Recipes works correctly (up to a point) only on U.S. and some other systems. On Japanese and many other systems, a curly quote and other high-bit characters like the em dash in an @"..." compiler directive appear as garbage characters on the screen. (For this same reason, a number of NSString methods using C strings will be deprecated in the near future, but the @"..." compiler directive will not be deprecated.) A more subtle error will also occur: the translated value in a Localizable.strings file will not be used, since the key in your code won't match the key in the Localizable.strings file.

Rather than attempt to rewrite every passage in Vermont Recipes that is affected by this error, we will describe a couple of alternative techniques once here in this note. If you are typing the Vermont Recipes application on a non-MacRoman system or you want your copy to be fully localizable, use one of these techniques. See generally the "Creating and Converting String Objects" task in the Strings programming topic in Cocoa Developer Documentation.

1. You can choose simply to do without curly quotation marks. In this case, you must "escape" the straight quotation marks when you type them into Project Builder by preceding them with the escape chararacter, backslash ("\"). For example, @"There are \"straight quotes\" around me".

2. You can use Cocoa methods that encode strings in UTF8 encoding, such as the NSString stringWithUTF8String: class method, or that convert NSData objects to an appropriate string encoding, such as initWithData:encoding:.

3. You can use what is perhaps the easiest technique in most cases, the stringWithFormat: class method with the "%C" placeholder (note that the "C" in "%C" is an uppercase "C") and Unicode curly quotation mark character numbers, 0x201C for the left curly quote and 0x201D for the right curly quote. The same techique can be used for curly single quotes and any other Unicode character. For example, "[NSString stringWithFormat:@"There are %Ccurly quotes%C around me", 0x201C, 0x201D]".

4. You can leave the curly quotes out of your code and also leave them out of the key side of your Localizable.strings file, but do use them on the value side of your Localizable.strings file. In this way, even your English Localizable.strings file will be "localized" to display curly quotes.

Be sure to save your Localizable.strings file in the same encoding as the code files. By default in Mac OS X 10.2 Jaguar, this is "Western (Mac OS Roman)" encoding. However, to make your Localizable.strings file work correctly with curly quotes and other Unicode characters on all systems, use one of the techniques described above and save your Localizable.strings file in the recommended UTF-16 encoding. To do this in Project Builder, select Localizable.strings in the Groups & Files pane, click once in its text in the main editing pane to give it keyboard focus, then choose Format > File Encodings > Unicode™ (UTF-16). [2003-04-08]

Page 324—Remove the two forward slashes commenting out the line #pragma mark INPUT VALIDATION AND FORMATTING. [2002-11-25]

Page 326—In the 6th line from the top of the page, the quotation marks immediately surrounding %@ should be curly quotes, because they are intended to appear as such in the alert sheet. The line should read as follows, or it won't compile: [2002-11-25]

@"“%@” is not a valid entry for \

Pages 328-329*—Although Apple's Cocoa Developer Documentation underwent substantial revision in November 2002 and again in early 2003, the topic Using Alert Sheets still incorrectly describes the roles of the alternateButton and otherButton parameters to the NSBeginAlertSheet function, as well as the use of the NSAlertAlternateReturn and NSAlertOtherReturn returnCode constants. As Vermont Recipes explains and other Apple documentation confirms, the "other" button is typically configured as the Cancel button adjacent to the OK button, and the "alternate" button is the optional third button to the far left in a three-button sheet. If your code does not implement these function parameters correctly, buttons in a three-button sheet may be misplaced and the return code constants may not work correctly in both two-button and three-button sheets. [2002-11-25, 2003-04-08]

Page 332—The speedSheetDidEnd:returnCode:contextInfo: method registers a new undo action every time the user enters a value below the minimum and clicks the "Set 75.0 mph" button. This can result in stacking up multiple undo actions that have no apparent effect. This is harmless, but it could be avoided by adding a test to determine whether the slider is already at its minimum value before registering the undo action. [2003-04-08]

Page 335— In the 2nd line from the bottom of the page, the quotation marks immediately surrounding %@ in two places should be curly quotes, because they are intended to appear as such in the alert sheet. The line should read as follows: [2002-11-25]

"“%@” is not a valid entry for the Speed Limiter." = "“%@” is not a

Pages 335-336—Add a trailing semicolon to each localized string entry. [2003-04-08]

Page 341—Remove the two forward slashes commenting out the line #pragma mark INPUT VALIDATION AND FORMATTING. [2002-11-25]

Pages 346-348*—I have been persuaded that my remarks about circumstances under which pending text field edits should or should not be validated and committed are too rigid. The Aqua Human Interface Guidelines give you considerable flexibility in this regard. Your guiding principle should be to avoid a design that will permit unwitting and inadvertent loss of data. In particular, you should consider finding a way to validate and commit pending edits when the user closes a window or quits the application, instead of abandoning the pending edit, since most users assume then that.what they typed will be saved. [2002-11-25]

Recipe 6: Text Field Extras: Formatters

Page 367—The sheetForDecimalTextFieldValidationFailure:errorDescription: method must be declared in VRTextFieldController.h. [2003-04-08]

Page 367—In the sheetForDecimalTextFieldValidationFailure:errorDescription: method, the quotation marks immediately surrounding 0, 9, and 1,472.34 should be curly quotes, because they are intended to appear as such in the alert sheet. The Localizable.strings file entry on the following page correctly shows the curly quotes. [2003-04-08]

Page 387—In the first two lines of paragraph 4, change "NSFormatter+VRFormatterBugfixes.h" to "NSFormatter+VRFormatterBugfixes.m". [2003-04-08]

Page 391—In the first line of paragraph 1, change "MyTelephoneFormatter" to VRTelephoneFormatter". [2003-04-08]

Page 391—The first line of the makeTelephoneFormatter method should not take a parameter, for the reason given on the following page. Change this line so it reads:

- (void)makeTelephoneFormatter {

[2003-04-08]

Page 394—In paragraph 3, add the following line following the line that imports VRTelephoneFormatter.h:

#import "NSFormatter+VRFormatterBugfixes.h"

[2003-04-08]

Page 394—In paragraph 4, the curly brace marking the beginning of the init method block is missing. Change the first line of the method to "- (id)init {". [2003-04-08]

Page 403—In the fifth line from the bottom of the page, the dash after "index" should be the decrement operator, two minus signs. [2003-04-08]

Recipe 7: Text Field Extras: Undo and Redo

Page 422—Add the following text at the beginning of paragraph 3: "Define the isEditing: method in VRUndoableTextField.m as follows:"

- (BOOL)isEditing {

    return isEditing;

}

[2003-04-08]

Recipe 8: Text Field Extras: Drag and Drop

Page 434—In the third-from-last line in the first paragraph after the bullet list, change "drag the characters 1234" to "drag the characters 234". [2003-04-08]

Recipe 9: Forms

Pages 449-450—In paragraph 6, change all references to "[self VRTextFieldModel]" to "[self textFieldModel]". [2003-04-08]

Page 453—In the second line from the bottom, change "\n\tteleponeValue:%@\" to "\n\ttelephoneValue:%@\". [2003-04-08]

Page 456—In paragraph 2, change the first line of the code snippet to:

else if ((control == [self addressForm])

[2003-04-08]

Page 461—In the third line of paragraph 2, change "VRMainWindowController.m" to "VRMainWindowController.h". [2003-04-08]

Page 462—In the second-to-last line of paragraph 6, change "copy" to "cut". [2004-04-08]

Recipe 11: Table Views

Page 539—The draggingUpdated: method calls abortEditing. Note that a bug was introduced in Mac OS X 10.2 Jaguar whereby calling abortEditing while a cell was being edited could cause a crash. According to the Mac OS X 10.2.3 AppKit release notes, this is now fixed. [2003-04-08]

Recipe 12: Keyed Archiving

Page 555*—Apple substantially updated its documentation on Archives and Serializations to cover keyed archiving in November 2002 after Vermont Recipes was published. [2002-11-25]

Recipe 13: Application Menus

Pages 592-595*—In this discussion of Cocoa's validateMenuItem: method, a menu item's unique tag is used to distinguish it from other menu items. As noted in the comment for pages 131-132, above, a more common technique is to test against a menu item's associated action method. [2003-04-08]

Recipe 18: Alert and Dialog Panels

Page 676—In paragraph no. 4, change "At the beginning of the do loop" to "At the beginning of the if block within the do loop". [2002-12-28]

Recipe 20: Online Help

Page 715—Change the first sentence of paragraph 11 to read as follows: "In the MainMenu.nib nib file, you must connect the Vermont Recipes Help menu item in the Help menu to the First Responder icon in the MainMenu.nib window and select the showHelp: action method. [2003-04-08]

Recipe 21: Ensuring Backward Compatibility for Jaguar Applications

Page 720*—I refer at the bottom of the page to the three different formats in which nib files can be saved in Mac OS X 10.2 Jaguar. Apple has recently acknowledged that a bug causes fatal errors when using the new "10.2 and later format" and "Both Formats" settings in at least some nib files containing a custom class. Until this bug is fixed, you should save your nib files in "Pre-10.2 format" if they include custom classes. A minor side effect of this restriction is that you cannot create the new Jaguar spinning progress indicator directly in Interface Builder. Instead, make it a standard progress indicator in Interface Builder and set its style in your awakeFromNib method. [2002-12-28]

Page 734—Shortly after Vermont Recipes was published, Apple released updated Cocoa Developer Documentation, including a new Programming Topic on Dynamically Loading Code. Consult this topic for techniques that might be useful in resolving backward compatibility issues if your code must use external variables that are new in Mac OS X 10.2 Jaguar. [2002-12-28]

Page 734—Apple published the anticipated Technical Note on backward compatibility issues a few weeks after Vermont Recipes was published. See Ensuring Backwards Binary Compatibility—Weak Linking and Availability Macros on Mac OS X, Technical Note TN2064 at http://developer.apple.com/technotes/tn2002/tn2064.html. Apple supplemented this with Technical Q&A QA1233 on April 1, 2003. [2002-12-28, 2003-04-08]

Page 734—With the December 2002 Developer Tools, the MACOSX_DEPLOYMENT_TARGET environment variable can be added in Project Builder. Due to a bug, it can lead to a lot of linker warnings under certain circumstances, but it appears that these warnings can be safely ignored. [2003-04-08]

—Bill Cheeseman, Quechee, Vermont, April 8, 2003

Vermont Recipes
Copyright © 2003 by Bill Cheeseman. All rights reserved.