Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2005-130
$eventId
ID of event: wwdc2005
$eventContentId
ID of session without event part: 130
$eventShortId
Shortened ID of event: wwdc05
$year
Year of session: 2005
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC05 • Session 130

Writing AppleScript Automator Actions

Application Technologies • 1:14:30

In this hands-on session we will take you step-by-step through creating an Automator Action using AppleScript, AppleScript Studio, and Cocoa Bindings. You will learn everything you need to know to create Automator Actions using AppleScript for your own application.

Speaker: Todd Fernandez

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Good morning. Welcome to session 130. I'm Todd Fernandez, the Apple Script and Automator Engineering Manager, and I'm very pleased to be here with you this morning to tell you everything you need to know about how to create great Automator actions for your scriptable apps using Apple Script. So we have a very simple agenda this morning. This is a hands-on session. I'm going to take about 10 minutes to go through a brief introduction and cover some important background topics that you'll need to understand, and then dive right into our example project. We're going to, together I hope, if you've got your PowerBooks and have downloaded the sample code, build an Automator Action using AppleScript together. And I'll conclude with some pointers to some additional information that will help you learn about building Automator Actions with AppleScript, and then answer your questions. So the first thing I'd like to do is kind of take a brief survey. How many of you have used Automator?

How many of you have used runAppleScript action in a workflow? All right. How many of you have built an action? How many of you have shipped an action to customers? Okay, a couple hands. Well, I hope next year when I ask that question will be a lot more hands.

So we released Automator along with Tiger about six weeks ago and the reviews have been very flattering. I just wanted to highlight one from Andy Anotko. Now, that second sentence, I could spin a really complicated rationale as to why it's up there, but it's really so I have the word "ginchiness" on my slide and so I can say it to all of you. The second thing I'll point out is that if you take away the geeky jargon, which would probably leave you with about this left out of the review, this could have been written by my mother. So you're welcome, Andy, and we love you too.

So turning that around a bit, I really wanted to express my thanks to those developers who've created those over 550 Automator actions that have already been released. Last year, I challenged the developers at the conference to catch the wave and really adopt this great new technology for Tiger. And you really rose to the challenge. And I and the team really appreciate your support.

For those of you who have not come on board yet, but have a scriptable application, you must realize that you have some powerful functionality there that's often hidden beneath the surface, accessible only to that small percentage of users that are comfortable with writing scripts. The advent of Automator gives you a great opportunity to take advantage of that hidden power and expose it to a much wider audience, thereby attracting new customers for your application. And once a customer adopts your application and builds it into their automated workflows, well, you've got a loyal customer because they are not going to want to go back to doing things by hand.

So with that as a little bit of motivation, let's start talking about Automator and what you need to know to create actions for it. So what is Automator? Well, for those of you who raised your hands before, you know, but for the rest of you, it's what we call a personal automation assistant.

And it consists of four main pieces-- the Automator application itself, which is what you use to create workflows, a large set of actions, over 200 that Apple shipped with Tiger, and again, over 550 that you and your colleagues have already created, tools to create actions, and a set of example action projects as well as workflows to give users an idea of what they can do with Automator.

So I've been talking about actions and workflows. And again, this will be review for those of you familiar with Automator. But an action is what you all need to create. And it's a simple piece of functionality that does one specific task. For example, copying finder items from one location to another. So where do users get these? Well, as I've already mentioned, we have over 200 that came with Tiger that Apple created. Third-party software developers are creating them for their applications. And some users who have the necessary programming or scripting expertise can create their own.

So what is a workflow? Well, this is what Automator users create. And it's simply the combination of a series of actions in a linear data flow, with the output of one action becoming the input into the next action. And this is a simple example of a workflow to find some photos with a particular keyword, do some processing on them, package them up, and then email them out to a particular address book group. Now you'll note that our friend copy finder items that I used as an example of an action is in there. And in fact this is simply a workflow consisting of six separate actions.

So that's a little background. So what are we going to learn today? Well, you're going to learn how to build an action using AppleScript. And these are some of the tasks and tools that we're going to use to do that today. You're going to learn how to manage the user interface of your action and, as the user changes the interface elements in it, how you read those values in your scripts in two different ways, using both Cocoa Bindings and AppleScript Studio.

Again, this is a hands-on session. I hope that all of you have had a chance to download the sample code. We've provided you with pre-built projects for each step, from 1 to 6, as well as a final version that has everything in that I'm going to show you today. They do require Xcode 2.1. They're in the new project format. But those of you who have Xcode 2.0 installed can still build along with me if you like. We're going to start from scratch, so you don't need to use the pre-built projects to do this. Another point, and if you're in that situation, there is a detailed outline that lists every step we're going to follow and also all of the code and other materials that you'll need to build the action today.

Just another note on that topic, the milestones are the end point of each step. So the step one milestone is where we will end step one, so it would be the beginning of step two. And you have a couple of options. This is intended to be a hands-on session, so I certainly encourage you, if you've got the sample code or you've got your PowerBook open, to go ahead and build the action along with me. But you can also just open up the different milestones of the completed project and look at the changes that I'm making as I do them, or simply just watch and then experiment with the sample code later at your leisure.

So let's get started on creating an action. So just kind of review again. We're going to be talking about AppleScript actions, and that's one of the types of actions that you can create. You can also create actions using Cocoa. And there's another session this afternoon in this exact auditorium that you can learn about how to create a Cocoa actions. And you can also now, with Xcode Tools 2.1, create them using other scripting languages. And there's a session tomorrow afternoon about how to do that.

And each of these types has a different requirement for creating an action using that technology. If you want to create an AppleScript action, well, the application you want to target needs to have a scripting interface. It needs to be scriptable. If you want to create a Cocoa action, the application or technology you want to access needs to provide you with a set of APIs and a framework that you can link into your action. And if you want to create an action using another scripting language, you'll need to have some kind of command line tool that you can access from that language. One other point is that you don't necessarily need to target an application with your action. You are perfectly able to create an action simply using the features of AppleScript language itself or Objective-C or C++, any of the other programming languages that are possible with actions.

