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 has known transcription errors. We are working on an improved version.
Good morning. Welcome to session 130. I'm Todd Fernandez, the AppleScript Automator Engineering Manager. 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 AppleScript. 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 all 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 there 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 of 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. I'm going to point out that 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 going to 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 going to 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 that makes it much easier to configure your action's Info.plist settings, a new action target inspector.
So with that, I'd like to switch to Demo 2 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 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... will 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 AppleScript 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 going to 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, ShellScript Actions tomorrow afternoon. But we're going to create an AppleScript 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.AppleScript in 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. A 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.AppleScript 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.
[Transcript missing]
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 a 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/applications. And we have a -action argument which is added automatically based on the project name to pass the built product for your action to Automator on the command line. So it will be loaded in and you can immediately use it and test it in your workflows. So we'll go ahead and build and go.
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 in 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.AppleScript 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, etc.
So there's a bit more background information here. Automator has a number of keys in its info P list 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 amname 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.
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. AMXEPS 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 the most 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 Cocoa Action 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 in 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 or concept to AppleScript coercions.
And again, we're going to actually talk quite a bit about this in the Cocoa Action 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 InfoP-List 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 InfoP-List 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 InfoP list 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 InfoP list. 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 run time 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 monitored using or 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.
Absolutely. 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've 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. Let's add it to the workflow and we see that we also now have our typeset 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.
We're going to 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 going to 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 going to be modifying, sorry about that, modify three files. We're going to start by modifying the nib, then add the entries to the info.p list, and finally add the code to your main.AppleScript. 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.
[Transcript missing]
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 going to 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... 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... Okay, 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. We'll leave that as good enough for now.
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 pop-up 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 So, you get to the 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 default 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.AppleScript 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, passed from Automator. The first thing we're going to 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 InfoP list. 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 desktops, 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 in path using AppleScript Studio's call method command.
This is an idiom that's going to 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 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. So 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.
[Transcript missing]
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 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.
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 AppleScript 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 show action when run 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. You 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 AppleScript Studio to our scripts. So what we need to do is switch to the AppleScript 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.AppleScript 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 AwakeFromNibHandler, 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 going to also attach those to our UI.AppleScript. 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. Well, we'll save our nib. 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 and 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 attach 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 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. 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.
[Transcript missing]
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, or 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, really 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 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 is, is 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.
So 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 the 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 something's not getting, it's not getting updated when, 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. 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.elpraj to Spanish.elpraj 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 mylocalizedstring. 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. And 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 have bring out our action again. And as you can see, or maybe not, now you can see, we have
[Transcript missing]
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 output that's going in-- or 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--and 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 6 milestone is actually the beginning milestone for this If you are following along with me, please open up the Step 6 project.
So here's our main.AppleScript. 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. 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 we 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 : 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 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 Placeholder, so we want to add in the breakpoint name. Okay, so that's our break point 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 locations. 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 that 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 hope-- I know that 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 morning 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 going to 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 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.