How to add some Applescript to your application. This is by no means a comprehensive tutorial, but only a guide I built first for myself and wanted to keep it handy for myself and share it with others. There’s not a lot of information on this topic on the web.
It’s super complex so make sure you do it exactly right. It might take a few test apps to understand it well.
You can use a category of your Cocoa object’s base class, because that is simple to handle. Then you can tell your object to “do something” and the implementation of the script can be wildly different than the standard internal methods. But remember there can be no new ivars in the category! This means if you need to get values from the main class then you will need to have a lot of getters/setters in the main class.
@interface MYObjectClass (MYObjectCategoryName)
First example is how to make a method do something.
The method must take a NSScriptCommand value. Don’t put any other parameters here, the NSSC will pass in a bunch of variables, as many as you want, as an NSDictionary.
In your method, do something like this:
NSDictionary* theArgs = [command evaluatedArguments];
NSString *inputText = [theArgs objectForKey:@"theText"];
NSLog(@”passed text = %@”, inputText);
1. The arguments (theArgs) is the standard way to get all the passed in arguments. Your Applescript could look like
doThisThing first name “Dick” last name “Jones”
2. In the method then you have to get each part of the parameters into an ObjC variable. They pass in as one Dictionary. So you need to call out each key from the dictionary. There will be a line for each variable you passed in. Such as:
NSString *theFirstName = [theArgs objectForKey:@"firstName"];
NSString *theLastName = [theArgs objectForKey:@"lastName"];
You can see that the KEY name does NOT have to match any particular variable name in your code. You can pass about variable names as you normally would in ObjC. But you will need to match up the key name in the SDEF file later.
3. The SDEF file is the Rube Goldberg-esque construct that will link Applescript to Cocoa. Use SDEF Editor if possible, but it’s also handy to edit the XML directly. Add a Suite for your application, and give it a code. Adding the Suite is pretty basic. There’s no direct link from the suite to any particular class files.
For simple applications, you would add just one suite for all your commands, like “MyApp Suite”. Extremely complex applications (think InDesign, Photoshop, MS Entourage) can have multiple suites, because each one does something different. For example, InDesign has a suite just for Layout (the primary way to manipulate page elements), and also one for Color (to create and edit swatches, colors, gradients etc), and about 18 others.
Now there are several conceptual levels in the SDEF. The CLASSES will let you designate one of your ObjC class files as something scriptable. You can give it a name that is different from the actual Cocoa class name in your code/project. So you can have “rectangle” as the script object but the class it works on is “CPRectangle”.
4. Add a command to the class object. Give it a name. This name will be the same as the name used for Suite > Commands. It must be the same in order to link up. Set the Cocoa Method to what method this actually runs, like: -(void)pavsFunc:(NSScriptCommand *)command. See image below.
So far we’ve simply added information to the SDEF that says “class rectangle responds to a command “rotate” and it calls the actual Cocoa method “pavsFunc:”
5. Now we have told the class what command it can understand, we have to add a part that tells Applescript what commands are available. Under the Suite > Commands folder, add a new command. Give it THE SAME NAME as your command for the class. Give it an interesting code, and some descriptive text so you know what it does. I think there’s a limit to the amount of text that can be displayed. Since we are working on a 1 to 1 relationship with this command, don’t add anything for Cocoa name or class here. (Cocoa Name and Cocoa Class are used for verb-first scripting; what we are making here is object first scripting.)
6. A direct parameter *must* be added in order for the command to be used by a class. So for example, the command “invert selection 1″ has the direct parameter of “selection 1″, the command is “invert”. It must be the class for the command or superclass.
7. Parameters are the other values passed to the command. The name is what you’ll call it in the actual Applescript. The code should not conflict with other codes nor Apple’s. The type is what kind of data this is, and may include objects of the same or other classes, not only text or numbers. Description is obviously for humans to read and for your documentation. Cocoa Key (see image below) is the value that will be used to make the dictionary of data that the Cocoa method must pull out from the Argument, like this: NSString *theFirstName = [theArgs objectForKey:@"firstName"]; (and in the Name part call it “first name”, ie: doThisThing first name “Dick”)
You can add any number of parameters and some can be optional. Be sure to handle optional parameters properly in the Cocoa code.
It’s not easy to have a command without a direct parameter– that requires whole new classes (look up verb-first in the Neuberg book). Really every command is handled inside of some “tell something…” block. The TYPE of the direct parameter is the name of the class in SDEF editor. Your class “CPRectangle” may be named “rectangle” in SDEF, so pick that from the popup list.
The commands should be good now.
The flow of command logic is something like this, in a very abstract and non-technical way:
1. An Applescript command is given by a script or editor.
2. The script calls a certain function/command you defined, along with any of its indirect parameters.
3. The command name then matches itself to the class command by name, and then the parameters dictionary is passed into the Cocoa method of the class object.
4. Then the Cocoa method is called and fired. If a dictionary was passed in, then the Cocoa method has to extract each dictionary item by NAME (ie Key) only, and then can do something with it, like setting a variable to the key value it scanned.
5. Potentially the Cocoa method returns something and that gets passed back to Applescript. That’s outside of my current tutorial at this point.