So the technologies and tools we're gonna use today in this session are AppleScript, of course, AppleScript Studio, and there's another hands-on session tomorrow morning about AppleScript Studio if you're interested in that. And we're also gonna use Cocoa Bindings, and there's another session on Cocoa Bindings this afternoon if you're interested in that technology, which is very useful for building actions of all types. And, of course, we use Interface Builder to build our interface for our action and Xcode to manage the project, including a new feature in Xcode 2.1 makes it much easier to configure your actions Info.plist settings, a new action target inspector.

So with that, I'd like to switch to demo two and give you a glimpse of what we're actually going to build together. So you see the end point. So this is Automator in a simple workflow consisting of three actions. We've got ask for finder items, which is one of the actions that comes with Tiger, just to give some input into our action duplicate finder items. And you can see that we have a checkbox that controls whether or not the output of this action is going to be references to the duplicated items or the original items if it's unchecked. We have a destination pop-up that allows us to choose standard locations or choose any location on our disk. And down at the bottom, you can see the example. This isn't the most exciting example. There are much better examples in actions such as rename finder items, but it gives you-- this will show us how to add some more advanced user interface behavior to our action. And just to give you an idea of what this looks like when we run, to show this is a working action, we'll just select a couple of images. Can I make it larger?

Command shift, command option. How's that? Improvisation. So in fact, that's all I really wanted to show you. But actually, we can show you in Finder that, in fact, that copied those-- that duplicate-- where did I put them? In documents. So here are our duplicated images. And how do I turn that off? Because that's going to-- OK.

Again, we'll get to give you a real close-up view of that. That's what we're going to build. So back to slides, please. So our step one, we're going to start from scratch. We're going to create a new action project in Xcode and build a very simple initial version just with one line of AppleScript to implement a simpler version of what I just showed you.

So in order to do that, we're going to create a new Apple Script Automator action project. We're going to write a very simple one-line script to implement the action's behavior. I'm going to show you how to build and test the action, and then give you a tour of what you get out of the box with the minimal changes that I'm going to make to the action template. So with that--oh, I'm sorry. I'll have these layout diagrams throughout the hands-on session to let you know which file we're going to change. In this case, we're only going to change the main.AppleScript file. So back to demo two, please.

Okay, so the first step is to create a new project. So we're gonna bring up Xcode's project assistant. As you can see, we have three different types of actions. Cocoa actions you can learn about this afternoon. Shell script actions tomorrow afternoon. But we're gonna create an Apple script automator action. And we'll even spell it correctly.

I will create our project. And hopefully you can see this a little bit better. And if I can grab the splitter. So just to give you a little tour of what we've got in here, we've got our main.apple script and the scripts group. We've got a number of resources, including a strings file and our main.nib, which we'll edit later to build our actions user interface. Number of frameworks that are linked in. The product that we're going to be building, a.action bundle. And the info.plist.

All we really need to do in this step is to come into our main.apple script and think about what behavior we want to add to this action. So we want to make a duplicate finder items action. And what we might want to do is look up finder's dictionary which we can do right in Xcode, to see what the duplicate command can do.

So again, you probably want to see that a little bit closer. So the duplicate command takes references to the objects to be duplicated and returns references to the duplicated objects and has a number of arguments. And you might notice the to argument, which we're obviously going to use when we get to adding a user interface to our action.

What we get in the main.apple script is the run handler. That is what you need to implement. That is what is actually called by Automator when your action is run in the workflow. And when Automator calls your handler, it passes the input, which is the output from the previous action. So you can access that from your script, as well as a parameters block, which includes all the settings for the user interface elements you have in your user interface, the settings that the user has chosen for your action.

But we don't have any of those yet, because our action at this point doesn't have a user interface. So all we need to do-- is telfinder to duplicate the input to our action. So we'll save that. The next step then is to build and test this action. And what we have set up in the template is a custom executable for Automator to make the process of building and testing your action very similar to building and testing any other application.

And if we open the inspector on that, you'll see it's just pointing to automator.app and /applications. And we have a -action argument, which is added automatically based on the project name to pass the built products for your action to Automator on the command line. So it'll be loaded in, and you can immediately use it and test it in your workflows. So we'll go ahead and build and go.

which will build the action and then launch Automator, passing the newly built action on the command line. And we're probably going to need to zoom in there again. So you can see we've got this funny looking application group up here which holds our action. That's because we haven't yet configured our action to tell Automator which application group it should appear in. Another issue is we don't have the Finder icon here. That's another setting that we need to tell Automator about our action. And if we select the action and look down here in the Try Not to Give You All Motion Sickness. In the description area, we haven't filled out the description to tell the user who might be interested in using this action what, in fact, it does.

And if we add the action to the workflow, you'll see it has no user interface, and the types that it takes as input and output are not configured either. So we've got an action, and in fact, we can-- let me zoom out so I can see here. We can, in fact, use this as it is.

And again, choose an item. And it's already working. It duplicates the item and passes the original back, because we haven't done anything differently than that. And we'll look in pictures, because it's going to duplicate into the same folder. And there's our copy. So just with that one line, we've already got a working action, but of course, it's fairly limited at this point. and we've got a lot of things that we need to clean up in the configuration. So let's go back to slides, please.

So that was step one. We've already learned how to create a project and implement a simple version of it by modifying its main.apple script and how to build and test it. So what we want to do next is add some more options. I'm sorry. We're going to next configure the info.plist to correct those little hiccups in there where the application group is, its icon, its description, et cetera.

