Write the Code

Back in Xcode, open main.applescript. This is the script that will run when our Action runs. It has been filled out with a skeleton handler, like this:

on run {input, parameters}
return input
end run

The variable input is basically the output from the previous Action in the Workflow, the data on which we are to operate. We do not have to return the same value, and what we return does not have to be a variable named input, but we should return something to function as our Action's output. In this case, we'll be expecting text as our input, and we'll be returning text as our output.

The variable parameters is an AppleScript record containing, among other things, the bound values from the interface. You'll recall that we have just one such bound value, called n; our first task should therefore be to fetch it:

on run {input, parameters}
set whatN to (get n of parameters)

Then we simply generate a string consisting of every nth word, and return it.

on run {input, parameters}
set whatN to (get n of parameters)
set L to (get words of input)
set output to {}
repeat with ix from 1 to (count L)
if ix mod whatN is 0 then
set end of output to item ix of L
end if
end repeat
set text item delimiters to " "
return (output as string)
end run

So much for the script. Save the script and close its window.

Edit the Plist

When our Action is loaded into Automator, Automator will fetch a lot of information about it from its Info.plist file. So our next task is to edit the project's Info.plist. You might be tempted to do this by double-clicking on Info.plist in the project window and editing the Info.plist as a text file, but I strongly advise against that, because if you make just one mistake in the XML of this file, your Action won't load and you won't know why. Instead, to ensure that your XML is valid, edit the file with Property List Editor. The simplest way to do this from within Xcode is to Control-click on Info.plist and choose Open With Finder from the contextual menu.

Editing the plist is rather tricky and extremely important. If we get this wrong, our Action will behave oddly or won't appear properly within Automator. Some entries in the plist are required; others are optional. For this example, I recommend deleting the optional entries. Some entries have default values you can simply accept; others must be edited if your Action is to work properly. You can learn more about the meaning of the plist entries from the file ...documentation/AppleApplications/Conceptual/AutomatorConcepts/Articles/AutomatorPropRef.html on your hard disk. For now, just imitate the settings shown in Figure 6.

Figure 6. Editing the plist

Here are comments on some of the settings in the plist:

AMAccepts and AMProvides specify the input and output data types, respectively. These data types are probably the most poorly documented aspect of the entire process. I figured out what values to provide here by studying the Info.plist files inside some of the preinstalled Actions. You should leave the Container as List even if your script does not expect or produce a list. The Types value com.apple.applescript.text-object indicates that our Action expects and produces text.

AMCategory and AMApplication are pretty much arbitrary, but they are used by Automator's interface in searching and filtering the Actions list. Here, I've chosen to ally our Action with TextEdit. By specifying AMIconName as TextEdit, I've even given our Action the TextEdit icon; feel free to include your own icon in the bundle and specify it here.

AMDSummary will be displayed in the Automator interface, so it should be a reasonably informative cue to the end user as to what this Action is for.

CFBundleIdentifier is arbitrary, but it should be unique to this Action. I've used my last name and the name of the Action to help guarantee this uniqueness.

AMDefaultParameters is absolutely crucial. Despite its name, it does more than specify a default value; it ties together the interface and the script. For every bound value in the interface, you must provide an entry in this dictionary whose name matches both the key you specified in Interface Builder and the specifier you use in your script to refer to the corresponding item of the parameters record. If you don't do this, your script won't be able to access the values from the interface! The value of each entry should be a default, which will appear initially in the interface; here, I specify 2, with the result that both the NSStepper and the NSTextField in the interface will be set to 2 when the user first sees our Action.

Once you've edited and saved the Info.plist file, you must also edit the localized strings file. If you don't, your Action will not appear correctly within Automator. (This fact is poorly documented; when my first Action didn't show up properly in Automator, I had quite a hard time tracking down the problem.) In this case, since my default language is English, this file is listed in Xcode under the name InfoPlist.strings (English). You can reduce this file to a minimum, as shown here, but you must not empty it completely:

Testing

Testing your Action is very easy and extremely cool. Simply click on the Build and Run button in Xcode, and presto, Automator starts up--and, if all has gone properly, it contains your Action! When you select your Action, Automator displays the information for it, including its name, its icon, the AMDSummary, and the input and output data types.

Figure 7. The information for our Action, as displayed within Automator

Now create a Workflow that actually uses your Action. When you have dragged your Action into the right side of the Automator window, its interface will appear; check that this looks as you expect, and that you can interact with it properly. Finally, run the Workflow, to see whether your Action behaves correctly.

When you're testing your Action within Automator, Automator itself is a subprocess launched by Xcode. This means that if you want to make a change in your code (or any other aspect of your Action), you must quit Automator in order to retest. This may sound very painful, as you will lose the Workflow that you're using as a test sequence and will have to re-create it manually when you test your Action again. However, that's not entirely so. Simply save your Workflow as a file from within Automator before quitting it. Then, when Automator starts up again, choose your Workflow from the File -> Open Recent menu. You will immediately be able to run the very same Workflow--using the revised version of your Action.

For debugging, you can use the standard repertory of devices. The display dialog command works from within Automator. Uncaught errors are reported from within Automator. Values written out with the log command appear in the Xcode console. You cannot, however, set a break point and then use Build and Debug to test your Action. (Well, you can, but the Action won't pause at your break point.)

When your Action is finished, choose Project -> Set Active Build Style -> Deployment, clean the target, and build one last time. You now have an Action file that can be distributed to other users, who can install it in ~/Library/Automator in order to use it the next time they run Automator.

Onward and Upward

Once you've created your first Action, the documentation will make much more sense to you, and you can start exploring it to discover some of the advanced things you can make an Action do. There are two main areas you'll want to explore.

You might want to make your Action's interface more lively and interactive. To do so, you supply a secondary script, with which your interface communicates through event messages of a sort familiar from AppleScript Studio. (You can see these listed in the Interface Builder inspector, in the AppleScript pane, under Automator Action View.) You can even bypass the use of Cocoa bindings and script the linkage between the interface and the values to be handed off to your main script. (Study the Get Process Information example on your hard disk.)

Through settings in the plist, your Action can be made to perform a number of interesting tricks. Particularly intriguing is the use of AMCanShowWhenRun, which in our example we set to No. When this value is set to Yes, your Action's interface in Automator is displayed with an Options disclosure triangle that reveals a Show When Run check box; if the user checks this, then when the Workflow actually runs, it pauses just before it comes to your Action, and presents your Action's interface in a separate window so that the user can interact with it in real time before proceeding.

Also, don't neglect to study the included example Actions. And, as I mentioned earlier, it can be useful to study the Info.plist file inside an existing Action in order to understand how its values affect the Action's behavior.

As you've seen, it's easy for an AppleScript programmer to create an Action for Automator. An Action is a great way to package functionality so that end users can take advantage of it without knowing any AppleScript. Actions should prove to be great way for you to distribute scripts for others to use.