AppleScript_ The Definitive Guide - Matt Neuburg [256]
What we do want copied into the built application is a resource file containing the dictionary, along with scriptSuite and scriptTerminology files to implement Cocoa scriptability (see "Dictionary Formats" in Chapter 3). To arrange for this to happen automatically, choose Project → New Build Phase → New Shell Script Build Phase. The info window for the run script phase will appear; in the script field, enter this Unix code:
/usr/bin/sdp -fast -o "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/Contents/Resources"
"$SOURCE_ROOT/searchTidBITS.sdef"
When you build and run the application, you'll find that it runs normally; if you examine its dictionary in a script editor application, you'll find that the SearchTidBITS Suite is present and that there are three new application properties (search text, search title, and search author) and a new command (do search).
Now let's add implementation code. We'll need a place to put it, so we'll create a new Cocoa class. (I assume you know how to do this, so my instructions will be very abbreviated.) Open MainMenu.nib and, in Interface Builder, create a new NSObject subclass called MyObject, instantiate it, and make the instance the application delegate by making the Cocoa connection between the File's Owner and MyObject. Now save MyObject into the project. Save, and quit Interface Builder. Back in Xcode, here's the implementation code for MyObject:
@implementation MyObject
- (BOOL) application: (id) sender delegateHandlesKey:(NSString*) key {
NSLog(@"handles key? %@", key);
if ([key isEqualToString: @"searchText"])
return YES;
if ([key isEqualToString: @"searchTitle"])
return YES;
if ([key isEqualToString: @"searchAuthor"])
return YES;
return NO;
}
- (NSAppleEventDescriptor*) doAS: (NSString*) s {
NSAppleScript* as = [[NSAppleScript alloc] initWithSource:s];
NSAppleEventDescriptor* d = [as executeAndReturnError:nil];
[as release];
return d;
}
- (NSString*) searchText {
NSString* s = @"tell current application "
@"to get content of cell 1 of matrix 1 of window \"search\"";
return [[self doAS:s] stringValue];
}
- (void) setSearchText: (NSString*) t {
NSString* s = [NSString stringWithFormat: @"tell current application "
@"to set content of cell 1 of matrix 1 of window \"search\" "
@"to \"%@\"", t];
[self doAS:s];
}
// ... and so on ...
@end
The accessors for "searchTitle" and "searchAuthor" are omitted for brevity; you should be able to write them easily. (They are exactly the same as the accessors for "searchText" except for the names, and except for the cell numbers, which are 2 and 3 respectively.)
This implementation works around the problem of communicating from Objective-C code to AppleScript code by not even trying to do so. Instead, we communicate with the interface. We know that our AppleScript Studio application is scriptable through the native AppleScript Studio commands, so we use them directly, just as we do in our AppleScript code, to drive the interface. We can do this readily; the current application is SearchTidBITS itself, so we are sending a message to ourselves. But this is still a skanky solution: instead of sending a message from one region of code to another, we are using the interface as a kind of drop box. We can't tell our AppleScript code to set its internal textSought, titleSought, and authorSought globals, so we content ourselves with leaving the corresponding values in the interface, where the AppleScript code will find them later.
So now let's tell the AppleScript code to find them, by implementing the do search command. This command doesn't take a direct object, so we'll implement it using verb-first dispatch. (See the earlier section "Cocoa Scripting" for the other way of implementing a command, object-first dispatch.) It works like this: in the sdef, you declare a Cocoa class representing your command; in your project, you create an NSScriptCommand subclass with the same name. We've declared in the sdef that