So there's a bit more background information here. Automator has a number of keys in its info.plist that each action specifies to tell Automator how to handle this particular action. The ones that I've mentioned so far AM application is what controls which group your action will appear in in Automator's library.

The category is not shown in the user interface, but it is useful for doing searches in Automator search fields. So I encourage you to choose a category. You can find the categories for our actions in their info.plist, or you can create your own for your own sets of actions. The am-icon name controls the icon that appears to the left of the action's name in the action column.

And am_name controls the name of the action that appears in the action column. Now, in Xcode 2.1, the project template has been updated so that it automatically names your action based on the project name you selected, sensibly enough. But you can, of course, change that again later, if you so choose.

So some other important settings are the types, the valid types of input and output that your action accepts and provides. And for those, we have a pair of keys, am accepts, which again specifies what input is accepted. And you can see it highlighted there, the English description that appears in the user interface.

It includes a types array, which allows you to specify one or more types that are valid for your action that it can handle and do something interesting with. Am accepts dictionary also includes an optional Boolean, so you can tell Automator whether or not your action requires input to work properly. Many actions don't actually require any input.

So similarly, there's an am provides key, and this is identical to am accepts, except it doesn't require an optional boolean. And this specifies what the output your action provides to the next action in the workflow. And again, it can be an array if it returns more than one type.

So some guidelines on choosing these. We have documented a number of these types that we use for our own actions in the Automator Programming Guide. And you should certainly adopt those as often as you can so that your action is interoperable with as many other actions as possible. However, it is possible to create your own types. And that's one of the things that we're going to cover in more detail in the COCOAction session this afternoon. Again, you can specify multiple types. For example, if your action accepts both address book groups and people.

Another special case, the view results action that I've been using to test duplicate finder items today, doesn't require any input, but we don't want it to block the data flow through the workflow. So if your action doesn't need any input and doesn't do anything with its input, it should just pass through the input it receives. And you just leave the types as general as possible. The idea here is you want to give Automator the most detailed and accurate information you can so it can best handle your action and give the user the most support and using it properly in workflows.

So I've already mentioned that you can create your own types. If you need to do that for your actions, if your application specifies some types that are different from the standard ones that we've already identified, what you should also do is create a set of conversion actions to convert your new type to one of the existing types.

Conversion actions are a great Automator feature that allows types that don't appear to match in the user interface to, in fact, look like they match to the user, because Automator invisibly and automatically adds in whatever needed actions that it has access to to map from, say, iPhoto photos to files folders. There are automatic conversions to get between those types so that when you add those actions to the workflow, it will appear to be a match.

These actions are just like any other action except for three minor differences. You build them with the same project, but the product needs to be a.c action for conversion action bundle. They don't have any UI because they are never seen by the user unless, of course, you show Automator's log. So you can see when these are actually executing in your workflow, but to the user just looking at the workflow, they're invisible. Automator handles it automatically. And for those of you familiar with AppleScript, This is a very similar concept to AppleScript coercions.

And again, we're going to actually talk quite a bit about this in the Cocoa actions session this afternoon and even show you how to create a conversion action. So what we're going to do in step two, again, is configure our info.plist and fill in some of those missing settings that made our action not look quite right in Automator's user interface. And we're going to do that in two ways-- well, actually three. For those of you who haven't upgraded yet to Xcode 2.1, well, you should. But if you haven't yet, I'm going to show you how you can edit your info.plist in two different ways. And then I'm going to show you the improvements we've made in Xcode 2.1 and made this much easier to do with the new action target inspector. So in this step, we're going to only modify the info.plist. And with that, please back to demo two.

So let's come back to Xcode and stop that. So one way to edit your info.plist is simply to edit it as a text file here in Xcode, and you can certainly do that. For example, we want our action to accept alias objects, not just any object. However, a safer way to do this is to use Property List Editor. And you can do that easily from within Xcode by opening the info.plist with Finder. And we can see all the different keys, some of which I've talked about already and some of which I haven't. But this is the AM accepts dictionary. And let's zoom in.

So we can see we've got our types array with one element, and I've already edited that to be alias object, the optional boolean. In this case, we certainly want that to be no, because this action doesn't make any sense without any input. And we also need to update our am provides dictionary.

to make a similar change because our action is going to also provide alias objects. Let's save those changes, come back to Xcode. So that's the Xcode 2.0 way to edit your info.plist. In Xcode 2.1, if you disclose the target and bring up the inspector and switch to the properties pane, You can see that up here at the top are the standard settings, the CFBundle executable, the CFBundle identifier that you're familiar with from any Xcode project. Automator uses those too. But down here at the bottom, we've added a lot more information for the different collections of Automator settings. And we'll start with the general.

Again, the action name is set automatically for you by the template. But we need to set the application. And again, these map fairly closely to the AM keys in your info.plist. This is AM application. And we want that to be finder. Finder is the application that our action uses to do its work.

We use that for the category as well and for the icon, which you see then appears in the well as soon as you add it in there. These check boxes down here at the bottom control the show action when run option within the workflow, which allows a workflow creator to specify that a particular actions user interface should be shown at runtime so it can be dynamically set by whoever is running the workflow. We'll talk a little bit more about that later.

Then we can go through some of the other collections. Input is again, am accepts, which we've already set. We've got the optional Boolean and the change we've already made. Same with output, but it doesn't have the optional Boolean. The parameters collection allows you to specify parameters to hold the settings for user interface elements that are maintained and managed via Cocoa Bindings. And I'm going to show you how to do that in the next step. Required resources is another entry that allows you to tell Automator that there's a particular application or file or any kind of reference, or resource rather, that your action needs on the system for it to work properly.

The warning setting allows you to tell Automator whether your action makes changes to the data that's input to it that are-- either it doesn't change it at all, which is the safe level. And duplicate finder items is a good example of that. It doesn't do anything to the original files. It merely duplicates them.

