AppleScript_ The Definitive Guide - Matt Neuburg [249]
The reason Cocoa keys in the sdef are so crucial is that Cocoa scripting uses key-value coding to find its way through your code. Key-value coding (or KVC) is an informal protocol that takes advantage of Objective-C's dynamism and introspection. It uses a string as a key to hunt for names among your instance variables and methods. The object model is navigated by way of a path leading down from the application class. At every step of a path, your classes must be KVC-compliant (meaning that the right instance variables or methods are present) or things won't work.
In our application, so far, there is just one simple little path. The scriptability framework will start with the application class. It has a person element whose Cocoa key is "persons." So the framework looks in MyObject to see if it is KVC-compliant with respect to to the key "persons." Is it? Yes, because it has an instance variable named persons. That instance variable is an NSMutableArray; that's a built-in class which is itself KVC-compliant. The contents of this NSMutableArray are Person objects; that fits with the Cocoa key for this class, which says that everything in persons should be a Person. And Person is KVC-compliant with respect to "name," because it has a name accessor method and a setName: accessor method.
We build our application and run it, and point Script Editor at it, to test our scriptability—and it doesn't work! For example, we say:
tell application "Pairs"
get person 1
end tell
and we get an error message in the console:
[ compliant for the key persons The reason is simple: the very first step in the path is incorrectly set up. As our sdef says, the application class's Cocoa key is "NSApplication." But we want the path to start in MyObject, not in NSApplication. We must not change the Cocoa key for the application class; rather, we need a way to tell the scriptability framework to jump from NSApplication to MyObject as it descends the path. One very simple way to do this is to make MyObject the delegate of NSApplication; you can specify this by a connection in the nib. We must also write some code in MyObject announcing that it, as the application delegate, implements certain keys: - (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key { if ([key isEqualToString: @"persons"]) return YES; return NO; } We add that code to MyObject, which is now also NSApplication's delegate. We build and run the application, and we test it in Script Editor; lo and behold, it works! tell application "Pairs" count persons -- 2 name of person 1 -- "Jack" name of person 2 -- "Jill" name of every person -- {"Jack", "Jill"} name of every person whose name ends with "k" -- {"Jack"} exists person "Jack" -- true exists person "Matt" -- false delete person "Jack" count persons -- 1 get name of person 1 -- "Jill" set name of person 1 to "Mannie" name of every person -- {"Mannie"} end tell This shows the advantage of starting with an application framework. We've added to an existing application no more than a couple of lines of code and a dozen lines of dictionary, and presto, we're scripting our application. We can get and set a property; we can count elements, delete an element, and test by property for the existence of an element; we can even use a boolean test specifier. Having achieved this initial intoxicating success, we should consider some improvements and refactoring before proceeding any further: Better accessors So far, our code lends itself more or less by accident to KVC. For example, access to the person element is possible only because there happens to be an instance variable called persons in MyObject. We should implement our accessors in a more deliberate fashion, in accordance with the expectations of key-value coding and the scriptability framework. Separate accessors It will be wise to separate