or whether your action makes a change in a reversible way, for example, flip image, since you can always flip it back, or in an irreversible way, say, cropping an image, which actually loses data. So by setting this pop-up, you can tell Automator whether your action is safe, and then it can present appropriate warnings to the user when your action is added to the workflow. And if we needed to change this to one of the other settings, we'd need to fill out these other values down here.

So what you need to do is show your target and select it in the Groups and Files column. Let me zoom in there a bit. So again, that's in Groups and Files, the target for your action, and then open the inspector by clicking the Info button. And within the inspector, it's the Properties pane.

Absolutely. So let me zoom out a bit so it's not-- where was I? So keywords. Keywords are another way to make it easy for users to find your action in Automator's library by doing searches for them. So we'll add a few keywords that have to do with our particular action.

The next collection is the description, which we've talked about already, and we see that we haven't configured this. So there are a number of different items that you can add specific to your action that are highlighted differently in Automator's description area. In our particular case, we're just going to add a summary.

I'm going to copy this out from my little assistant, but you can copy and paste this from the outline if you have that open. Moving on, we also have an entry for related actions. If your action is related to a number of other actions, you can specify one or more here, and those actions will be listed in your actions description, making it very easy for users to figure out what actions your action will be useful in conjunction with. And finally, we also make it easy to manage localizations for your action, and I'm going to be talking about localizability in step five.

So with that, we've configured our action. And let's go ahead and build and go to see what improvements we've made and how it appears within Automator. So as we can see-- We no longer have that funny application group at the top. If we click on the Finder group, we see our action appears here. And it has the Finder icon.

Now you'll notice the description still isn't filled out yet. Why is that? Well, you're going to have to wait a bit. I will explain why in a bit. But let's add it to the workflow, and we see that we also now have our types set accurately in the workflow. So we've corrected quite a few of the issues that we identified in our initial tour after step one.

So with that, please back to slides. So again, with step two, we just learned how to configure the actions info.plist file to specify especially the general settings and the types. And I pointed out a few of the other settings that may be of interest depending on your particular action that you're creating.

So in our next step, we're going to actually add some user interface elements to our actions user interface. And these elements we're going to manage by using Cocoa bindings. So before we add anything to our user interface, I wanted to highlight a few important design guidelines and also point out that similarly to the human interface guidelines for applications on Mac OS X and the scripting interface guidelines for your scriptable interface for your application, we also have identified some guidelines specific to action user interfaces. And they are documented in the Automator Programming Guide. The idea here is, again, like the human interface guidelines and the scripting interface guidelines, to help you achieve a consistent look and feel for your actions so that they work just like other actions and are much easier for users to adopt and use. We obviously have over 200 examples of our own that you can use to get an idea of what are good ideas and what are bad ways to build your user interfaces.

As far as overall philosophy, the idea is to keep separate actions separate. Don't try to make these giant actions that do several different things. It makes it much harder for them to be used in conjunction with other actions, and also makes them harder to understand what they do and what are the right settings to use. Really try to keep it simple.

I pointed out the parameters collection in the target inspector, and this maps to the AM default parameters key in your info.p list. These, again, are the default values for keys managed by Cocoa bindings, the mapping of the setting from the user interface element to your script or your code if you're working with a Cocoa action. The types for these parameters map very clearly to different user interface elements. As outlined on the slide, I won't bore you with the details. You're going to see a few examples in a moment.

interface, we're gonna use an automator-specific pop-up menu that we've established to make it very easy to add file, folder, and application choosing to your actions user interface. I'm gonna point out a few relevant design guidelines as we go. We'll then connect those elements to our script using Cocoa bindings, add the necessary entries to our parameters, and then add the code necessary to read those settings and make use of them within our actions behavior. So in this step, we're gonna be modifying-- whoop, sorry about that-- modify three files. We're gonna start by modifying the nib, then add the entries to the info.plist, and finally add the code to your main.apple script. So please, over to demo two.

Oops. All right, so let's open our nib file. And this is what you get out of the box. As you saw in Automator, we've got this little placeholder element here. So we'll just take advantage of that. And I'll make this so you can see it. I'm going to build this interface a little bit differently than what I've been showing because it occurred to me yesterday that it makes much more sense with this checkbox on the bottom.

I'm not sure how well it's gonna work zoomed in. So one of the things we need to do-- this is one of the design guidelines-- is that actions should use small controls. And by default, we get a regular-size checkbox. So in the inspector, which you can get by Command-1 or from the tools, Show Inspector, We want to change the size to small, and as we see that changes the size of the checkbox. Then we want to add the directory pop-up chooser, and that again is the automator specific version of it that we've added in Tiger. And to do that we need to add the automator palette. So we need to go to tools, palettes, palette preferences.

and click the add button. Forced me to navigate back up to developer, extras, palettes, and choose ampalette.palette. As you see, that immediately gives us auto up here in the IB palette. Absolutely. So Tools, this is again within Interface Builder. The Tools menu, select the Palettes item, and then the Palette Preferences item within that submenu. That will bring up Interface Builder's preferences with the palettes pane selected. then you need to click the Add button, and in that open panel, navigate up to Developer, Extras, Palettes, and select ampalette.palette.

So what we want to do here is allow the user to specify the directory into which the items will be duplicated. So we're going to choose the middle one here, drag it out to our interface, make sure we get it lined up with our label. and then use the IB guidelines to line it up with our checkbox.

And in the attributes for our pop-up, we're going to make a few changes. For example, we can allow the user to choose new paths. And something more interesting for this particular action, we can allow a placeholder. This will... allow the user to specify, "Well, I don't want to duplicate these items to a different location. I just want to duplicate them in place." And we'll rename that to "same folder" for this particular action.

So one other design guideline we need to do, if you select all the items in the interface and hold down the Option key, you'll see that IB gives you a pixel guide. And one of the UI guidelines is to have a 10-pixel border around the elements within your Actions user interface. This is important because you don't have control over exactly how your action is laid out. You only have control over what your-- the view that you're building here. Automator will load this view-- wow, this is really hard to do zoomed in. There we go.

by holding down the Option key. So let me zoom back out again. I think that was-- OK, there we go. And again, you can select all with Command-A and then hold down Option to see the pixel boundaries. Well, that sent us out again. You all will get them to 10. I will leave that as good enough for now.

Again, the importance is that Automator is going to load your view within the action view, which includes the title bar and the status bar. So for your action to look like other actions and not have obvious gaps, you need to have that standard 10-pixel gap margin around the outside of your interface elements.

So now that we've built the interface, we need to connect it to some variables that we can read from our script using Cocoa Bindings. And to do that, we're going to again use the inspector and bring up the Bindings pane, which you can do with Command 4. And I will zoom in again. For the checkbox, we're going to use the value binding. Whoa, that was pretty cool.

And what we want to do is name the model keypath, the value that we can then read in our script. So let's just name this "return duplicate items." This will become important later because we're going to have to use this exact same name within our info.plist. They need to match, otherwise this won't work. And I will, again, remind you of that later. So we've made the binding for the checkbox and named the variable that we'll be able to read from our script. So now we need to do the same thing for the popup menu so we can read its value from our script.

And for this one, we want to use the path binding. And for that one, let's call it the to location. And again, we'll make that binding. Save our nib file. We've got our interface built. We've made the connections using bindings. And now we're going to return to Xcode.

And again, we can do this with-- if you have Xcode 2.0, you can do this by opening the plist in Property List Editor. opening--excuse me, disclosing the am_default_parameters key, adding a new child, And let's add the to location this way. And that is a string. Again, this is going to receive a POSIX path from that pop-up menu.

And by default, let's just leave it be the placeholder, which means an empty string. So we're in fact done here. Let's save that change. And we'll add the other-- necessary parameter for the checkbox using the inspector. Again, you get to the inspector from, by selecting the target and then opening the inspector.

the Info panel. Selecting the Properties pane, and we need to select the Parameters collection. and add a new entry. So again, this is where it's critical that this exactly match what you've used in your nib so that the values will be transferred accurately. For this one, we'll make it a Boolean. And we'll want the checkbox to default to checked.

All right, so now we have built our user interface in the nib. We've made the bindings. We've added entries to hold the binding values in our info.plist. The last step is to update our main.apple script to actually go ahead and use those settings and do something different depending on what the user has selected. So here I'm going to use my assistant again, but the code that I'm about to paste in here is also in the outline that you have as part of the session materials.

and we'll bring over a new version of main.AppleScript and compile it so we get the formatting, and I will walk you through this. So, again, it's the run handler. We get the input and the parameters block, pass from Automator. The first thing we're gonna do is read the two values that the user has set in the user interface from the parameters block. And that is as simple-- again, these are the same names that we've added in our nib and in our info.plist. Absolutely. How's that?

So that's the first thing we need to do is read those values. We assign them to variables so we can use them in our script. Now, as I mentioned, the to location coming from that pop-up is actually a POSIX path as a string. So what we need to do is first make sure that we expand any tildes if this path that the user selected is documents or desktop, something based on their home directory. And in order to do that, we're going to use a Cocoa call called string by expanding tilde using AppleScript Studio's call method command.

This is an idiom that's gonna be very common for writing AppleScript actions using the ampath pop-up button. That's that nice automator path chooser. And the good news is you have this code because it's part of the sample code, and you can use this in your own actions. So we're going to expand the path and then convert it into an AppleScript alias. That's what this block of code does. We'll... We can talk about that in Q&A if people are interested in that.

The next thing we want to do is actually go ahead and use the values that the user has set. The to location, if the to location is an empty string, that means the user has selected the placeholder. And we just want to duplicate the items in place, which means we can actually use the exact same line of code that we used in our initial implementation in step one.

Otherwise, we want to actually pass to Finder the target folder that the user selected in this second line. Finally, we want to... and then we want to save the result that Finder sends back so we know what the duplicate items are in case the user wants to return those as the output from the action. And in that case, what Finder returns are actually Finder-style references to the items, and our action is supposed to return alias objects, so what we need to do is iterate through that list that's returned by Finder to convert them to aliases and then return them. If the user hasn't asked for the duplicate items to be returned, then we just want to go ahead and return input again, just like the simple version that we implemented in step one.

So we've got our source changed. Let's go ahead and build and-- wrong way. Let's build and go. to see what our action looks like now, now that we've added some interface to it. So again, it's in the finder group. Let's bring it out. And there we've got our user interface.

So we've got our checkbox, and we've got our pop-up menu. And you've seen this running several times. You'll see it running again, so I'm not going to take the time to run it now, but I can assure you it works. Hopefully those of you following along with me are finding that that's the case. And with that, let's go back to slides.

So again, we've just learned how to build the Actions user interface using two simple user interface elements, and you can add whatever other elements you like. We've added a checkbox and the special automator pop-up. I pointed out a few design guidelines. The pixel margin around the border using small controls.

and how to connect those elements that you add to your script using Cocoa bindings. So the next step is to add that example, if you remember, from what I showed you originally, some more advanced behavior that occurs in the Actions user interface as the user is changing values, not just at workflow runtime. And we're going to use Apple Script Studio to do that.

So what we're going to do in this step is we're going to, again, add that behavior to our actions user interface. I will outline a few of the important AppleScript Studio handlers that you need to implement in order to add that behavior, and then test the action with showActionWhenRun option, which I mentioned earlier but haven't shown you yet, and show you how to make sure that that works properly with your action. So in this step, we're going to edit the main.nib again, and then add a new file, a new script file, to hold the studio handlers to implement the behavior we're going to add. So please, back to demo two. So let's return to our nib.

Let's just-- hello. Let's reorganize these just for fun. And what we want to do is add that-- I'm just going to duplicate those text objects with Command-D. It's easier to edit them that way and get the right size. I'm just going to make the example label. And you can use Command-Equals to resize the text to fit the entered text. And just add a placeholder that we can add next to it. We can use, again, the IB guidelines to line those up. And let's line up our interface elements again.

Again, you hold down the Option key to see these pixel guides. One other thing we'll do, we'll stretch this out because the text is unlikely to be that short. And whereas the text we're going to show here is really going to only have one of two values in an action that is doing something a little bit more interesting with this, that the size of that text is going to scale. So what we want to do is look at the size inspector pane. and--oops, not vertical springs, but add horizontal springs, what this will do is if the text is larger than that default size we've established in the nib is it will dynamically scale depending on what text is actually added there.

All right, so that's what we need to do to lay out the elements. Now what we need to do is start connecting them via Apple Script Studio to our scripts. So what we need to do is switch to the Apple Script pane. for the text field that we're going to use to actually dynamically set the text based on the checkbox setting. And we'll just call that example.

And what we want to do is have this behavior happen whenever the user checks that checkbox, we want to update the example. To do that, we're going to add a clicked handler to that object. And we also want to name it to return duplicate items so that we can access that object from our script by name. Now, in order to actually implement that handler, we need to add a new script. So within the inspector, we can click the "New" button. and we'll call it ui.AppleScript, and we'll make it an AppleScript text file as far as format.

and click "Save" to create the file. Sorry, that's so jumpy. But hopefully you can see there we've added the clicked handler and it's attached to the ui.apple script file. Now, one other handler we need to add is to the view. That's the view that contains all of the elements we've added to our user interface. And we need to add the awake from nib handler, which is called when your action is loaded by Automator in preparation for adding it to the workflow.

and the open handler, which is called right before Automator actually opens it up in the workflow. And we're gonna also attach those to our UI.Apple script. These other handlers can also be used for different purposes. The activated handler is called when the workflow containing your action becomes frontmost again. So if there's something that you need to update each time the workflow is frontmost, you can implement that handler. The parameters updated and update parameters handlers I'll talk about a little bit later.

So let's click edit, and I'll save our nib. Oop, and I won't do that when I'm zoomed in. Click Edit to open up our new script that we created. And again, I'm going to get rid of the default and copy in the right code, and again, this is in your outline so that you can also copy and paste.

Compile that, let me step you through this. So this first line is just to set up a property to hold a reference to that content view of your action that holds all the individual user interface elements. And since we attached the AwakeFromNibHandler to that object, we can simply set that property to the object that's passed into that handler, because that's the only object that is attached to that handler.

Now, I showed you attaching the opened and clicked handlers. Again, the clicked handler is called when the user checks the checkbox. The open handler is called when the action is opened within the workflow when the user adds it to the workflow. And all we need to do in that case is update the example to show the current setting of the checkbox.

I've separated that out into a separate handler called update_example, and all it needs to do is it uses the AppleScript tell block to tell the content view to check the state of the button. And one other thing that I'll show you here, that checkbox is actually a button. If you don't know what class you need to use or what property that you need to check, In each action template, we've provided a reference to the AppleScript Studio dictionary, and you can search for the button class. and look at all its properties. So I--sorry, I'll zoom in again. So we can see that state is, in fact, what we need to read to access what state-- whether it's checked or not.

back to our source and zoom back in. So that's what we're going to do is we're going to read the value-- if the state of the button is checked, and we're reading it by name, the name we established in our nib file. We're going to set a variable to return duplicate items, just that string. If it's not set, return original items, and then set the content of the text field example.

Again, we labeled that text field in the AppleScript pane in the nib to that string. And again, you could look up text field in the Studio dictionary to find out that content is the property that you need to set in order to do that. So with that, let's build and go again. Save changes we made to our nib.

Drag out our action, and we see that was the open handler getting called to update the example. And if we check the checkbox-- well, you probably can't see it, but I'll zoom in so you can. As we check the checkbox, the example updates. So this is not particularly enlightening-- this is not the most compelling example. However, you can imagine that there are many interesting things that you can do based on this in your own actions. I will highlight-- merely rename finder items, which shows another use of this technique to show an example of what the renamed items will look like. And again, it dynamically updates based on the choices that the user makes in the user interface. This is a very powerful technique.

However, what we'd like to do is try this with the show action when run option to make sure that it all works properly. And again, what this tells Automator to do is when I run this workflow-- it will actually dynamically show that user interface at workflow runtime so that if I want to make this workflow more general, Every time the workflow runs, I'll have the opportunity to configure this actions user interface. So, again, I can do all the things I can at workflow creation time, and the dynamic behavior I added here works correctly. But let's make sure it works correctly after I run this. What you can see is I've checked return duplicate items. When I initially ran this workflow, it's not checked behind it, as you see here. So we click Continue. We see that the pop-up menu has been updated based on what I chose in show and run panel.

But the example hasn't. You can see now the checkbox and the example don't match. And it will get back in sync if I check it some more. But it's not getting updated based on what the user did when shown when run. So we need to fix that. And to do that, we're going to add an additional handler. So let's go back to our nib file. And we've implemented the opened handler.

What we need to do is also implement the parameters updated handler. And this handler is called whenever the parameters controlled by bindings that are in the parameters block that we're reading are updated. And this gives us an opportunity to update our user interface, which is in fact what we need to do. So this gives us a hook into when we need to update our interface because behind the scenes the parameters have changed. So we'll edit our script file again. remove the comments. And again, this code is in your outline. Oops, I missed.

And what you can see here is what we're doing is we're reading the parameter. Again, we know that that was changed because that's why this handler was called. And we're making sure that our user interface is updated by setting the state of the button to match the parameter from the binding. And then updating the example. And it's really as simple as that. Let's build again and verify that it's working as we expect.

So now if we change that to return original items, again, the example updates correctly here in the panel. And as we continue, we see that in fact the change was made is reflected in the workflow. So we've now completed the circle so that all the changes are accurately reflected.

Please back to slides. So in step three, you learned about how to connect interface elements via Cocoa bindings. Step four, we just learned how to do that using AppleScript Studio, which gives you more power to do more interesting things in your actions user interface to dynamically reflect right within the actions interface the settings that the user has chosen. And we've shown the show and run feature and how to make sure that it works correctly with behavior that you've added using AppleScript Studio.

So the next step, I alluded to this earlier, I'm going to show you how to make your action localizable, which we certainly encourage all of you to do. All of the actions provided by Apple have been localized into many different languages, and we certainly would like to continue that with as many other actions as possible.

So what we're going to do in this step is the info.plist entries to control what application group, keywords, the description, et cetera, make those localizable. And then I'm also going to show you how to read localized strings from your scripts so that the scripts that appear in that example can be localized as well. In this case, we're going to update our UI.AppleScript file to read those localized strings, and we're going to update our info.plist.strings file, which is the localized version of the info.plist keys. And we will add a new file, localizable.strings. Back to demo two, please.

So the first thing we want to do is see what we get by default in our info.plist.strings. And automatically, Automator sets up in the template, using the template, the name so it's localizable out of the box. But as you see, we're not able to do that for these other settings, for the application, the category, if there are any default parameters that need to be localizable, the description, keywords and the warning strings if needed. So again, I'm going to copy in the final version of this.

And this is in your outline. Save that and walk you through it. So the top hasn't really changed. But here, we know finder is the string that we're using for application and category. We only need one entry in the strings file. So it's there. And again, it's key equals value.

The only default parameter that's a string is set to the empty string, but if we were setting it to something that was a string that could be localized, you could add that entry here as well, to location equals and then the value. We've made our summary string localizable and our keywords localizable. And we haven't used the warning settings, so we won't change that. So that's really all it takes, is adding these entries to your info.plist.strings file, which is created for you by default and has placeholders in place to make it easy for you to add these. And then, of course, you would need to create French.lproj to Spanish.lproj based on the languages that you want to localize this into.

So the other side of the coin is within our scripts where we use strings. And in our particular case, the display example is the best example. We're actually hard coding these strings right here within our script. And to make this action fully localizable, we need to be reading those from a file that can be localized. So again, this code is in your outline.

And the only change really is that we're, instead of just setting the string by itself, we're passing that as a key into a handler called my_localized_string. You might wonder what that is. Well, we haven't added it yet. So let's go ahead and do that. And this is just simply a convenience. I could have added this localized string command right within the script code twice, but this just makes things a little bit cleaner. And this is the CFBundle identifier that is set in your actions target inspector. It's set automatically for you based on the project name at creation time, but if you change it for any reason, you'll need to make sure that you update it here. "This setting needs to match... "this setting in the Inspector panel." Let me zoom in for you.

in order to look up the file to find those strings. So, of course, the next step is we actually need to add a file that contains those strings. So we'll add that to our resources folder. create a new file in Xcode, and we're just going to select "Empty File in Project." And this is an important point. This file must be named localizable.strings-- spelled correctly-- strings.

And this will look very similar to the info.plist.strings, it's just key equals value again. for the strings that we want to make localizable. And to test this, I know you might not believe me that they're localizable, so let's actually change the values to make sure that our action actually really is looking these up out of the localizable.strings file, which again would make it possible for you to duplicate this to another languages.lproj. And one thing that I overlooked here is we need to make this file localizable. So by selecting the file in the Groups and Files column, we again bring up the inspector.

and click the "Make File Localizable" button. That's all it takes. And that will move it into the right place in our project. And we're, of course, seeing the English version, but again, you can duplicate this and then change the values to translate return duplicate items into whichever language you're working with. So let's build and test that.

bring out our action again. And as you can see, or maybe not, now you can see, We're actually reading those strings. I added the test prefix. We're actually reading those out of the localizable.strings file. Back to slides, please. So that shows you how to make your action localizable.

There are two main pieces to that, making any strings that you reference from your scripts localizable, as well as making the entries in your info.plist localizable. So that when a user is using another language as primary on their Mac OS X system, Automator is localized, as well as all of the actions that Apple created, so that your action also appears localized with the language that the user selected.

And our final step today is I'd like to show you a little bit about how to test and debug your actions. So you've seen a little bit of this already. I showed you the Automator custom executable, which has a dash action argument configured to pass your action to Automator when you hit build and go.

I'm going to talk a little bit more about AMLint on the next slide, but that's a new tool that we've added in Xcode 2.1, and that's run before launching Automator and after your action is built to do some verification. You can also build and debug, which, again, I'll do in a moment, to use the AppleScript debugger to step through your script code and view the variables as you're stepping through it.

When you're testing your action within a workflow, you may want to be able to pause and step through each action to verify that the input that's going into your action is what you want, and even cancel if things are going badly for you. And in order to do that, you can add pairs of view results actions to see the output of the previous action, and therefore the next action's input, and ask for confirmation actions to pause and then cancel, if you like, or continue the execution of the workflow.

So AMLint. AMLint is an action verification tool, as you might gather from the name. This is new in Xcode 2.1, and we've integrated it right into the action project templates. You get this out of the box. It's another build phase. It's run automatically after your action is built, but before it launches Automator and passes your action to it. You can also run it on existing.action bundles. It's in userbin AMLint. And what it does is it checks for various violations of the design guidelines in the Nib, in the InfoP list, and we will continue to expand its checks over time. But it can really help you to avoid some common problems.

So what we're going to do in this step is I'm going to show you some common errors in the nib that AMLint will flag and how to fix them, and then debug the action using some AppleScript log statements as well as the debugger. So I'll try-- we're running long here, so I will keep this a bit short. Let's go into our main.AppleScript. What we're going to do for this one is, in contrast to all the other milestones, is the step six milestone is actually the beginning milestone for this So if you're following along with me, please open up the Step 6 project.

So here's our main.apple script. And well, we don't need to do that yet. What we want to do is add some logging in our handlers just to make sure that they're being executed when we think they are. And one trick, it's not actually necessary here, but it's a good habit to get into, is to use tell me to log instead of just log. If you're within another tell block, a plain log statement will not give you the desired results, but tell me to log will always give you what you're expecting. And let's do the same thing within our update example.

We'll save that. And then let's build and debug. All right, well, you might not be able to see that there, but our build actually failed. Our errors and warnings is red, and we see down here in the status bar that the build failed. So let's look up at our errors and warnings.

Okay, so we see that amlint has flagged two errors in our nib, that text fields are using the wrong font. So let's open up our nib again. And we can see that, yes, in fact, in this version of the nib, in this milestone, the example label and placeholder are the wrong font. They're much bigger than the other fonts here. And if we look at the attributes, we'll see that, yes, in fact, the size is regular. So that's the error, the violation of the design guidelines that AMLint is flagging. And if we fix those... and re-save our nib, and we'll take the time to realign those. Now if we build and debug, Our build should complete, and Automator should launch within the AppleScript debugger.

Let's build a workflow and actually test this. So when I showed you running this earlier, I showed it to you running -- selecting multiple items. So let's just select -- actually, I did show it selecting one. Let's just select one single item. And what we see is in view results that we get an empty list. Well, that's not what we should have gotten. We should have gotten a list with one item, the duplicated item for the-- input file. Well, so that's not good, but let's see if that is specific to returning the duplicate items. Let's run the workflow again.

Okay, in that case, we did get what we expected. We got a reference to the input file. So this bug seems to be specific to returning duplicate items, and we can further test this by allowing-- selecting multiple items. In that case, you can see, I hope, that we are in fact getting references to the duplicated items. So this seems to be specific to returning duplicate items when only one item has been added.

So we've got this already running under the debugger, main.AppleScript, and put in a breakpoint so we can step through. And we know that the problem is within this case, so let's break there. And in Xcode, for those of you not familiar, you just click in the margin to add a breakpoint. And in fact, in Xcode 2.1, we can add breakpoint actions, including AppleScript commands.

Not a second-class citizen. So let's see, we want to just display dialog, returning duplicate items. and we can add in one of these placeholders, which you probably can't see because it's too small, but there are various, uh... so we want to add in the breakpoint name. Okay, so that's our breakpoint, and we're still within the debugger, so let's go back and run that workflow again. And we know that the failure case is when we only pass in one item, so let's do that. And here's our display dialog with our breakpoint action. And here we are stopped in the debugger. So let's make that a little bit bigger and zoom in.

So let's go ahead and step through this. Another thing to point out is we've got all of our-- we've got our variables displayed up here. Duplicate finder items, that's the result that we got returned from the command of finder. And let's see, we've got our two location. So let's step through and find out what happened here.

So here's where we're going to loop through our list of items returned from Finder. And we see we skipped right past the loop. Well, that would certainly explain, since duplicate item aliases is still an empty list, why we're getting an empty list as our return value. So we've tracked down where the problem is.

Then we need to figure out why. And in fact, we can see the duplicate finder items isn't a list. So finder is not returning us a list as we were expecting. So we're going to have to be a little bit smarter about that. And I will leave it as an exercise to all of you. Or you can just open up the final step, which has this bug corrected.

And finally, let me just show you the console log to show you that, in fact, our log statements when awake from nib was called, which happens once, and I clicked the checkbox a couple times, so each time I did that, as well as on opening, we got our updating example log message to the console log.

So with that, let's back to slides, please. So just one final point about installing your actions. Apple's actions go in System Library Automator. But for your actions, if you have an installer, you can install them in the user's home directory Library Automator, or for everyone on the system, in /library/automator. You can also put them in the network domain of the library. Or what is very useful for those of you who have applications that just have drag installs and you don't have an installer, you don't need to add one just for Automator actions. You can install your Automator actions inside your application bundle, inside contents library Automator. And Automator will automatically find them as soon as your application is dragged onto the user system.

So just to sum up, what have we learned today? We've learned how to build, test, debug, and install an action using AppleScript, AppleScript Studio, and Cocoa Bindings to manage the relationship between your actions' user interface elements and your scripts, and we've used Interface Builder and Xcode to build and manage our project.

So what next? Well, I know some of you are following along and continue to experiment with this particular sample project. There's quite a bit of documentation. There's a brand new AppleScript action tutorial that has just been released along with Xcode 2.1. And explore the other example projects that are part of the developer tools in developer examples automator. There are a number of examples, not just for AppleScript actions, but for the other action types as well. And finally, go back to work and write and ship lots of great actions for your applications. We're really looking forward to that.

So just a pointer to the general URL. This has pointers to the documentation that I've referred to, as well as the sample code and other resources for this topic. There are a great number of related sessions going on this afternoon and all day tomorrow on Cocoa Bindings, Cocoa Automator Actions, AppleScript Studio, two labs tomorrow. If you would like some help or get stuck with writing actions for your application, please come to the lab tomorrow and we'll be more than happy to help you. And then finally, tomorrow afternoon, I hope you'll stick around. I know that's gonna be a great session on Automator Actions using the new shell scripting action type tomorrow afternoon in building Automator Actions for system administrators.

Some contact information-- I'd like to highlight the developer mailing list, which I'm sure a number of you are participating in. And I'd encourage you to subscribe. And we're starting to build a great community there. The Apple engineers try to help out as well. But there's been a lot of outside developers that have been helping each other. And that's great. We're building a community.