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: wwdc2008-539
$eventId
ID of event: wwdc2008
$eventContentId
ID of session without event part: 539
$eventShortId
Shortened ID of event: wwdc08
$year
Year of session: 2008
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2008] [Session 539] Making Your...

WWDC08 • Session 539

Making Your Application Scriptable

Integration • 1:00:05

When you make your application scriptable, it can be used in Automator workflows and by scripting languages such as Ruby, Python, and AppleScript. Take advantage of Mac OS X's rich application scripting architecture so users can integrate your application into their automated workflows, making your application more valuable to expert and professional users.

Speakers: John Comiskey, Chris Page

Unlisted on Apple Developer site

Downloads from Apple

SD Video (731.4 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good afternoon. My name is John Comiskey. I'm an engineer in the AppleScript group, and I'm here to talk to you today about making your application scriptable. Scriptability lets your end users solve their problems on their own. You spend a lot of time polishing your application, but you can't always anticipate every need, and you also can't always justify the time it takes to fill the needs of people that have very unique needs.

The benefits of Scriptability to you are very high. You get automation, which is going to help you do your testing and ship your product better, and it's going to help your end users solve their problems better. You get integration. You work better with other applications on the Macintosh and with the Macintosh system itself, and you get loyalty from your customers, which is great. You want your customers to come back release after release. You want them to upgrade. You want them to keep using your product, and Scriptability can help you do that.

[Transcript missing]

We're going to cover several different topics. First, why you want to be scriptable. We already talked a little bit about that, but we'll go into more detail. Then the first step, and we really do mean the first step in good scriptability, is designing a dictionary. And we're not just going to talk about that today, we're going to show you.

Then you have got to write some code. You should be able to leverage a lot of code that you've already got, but you will have to write some that's just for scripting. Then you have got to write some code that you've already got, but you will have to write some code that's just for scripting. Your end users can solve their problems and that you haven't regressed anything that they're depending upon in their daily workflows.

And scriptability will help you do a better job of testing. So why be scriptable? What's in it for me? As a developer, how do you benefit? Well, first we need to define our terms. What does scriptable mean? It means giving your end users a way of controlling your application with something other than the keyboard and the mouse. You want to give them a programmatic way of controlling your application so that they can automate tasks that they do repetitively.

Strictly speaking, you need to be OSA compliant. OSA compliant, OSA stands for Open Scripting Architecture, and it's a set of protocols and agreements that we all stick to so that our applications work better together and work better with the system. And to find out the particulars and the details of that, you want to read the scripting interface guidelines, which is Techno 2106.

Just as we have human interface guidelines to make sure that programs are uniform across applications, uniform across vendors, and every experienced Macintosh user can approach a new application and know instinctively what it is they're supposed to do, how to explore the menus, how to explore the toolbar, how to move things around on the screen and get it to do what they want to do.

We also have a set of scripting interface guidelines so that we have consistency across applications and across vendors, and people who are experienced scripters get that same end user experience of continuity across the entire system. And the key component of that is that we want you to do model scripting, not view scripting. We want you to expose the objects that your application manipulates rather than just all the buttons and sliders on the screen.

View scripting is free and it's worth every penny. You can do things with view scripting, but anytime the view changes, anytime you move a button, anytime you put something inside of a box, all of a sudden your view scripts break and they all have to be rewritten. Model scripting doesn't act like that. Model scripting works release after release.

The biggest benefit and the most immediate benefit to you as a developer is in automated testing. You've got a lot of code that backs up your beautiful user interface. You've got a powerful back end. Maybe it's hooked up to a database. Maybe it's hooked up to the network.

And you need ways to test that. And you've probably got very dedicated people that sit down in front of a computer and try one thing after another to see if they can make something break. But try as they might. They really can't touch everything every time. If you've got a way of automating your testing, they can do that.

And what you want to do is you want to do regression testing. You want to keep your results from the time before and compare them. Make sure that if you make a change, you get the intended change, but you don't get any unintended changes along with it. Stress testing is hard to do by hand, but with automated testing, you can set up a script and let it run overnight and do thousands or tens of thousands of iterations while you're at home sleeping. And you can also have validated releases. When you get ready to release your software, you can know for a fact, not only does the new stuff that we put in there work.

Not only do our whiz-bang features really get that wow and that pop, but all the things that people have come to depend upon also still work. It's a sad surprise to open a new application, see all the cool new things it can do, but the one thing that you do every day is broken.

Another thing that you and your end users get from scriptability is integration. It makes your application work better with the rest of the system. And you can leverage existing technologies inside of the operating system. AppleScript is 15 years old now. AppleScript's been bugging me about getting a learner's permit and borrowing my car, but that's not going to happen. More recently, we've come up with Automator. Automator is big chunk automation for end users to make automation even more approachable.

We've come up with some common things that people do. Those are called actions. They can be put together in a big block fashion into a workflow and solve common problems that everyday people have. And it's much more approachable than AppleScript. You don't have to think of yourself as a programmer to do it.

And we've now got, that says scripting bridge, but it's pronounced Apple event bridge. And it is a programmatic way for one application to talk to another using Apple events, using the scriptability of the external application. And what happens is the object model of an external application becomes available to you, the developer, as full-fledged objects inside your application. So you can simply tell other applications to do things for you, and then you don't have to write all that code yourself.

That's your bridge to leveraging other applications. Anthracite is a web mining tool. And Joe Pizzillo wrote it. He's a great friend of scriptability. If you see a guy in a plaid hat, that's Joe Pizzillo. Say hi. And he did a great job of making Anthracite scriptable. And Anthracite can pull down huge amounts of data off of the web. And he didn't want to write all the code to process all of that.

But he didn't have to, because Microsoft Excel is extensively scriptable. He can export data directly into Microsoft Excel spreadsheets, and you can use Excel to do a lot of your data grinding for you. But thousands and thousands of numbers are sometimes hard to understand. The Omni group is another great friend of scriptability. They have a tremendous suite of scriptable applications.

and OmniGraffle is one that can take huge amounts of numeric data and express it in a graphical form and make it much more understandable. Adobe is also another company that has embraced scriptability and put it into all of their applications. And Adobe Illustrator can fulfill the same role, turning thousands and thousands of numbers into one nice comprehensible picture.

And then when you're all done, you want to email this off to your boss so he knows that you've been working really, really hard in getting all of this stuff done. And once you've put this whole workflow together, it can run unattended while you're not even there, fire off this email at the end and everybody thinks you're working really hard and you're actually at the ballgame.

Automation is a benefit that accrues to your end users. They have problems that you didn't anticipate. They want to do things that you didn't think of and you didn't make a feature in your application. But if you give them a rich object model to work with, they can solve these problems on their own and they get immediate results.

They don't have to wait for your release cycle. They don't have to wait six months or a year for you to come out with a new version or bug you to be on a beta list even though you know they're actually going to deploy it throughout their entire company. You don't want to do that. And when people do write these scripts and solve their problems on their own, you want to get that feedback.

You want to have a vibrant online community feeding information back to you saying, hey, I wrote this script. And you want that for a couple of reasons. You want to capture that script. You want to make it part of your testing. You want to make sure you don't break that script ever because there's somebody out there really using it.

And also, you want to kind of pull these little features that people are adding to your application. If you find that 50 different people have written a script to do the same thing, you want to make sure you don't break that script ever. If you find that 50 different people have written a script to do the same thing, you might want to consider making that a feature of your app.

Customers also get their work done faster. They do the same things over and over again. And after you've worked with an application for a while and you've done the same thing over and over again, you get tired. You begin to hate the application. The last thing you want is for people to learn to hate your application. You want them to love your application. If you give them an automated way of doing their repetitive tasks, they can write scripts.

They can set that all up. It runs while they're at lunch. It runs overnight. And then the next time they come to your app, it's to solve a new problem. And they're really excited about it. And then they learn to love your app. And you want them to love your app because then they'll buy all your upgrades.

And automation is going to give them consistent results as well. They're going to get the same result every time. They're never going to wonder, gee, did I click this first and then that? They don't have to worry about what was clicked because it was all done programmatically for them.

And this leads to loyalty. Once somebody's made you part of their automated workflow, they are never going to get rid of your application. Even if your competitor comes out with a great new feature that everybody's got to have, if you're scriptable and they're not, your customers may go buy that other application, but they're never going to get rid of yours. You're going to be part of their workflow forever unless you break their scripts, which is why you need to do automated regression testing.

And this is all going to save you money. You're going to let your customers do the customization because they're the customers. They're going to think of what you've forgotten. They're going to think of what you didn't think of. And they're going to write scripts to do it. And if you've got this online community feeding that back to you, you're going to find out about it. And then you're going to be able to spend your time on the things that people are really interested in. You're not going to spend a year creating a fabulous feature that you're really, really proud of, but nobody ever uses.

The first step, and I said we mean the first step, is designing a dictionary. You want to empower your end user. You want to give them the ability to solve their own problems. And you start by reading the scripting interface guidelines. Tech Note 2106, one of the great tech notes of our time. You should definitely read it. And you want to deliver an object-oriented solution. Give your users tools that they can build their own solutions with.

Don't try to anticipate their needs and give them a silver bullet that does that one magic thing that they need done. Because sure as shooting that silver bullet, they'll be back saying, "I need one more silver bullet. I'm not done yet." And if you give them a rich object model, you also achieve interoperability with other applications.

And you don't have people coming to you and saying, "Gee, it would be really great if your application had a nice spreadsheet like Excel embedded in it." You can say, "Well, we have scriptability, so we don't need that." When you're designing things, there's some pitfalls that you can fall into, and if you read Techno 2106, we'll talk about those.

You'll see those in there. And mostly it's oriented around giving a good, strong object model. Those of you who are familiar with AppleScript or have maybe made an application scriptable before will look at this first line, close front window, and you'll say, John, it's a perfectly good line of AppleScript.

What's wrong with that? Why is there a big red X there? Well, because in our example here, somebody has put close front window in their dictionary as a command. It's three words, it's close front window, and it closes the front window. That's not the way we want you to do it.

What we want you to do is use close, which is a verb in the standard suite. You don't have to add that. That's already there. Front is a reserved word in the AppleScript language that's a synonym for the index one. And window is an object that's also defined in the standard suite.

So you don't have to do that either. You need to support it, but you don't have to define it. And so together, those things become close, the command, and front window, the object specifier. And now that's the right way of doing it. The same line of AppleScript, but the implementation is object-oriented instead. Another thing that you want to avoid, you want to be friendly to the language itself.

You don't want to stick verbs inside of things that are supposed to be nouns, because then when people write AppleScript, AppleScript is has a nice easy flow to it. It's easy to read. But if you throw verbs into the middle of nouns, funny things are going to happen.

You're going to have something that says, "if isFrontWindow is true." And that's going to make it hard to read, hard to figure out what's a language term, what's an application term. So what we want you to do is leave that word "is" out, and just call this "frontWindow." Again, it's an object specifier. If you support windows, "front" is defined for you. It all just works.

You also don't want to just take the internals of your code and just leave it out. You want to take the internals of your application and dump it on the end user and expect him to figure out what it was you wrote. You want to simplify it for him. You want to candy-coat it a little bit for him. And one of the things that will tell you faster than anything else that you've just dumped your internal implementation on the user is if there's intercaps in your dictionary.

Anybody who sees intercaps in your dictionary, they're going to know immediately you haven't done your homework. You haven't done it right. And again, this is an example of a really egregious violation. This is a command called "closeWindow" with intercaps and no spaces in it. And as its object, it takes an index.

That's not object-oriented. We don't close integers. We close windows. So again, we don't want to pass an index as the input to an operation. What we want to do is we want to pass an object specifier as the input to the close command and do it that way.

That's object-oriented. Another thing you don't want to do is put underbars in your dictionary. AppleScript will allow you to have multi-word terms. You don't have to connect things together for them to work in AppleScript. So again, you want to get rid of these underbars here, and you just want to change this to front window, which is an object specifier just like any other.

What do I do? Where do I start? Where do I finish? How do I know I'm ready to ship release one? Well, what we want you to do is think big at the beginning. Go through the process in an iterative fashion. Keep designing until you've designed terminology for everything your application can do.

Then step back and look at it and say, my gosh, I can't do that in one release. What am I going to do with this? Scale it back for the first release. And what we tell people is solve one compelling problem that a real user has. Talk to your online community. Find out what people's problems are. Find out something interesting that would be good to do with scriptability.

And provide enough scriptability in your dictionary to solve that one problem. Send out a sample script that solves that problem, and that's release one. Now you've got scriptability into your end user's hands. You've got an interesting problem that at least one person was really interested in getting a solution for.

And now there's going to be a buzz that's going to start. People are going to start scripting your application. They're going to find out that they can do some really cool things. They're going to find out that there's some things they can't get at. They're going to feed that information back to you.

And again, you're going to spend your time where you know it's going to do a lot of good. It's okay to have features that are in the GUI but are not in your scripting. Some applications have very powerful dialogues that allow you to create Boolean descriptions of searches. That you can take values of various fields, do comparative operations, and and or the various operations together, and come up with a subset of data that the user's interested in looking at.

And that's a great feature. But you really don't want to try to duplicate that in your scripting dictionary. The reason being, Apple script has whose clauses, Objective C has predicates, every scripting language has some way of expressing a query of some sort. You want to leave that in the language.

If you implement the basics of object model access, you'll get that query feature as part of the scripting language. You won't have to build it into your application. It's also okay to have features that are in the scripting and don't have to build it into your application. You can also have features that don't show up in the application's UI.

Not right away, maybe later, maybe never. In mail, there's a lot of fields that you can add to an address book card. Like job title, anniversary date, maiden name, things like that. Those were all available in scripting first and didn't show up in the UI until later. That meant that the address book team was able to test them. Power users of address book were able to get at them and start putting data in.

People that were importing data into address book could make use of them. It wasn't until later when they designed a nice UI for them that it showed up in the UI. That's okay. Your scripting implementation and your UI implementation do not need to match exactly. But they shouldn't stray too far afield.

A long, long time ago, networks were slower, computers were smaller. This is a gigabyte of memory and it's considered small. That would be enough for a thousand computers back when AppleScript first came into existence. We had to shrink things down and as a result, a lot of the terminology in Apple Events has shrunk down to four byte codes.

One of the jobs you have to do as a developer is to take human terminology that's going to mean something to your end users that they're going to use in their scripts and you're going to have to map that to a four byte code. Four byte codes absolutely must be unique inside your application.

Applications are isolated from each other by tel blocks in AppleScript and by similar mechanisms in other languages so that your terminology and the terminology of another application don't collide. But it's good for you to agree with as much existing terminology as you can for several reasons. The AppleScript language itself defines a lot of terminology, and you definitely want to agree with that. If you have any conflicts, AppleScript wins and you lose.

Scripting editions, including the standard editions that ship with the system, also define global terminology, and if you conflict with that, they win and you lose. If you want your application to be used by AppleScript Studio developers, AppleScript Studio defines a lot of terminology, and you don't want to conflict with that either. And even if you just want to get along with some other application, if you want to be able to send things like files and pictures back and forth, you want to agree on terminology with that application.

So if you go to your favorite search engine and you search for Apple event codes, one of the first couple of hits will be a big page that we have posted from Apple servers with a list of about 2,000 4-byte codes and the human readable terms that they correspond to. We strongly encourage you, when you're reviewing your dictionary and making sure that everything's just right, go to that page and check your 4-byte codes. If you see something that's already been defined, by all means, reuse it, reuse the same 4-byte code. You're not stealing, you're being nice.

Where do you get this design from? You don't get it from a typical programming specification. What you want to do is you want to get it from the end user perception of your application. You want to design a terminology that's going to be familiar to your end users because it uses the same words to describe things that they do.

Last year, I talked about something called a blind interview and I showed this real scary picture. And then I never did tell you what a blind interview was. So we're going to correct that. Today we're actually going to do a blind interview so you'll get an idea of how we go about doing this.

Your scripting dictionary is going to have to reflect your internal structure to a certain extent. In fact, the way Cocoa scripting works, there really do need to be Cocoa objects for everything that appears in your dictionary. But you want to do that within reason. There's a lot of private stuff inside your application, implementation details, helper objects, things that you really don't want to hand over to the end user. Hide all of that stuff. Simplify all of that stuff.

Use the classic data hiding capabilities of object-oriented languages to protect the customer from having to mess around with crazy bit flags and things like that. And I promised you a blind interview and now we're going to do that. We're going to bring up Chris Page for our demo. And we're going to show you what a blind interview is.

So, we're going to look at a program that should be familiar to all of you. It's Preview. It comes with Mac OS X, and you all have it on your machine, and if you double-click a picture file, Preview is what comes up and shows you that picture. So, Chris, if you could, wait, the blind part. What I'm going to do is I'm going to situate myself somewhere where I can talk to Chris, and he can manipulate the Preview application, and he can tell me what it is he's doing, but I can't actually see what he's doing.

If you come to the lab downstairs, typically I would sit across the table from you. You would have your application on your portable in front of you. I'd be across the table. I couldn't see what you were doing. This forces you to tell me in words what it is that you're doing, and then I'm going to write those words down, and those words are going to become the terms in your AppleScript dictionary. So, Chris, double-click a file. A file? A file.

All right. And tell me what you see. I see a window. Honestly, I can't see anything down here at all. I see a window with an image of the Golden Gate Bridge. Okay. Do you see anything around the edges of that window? Is there anything else besides just the image? Yeah, the window has a toolbar with some buttons in it. Okay.

And what are some of the tools in the toolbar? There's a previous and next button. There's some zooming buttons. Moving and selecting. A slideshow button to play a slideshow. And there's a button to show and hide the sidebar. Okay. Sidebar, huh? I wonder what that is. Are there any other tools? Are there any other places that we could look and find some tools? Well, we've got some menus with commands like rotation and flipping, markup, annotation. Okay. Okay, good. Okay, go ahead and close that. And open another file.

Here's another file. Okay, now what do you see? I see another window with another toolbar. But this time I see a text document, and I see a sidebar with four pages in it. Okay, so before we had a window with just a single image in it. Now we've got pretty much that same window, but it's got a document in it, and that document's got several different pages. Is that, it's probably a PDF, right? It is a PDF. Okay, okay.

So now, we're starting to develop a containment hierarchy here. We've got an application. This application has windows, and closely associated with the window is one or more documents. In the first case, it was just a single image. In this case, it's a single PDF file with multiple pages.

So we're starting to develop a list of things that our application can manipulate, and we're also starting to develop a containment hierarchy of how these things are nested one inside another. Okay, go ahead and close that, and see if you can find a whole bunch of files. A bunch of files. Let me see. Oh, here's some. Okay.

Go ahead and open all of them. Okay. Okay. Now what do you see? Now I see a window with an image and a sidebar with the other images that I opened. Okay. And can you change which image it is that you're looking at? Yes, I can click on the images in the sidebar and view them separately.

Okay. Okay, so now we can see in our first experiment, we clicked on a single image, and it just came up in a window all by itself. But now, if we open up multiple images, they're all available in a sidebar. So a window can actually have more than one image associated with it.

So by going through our interview process in an iterative fashion, we've learned something more about the containment hierarchy. A window can have an image in it. A window can also have multiple images in it. And that's going to be important when we start designing our scripting, our scripting dictionary. Okay. Thanks very much, Chris. You're welcome. Chris Page, he's here all week. Bring him back up for Q&A later. And if we can go back to the slides.

Okay. So, we did a couple of different things here. First thing we developed in inventory. What classes did we find when we opened this thing up? We found that there was a window and it was closely associated with one or more documents. Those documents could be either images or PDFs. We haven't really finished exploring. Maybe there's other things that Preview can open up. But we know already that there's at least images and PDFs and that they inherit from documents.

Your scripting dictionary is going to be able to represent both inheritance and containment. And inheritance and containment are two different things. Images and PDFs inherit from documents, but they're contained inside of Windows. Okay. We've also deepened our containment hierarchy. PDFs can have pages. So, we have to add that to our inventory. And there's this thing called a slideshow. And we're thinking we're probably not going to get to it in release one, but we better put it on our list because we're designing the whole universe.

And we want to make sure that we're designing the whole universe. And we want to make sure that we reserve those terms now so that we don't come back later and say, you know, we really should have called this other thing a slideshow. Boy, we messed up. Now we're going to have to work around it.

We also looked at the toolbar and the menus and we found some possibilities for commands. We want you to deliver an object-oriented solution. We want you to think objects first. But every application has a few things that it does that are unique to that application and you want to make sure that you cover those so that your end users can use them.

In preview, some of the things we saw were rotate, flip, and zoom. We have this thing called a slideshow we want to do and we know at the very least we're going to have to start and stop it. And then just in general, almost any application will have these kinds of things.

We were able to select an individual page or an individual image and focus the window on that. So we're going to want to have a way of selecting things. And the whole purpose of preview is to show us stuff. So we want to be able to tell preview, hey, show me this. and reveal is the word that we use for that.

I talked earlier about the difference between inheritance and containment. Here's the containment hierarchy. The outermost container is always the application itself. When you tell an application to do something, it acts as the outermost container for all these other objects. The application has a window inside of it. The window has one or more documents that can be either images or PDFs, and PDFs have one or more pages. And slideshows, we're figuring, will probably also have images in them. And as we develop this some more and we iterate our interview, we'll find out more about what goes inside a slideshow.

What makes one object different from another? What makes one object interesting when another isn't is the value of its properties. You're going to want to give your end users access to these properties to allow them to get them and set them and also to sift through their inventory of objects that they have and pick the ones that they're interested in.

We noticed while we were manipulating preview that there was some notion of a current document in the window. We want to make sure that the scripter can get at that so he knows which picture it is that is being displayed in the window right now or which page of the PDF and that the scripter will be able to change those.

Again, the PDF document is going to have a notion of a current page that's the one being shown in the window. We would want to support that as well. And another thing you can do with PDFs, when I was experimenting with it, you can blow them up and make them easier to read from the back of the room. So you're going to want to be able to control the scale of the document as well, and that's measured as a percentage of 100. And images, they're going to have height and width, and they're going to have a file type.

One of the things that Preview does that's really powerful is convert between file types. You're definitely going to want to make that scriptable. That's exactly the kind of tedious, repetitive task that people hate, and we'll teach them to hate your application unless you make it automatable. Slideshows are going to have intervals for how long a slide stays on the screen, transitions for how one slide goes off the screen and the next takes its place. And there's probably going to be settings for those for the entire slideshow. There may very well be settings for interval and transition for each individual slide as well. I know that Keynote supports that.

Then, after you've done this interview and you've gathered all this information, all you've got to do is sit down and write a little XML. There are two really good third-party solutions. Suite Modeler from Don Briggs. If Don's here, say hi. It's a good one. And then there's also SDef Editor.

They're third-party shareware solutions. By all means, search for them on the web, download them, see if you like them. I personally write my XML in BBEdit and use their formatting features to make it look really pretty. I make fire with Flint and Stone. So you do it the way that's right for you.

You're going to want to take the information that you got from that interview and put it in your dictionary. These are the terms that we came up with when we were doing our interview. We saw that there was an image that we were going to be able to look at. Images have height and width, and images have file type. Those file types are enumerated. The way I found out which file types preview supported is I did a save as and looked at the pop-up. That's where I found those.

Then you're going to want to assign 4-byte codes to those. I looked these up on our 4-byte code page, and I found some and used the ones that were already there. The ones that I didn't find, I made up some, and then I looked for the ones I made up and made sure that they weren't there. You've got to do it both ways.

If you make up something new, you've got to make sure it's not being used for something else. If you find what it is you're trying to define, you definitely want to keep the human readable term and the 4-byte code paired together. You're the programmers. you get paid to do that kind of stuff.

Then you're going to have to hook this back up to your Cocoa objects inside your application. So the image object is represented inside my application by a Cocoa object I call my image. We've also got this nice property file type. And since AppleScript's a nice English-like language, there's a space in there. Objective-C doesn't like spaces in the middle of its method name. So somehow or another, we're going to have to arbitrate that.

So in our property element here, we've got a non-empty element and inside of it we've got a Cocoa element. And we say that the key for this property is file type with the space squeezed out and an intercap put in there. So this allows us to map really nice-looking human-readable terms to the yuck that's inside of our applications and hide that from the user.

Then, when you're done with all of this... You put it in the resources folder in your application, you set a key in your P list, point to it, and it becomes your scripting dictionary. And then anybody that wants to can look at it with the dictionary viewer. If they drag and drop your application on the script editor, the dictionary viewer opens up. If they're an AppleScript Studio programmer and they're inside of Xcode and they ask about your application, the dictionary viewer comes up. And they see this nice representation of it.

And you can see that inheritance is being expressed here, containment is being expressed here, references back to objects that are defined in the standard suite are being expressed here. The end user can see everything you've done to make this image object scriptable inside your application. This is just a little baby step here. You're going to want to add more properties. You're going to want to add more objects. You're going to want to fill this out to define everything that's inside of your application.

After you're done with that, then you need to write some code to make it all work. And I promised you that there wasn't going to be a lot. There's four things that you basically need to take care of. You have to have element accessors. The containment hierarchy is all about nesting things inside of other things. You need to be able to traverse that containment hierarchy and get at these things. So that means that the container objects are going to have to have accessors to get at the contained objects.

Chances are your application already has something along these lines. If you're using an NS Document-based application, then the NS Application object already has ordered windows and ordered documents defined. Your window accessor and your document accessor are already there for you. You just have to make sure that you use the right method names in your dictionary, hook that up, and it just works. The objects that you define have to behave similarly.

You have to have ordered collections of things, make them available to the scripter so that they can manipulate them. You're going to need to do some element management, too. You want to create and delete things. Your collection of data is not static, and you definitely want to give the scripting user the chance to do that.

One of the first things a tester is going to do when they get their hands on your scriptability is write a script that's going to fill the database up with a thousand objects real fast so that they don't have to do that by hand every time they get a new release. You're going to have to have property accessors.

What makes one object different from the other? What makes one object more interesting than another is the value of its properties. This is something you might actually already have. You might get that for free, and we'll see that later. And then lastly, and we do mean lastly, you want to do your commands. You want to do everything you possibly can in an object-oriented fashion.

If you have some property that's just a Boolean value, you don't want to create two different verbs to turn it on and off. You don't want to have a show command and a hide command. What you want to have is a visible property. And if you set that visible property to true, then you can see the object. You set it to false, you can't. Saves you two commands. You're going to have to create that Boolean property anyway because you're going to need to be able to query it to see if something's visible or not.

Once you've got that and you make it read-write, you don't need the show and hide commands anymore. So try to do things in an object-oriented fashion first, then make commands when it's really a unique situation. Element accessors look like this. And again, I said you might already have something like this.

The name images comes from the object name in your dictionary, and it gets repeated in a naming pattern throughout these accessors. If you've got small collections of things, if you've got a little graphics program and put maybe a couple of dozen, a couple of hundred graphics on the screen at one particular point in time, you can just return the entire collection all at once.

If you have a more voluminous application, if the back end of your application is a database or the back end of your application is the entire Internet, it's not reasonable for you to return the entire collection every time somebody wants to look at one element in that collection. So you've got an alternative here. You can have two variants of this, one of which returns the count of the images, the other of which returns a particular image at a particular index. Cocoa Scripting will figure out which of these you've supported.

And use them appropriately to make your application perform better. And in the interest of performance, there's a couple more accessors that are defined. You may have a back end that allows you to find things by name really quickly. You may have a hash table where you can look things up by name and find them really quickly. And if you do, you want to provide that performance to your scripting users.

And you do that by providing a method called value in images with name. And again, images would change. Images would change. Images would change. Images, documents, windows, whatever objects you support, that word that's in there in yellow keeps changing. And if you've got your back end as a database, your database probably supports some notion of a database key. And it can get things by, it can search, it can retrieve things from the database using that key very quickly and very efficiently.

You want to pass that performance capability on to your scripters by implementing value in images with unique ID. defining unique ID property on the image object, Use the database key to represent that unique ID, and then you'll be able to fetch things from your database in the quickest and most efficient manner possible.

You're going to want to manage your objects as well. You want to create new ones, get rid of old ones, move things from one collection to another, move an image from one album to another, from one folder to another. And you do that with these two routines, insert object in images at index, and remove object from images at index. And again, images changes with the name of the object.

And if you provide these two, Cocoa Scripting will use them at the appropriate point in time to make new things, get rid of old things, and to move things around from one collection to another. And every good scriptable object needs to be able to identify itself. It needs to return an object specifier. And an object specifier is essentially a return address. It tells the scripter or the script itself while it's running how to get back to something that it's touched before, how to get back to an object that it was looking for before.

And you want to return the best object specifier that you can. If your back end is a database, and you have a key that uniquely identifies every record in the database, you want to use that in your object specifier. Because then those object specifiers are good for as long as the program is running. You'll always be able to get back to the same record. You'll never miss. Nothing bad will happen because you added or deleted objects. If you don't have something like that, you may want to use a name object specifier.

Particularly if you're referencing things in the file system, name object specifiers are usually very good for that. In AppleScript and scriptability in general, it's not necessary that names be unique. So the name object specifier is not as good as the unique ID object specifier, but it can be good enough for your purposes. You just want to make sure that you're consistent. If someone references something by name, and there's two copies with the same name, you want to make sure that you're consistent. name, you want to make sure that you always return the first one, whatever first means to you.

Because then if someone finds that object and then later decides, okay I want to delete that object, he won't accidentally delete the other one that happens to have the same name. As long as you consistently return the same object for the same name, that'll work too. The third kind of object specifiers is by index.

This is the weakest kind. This can change. If you return an object specifier that's defined by index and you add or delete anything to the collection, it's possible that that index isn't good anymore. The things have shifted back and forth and now the index actually points somewhere else.

That's problematic. It can lead to data loss. You want to try to avoid it. But there's situations where it's perfectly appropriate. For instance, if you're talking about a character inside of a word, inside of a paragraph, it's perfectly appropriate to do that by index. It's not practical to create a unique ID for every character in a document.

It's not practical to create a name for every character in a document. You just do it by index. And your scripters will need to know, gee when I'm manipulating text on the character level, I have to be careful about indices sliding back and forth. If I delete something, I have to throw away all my old object specifiers and get new ones.

If you've made an application scriptable before, then you know one of the most difficult parts is handling the making of new objects. The way that Cocoa Scripting worked is it would allocate an object of the class that it was interested in. It would call init on that object with no parameters. Then it would call set on that object to set the various properties that were specified in the make new command. And then it would call the insertion routine that we saw earlier to add it to the collection.

This was great for a lot of applications, but it wasn't so great for other applications. First of all, your designated initializer might not be init. You might have another initializer that needs to be called for your object to work correctly. An excellent case of this is Core Data. Core Data really brought this problem home to us. Making Core Data applications scriptable was difficult just for that reason alone. Core Data has an initializer that takes several parameters, and somebody needs to call that.

What we've done is we've added a new object creation interface. It's a method that you can define on a container object that will receive all of the information needed to create an object all at once and allow you to determine the order of the operations. This works great for core data. It works great for approaching existing applications that have complex initializers. It works great for a lot of people that were finding scriptability very difficult for this reason.

The first thing that happens is the class of the object that's being defined in the make new

[Transcript missing]

In the case of the preview dictionary that we were showing you before, the class would just be NSWindow because that's what's defined in the standard dictionary. And the key would be orderedWindows because that's the collection it's going to be inserted into later.

You also have the option when you make a new object to pass in some data using a with data clause. And what might be appropriate in the case of preview would be a list of images to open up when this new window is created. Those are going to arrive into your application in the contents value. In this case, the contents value is going to be an NSArray, and that NSArray is probably going to have NSURLs in it to define these files.

Then you're also going to want to be able to set properties on that object when you create it, and those are going to show up in your method that you code in the properties operand here. And that's going to be an NSDictionary that's going to have entries for height and width and values of 300 and 400 associated with them. So you've got all the data that you need to make this new object all at once. And so you make this new object all at once, and then you return it as the return value of this routine.

And then more or less immediately, Cocoa Scripting will call you back and tell you to insert it in the collection. This solves a lot of problems, particularly if you're trying to make a core data application scriptable, or again, like I said, if you've got a large existing application that has complex initializers for your objects. And that's a very common case.

Property accessors are important because it's how you differentiate one object from another. Chances are, like I said, if you're using things like key value coding, key value observing, and bindings, you've already got a lot of these things defined. And the old way to do it was to just have a pair of accessors that would have the name of the property and then set the name of the property, and you'd define both of these. If we wanted to have a name for an image, this would be the pair of accessors that would get us at the name property and allow us to set it.

Key-value coding also can do some introspection on your object and actually find your instance variables with the correct name and hook them up. If you're really bindings-centric, you may be depending upon this already and you don't have any accessors. You just let bindings access the instance variables directly.

Well, scripting can do the same thing. You don't have to write any extra code that's just for scripting. And if you've graduated to Objective-C 2.0 with the @Property directive, again, Cocoa Scripting knows about this. It finds it for you. You don't have to write any code. It all just works.

When you're implementing commands, when you're done right at the very end, you can't think of anything else to do, and you've finally gotten down to the commands, you're going to have to make some decisions about how to implement them. And there's basically two different ways. You can do them object first or command first.

Okay, how do you decide which to do? Always consider object first first. This is, we're asking you to do an object-oriented solution. So obviously we want you to do the object first method if you can. Any command that acts on objects independently is suitable for this method. Rotate's a good example. No matter how many pictures I tell to rotate, each picture just rotates individually on its own and doesn't need to know about all the rest of the pictures that are out there. Here I'm saying pictures instead of images.

That's not the case with some commands. If you have an align command, if you've got a bunch of graphics in a document and you want to align their left edges, you're going to have to act on all of those objects at the same time. Those objects do need to know about each other.

One of them has to be chosen as the left edge that everybody else is going to be lined up to. You may decide for purposes of your application to just pick the first one. You may decide for purposes of your application to pick whichever one is farthest to the left already.

So you want to be able to look at all of those objects all at once. To do that, you want to do command-first dispatching. And to do that, you define a Cocoa object that represents the command, the align command. You inherit from the NS script command, and you implement a method called perform default implementation.

And what's going to happen is when a scripter issues this command, an instance of this align command object is going to be created, and the perform default implementation method is going to be called. And then you get your opportunity to act on the entire collection all at once.

Cocoa Scripting uses your application as a set of callbacks. And once you're familiar with it, it is very logical, it's very predictable, and it's going to make a whole lot of sense to you what's going on. But there's a learning curve, and you're going to have to find out, how does Cocoa Scripting use my application? What callbacks do I get? The toughest part of debugging scripting implementations when you first get started is, where the heck do I put the breakpoint? What's going to happen next? I can't get my program to stop.

Well, one way that you can find out is to use some kind of logging mechanism. Just put a little macro, sprinkle it around in your application. I put it at the very beginning of every single routine that I write that has anything to do with scripting, and a lot that don't. I put it absolutely everywhere.

And you can have something really simple. This is what I started out with several years ago. But then Brooke Callahan from the Automator team, he put me wise and told me that I could have a lot of stuff done for me. I used to have to type in the method name. I used to have to type in the file name. Now I don't anymore, because I've got a little bit more complicated macro.

And again, I put this absolutely everywhere. And just by changing that zero to a one, I can turn this on. Just make sure you don't check it into the source repository with the switch turned on, or everybody else that works on the project is going to get real mad at you.

I maintain an application called System Events. If you're an experienced scripter, you may be familiar with it. One of the things that System Events can do is it can tell you if the script menu is enabled. It can tell you if that little script icon is up in the upper right-hand end of your menu bar. And you might want to know that if you're doing some kind of installation because you might have scripts that you want to install in there.

And if you turn on the logging in System Events and you issue the Get Script Menu Enabled command, then you'll get these messages down here at the bottom. And my macro automatically sticks my initials in there. That way, if I'm working on a big project with a lot of people, I can tell which messages are coming from my macros and which messages aren't. And my initials are not a string that's likely to show up accidentally in some other word.

I also have the name of the file, so I can go back to the source file where the problem is, or at least where I want to put the breakpoint. Because that's what I'm trying to learn here is what is Cocoa Scripting doing? Where do I put my breakpoints? How do I follow through? It'll also give me the line number, so it's really absurdly simple to know where I need to put my breakpoint. It'll give me the name of the routine that's being called, and sometimes that's all I need.

Sometimes I don't even need to go and look at the source file. I say, oh, I'm getting called there. Yeah, that's wrong. And also, if there's any key inputs to this routine that are going to tell you something about what's going to happen, you can put those in the logging macro as well, and you'll be able to see those in the console log.

You turn this on, recompile your app, run your test script, and you'll get a log of everything that your application did in order to satisfy the requirements of that test script. Turn it back off, the macros compile again, everything goes away, and you're ready to ship your code.

Testing is really important, again, for two reasons. First, it's one of the great benefits that you get out of making your application scriptable. You can test not just the scripting part of your application, but the whole back end, the whole engine that makes your application as great as it is. You can test that and make sure that you're not breaking anything.

So you want to do repeatable regression testing. You want to access every element. You want to create and delete every kind of element. You want to move things back and forth between collections. You want to access all the properties of all the elements. And you want to exercise all of your commands that you created. And you want to exercise all of the commands from the standard suite that you support as well.

These examples here are in the form of AppleScript. That's what I'm most familiar with. But if you attended the talk yesterday talking about Apple Event Bridge and the various language bridges, you'll know that AppleScript is not the only language you can use anymore. You can use Ruby. You can use Python. You can use Objective-C. You can use any language that has a bridge to Objective-C. So you can write your tests in whatever scripting language you like. You don't have to use AppleScript.

And Ruby and Python have some testing infrastructure built into them to drive suites of unit tests. You might want to make use of that to even further automate your testing. But these examples are all in the form of AppleScript because that's what I'm most familiar with. Again, you want to get at everything in your application every way that you can. You want to test all of your element accessors. And you want to use all the various different ways of specifying which element you want to talk about.

You want to do some of these special named indices and negative indices. And if you're not familiar with AppleScript, negative indices count backwards from the end of the collection. So if you want the last thing in a collection, that's index minus one and so forth. And you definitely want to test whose clauses or whatever the equivalent is in the language that you're writing. You want to create some kind of complex predicate. You want to ask for a collection of objects based on the values of the properties. And the reason why you want to do that is because that's what your customers are going to do.

This is the power of the language. Going after things one at a time, iterating through the entire collection, pounding on things with a little hammer, that's not the interesting part. The interesting part is mining the data, going fishing, finding out what's in there, getting at things by means of a query, and then acting on those results.

You're going to want to test your element management. That's one of the potentially most difficult parts of your program. That's where if things go wrong, you can have data loss. So you want to make sure that this works very well. You want to create new objects. You want to delete new objects.

And this is great for stress testing as well. Creating and deleting objects thousands at a time is great for stress testing. You want to get at all your properties and make sure that that works. You can get at the properties as a group. You can get at the properties individually by name.

You want to create repeatable regression tests where you change the values of a property, but then you change them back to what they were before. So if you run this test again tomorrow, you can expect to get exactly the same results. You can even automate the comparison of results if you're creating suites of automated tests in your favorite scripting language.

You want to test edge cases. Push your program right up to the limit of what it can do and make sure that that works correctly. And you also want to test error cases. Make sure that the scripter gets back some kind of reasonable response that's going to help them debug their script and figure out what it is they've done wrong. If you need more specific information about how to return particular errors, come and see us in the lab and we can tell you all about that.

And with your commands, you're going to want to do the same thing. You're going to want to test your commands with just default parameters. Just issue the command and see what happens. Make sure it doesn't do anything bad. Make sure that it presumes some reasonable defaults and does something useful. And you want to do it with all the parameters specified as well.

[Transcript missing]

And last and probably most important, you want to include some real-world test cases in your test suites. We talked about getting feedback from your online community. We talked about your end users becoming very loyal to you because they've put you in their automated workflows and now they're dependent upon you as part of their daily lives.

You want to make sure that you don't break those workflows. You want to make sure that you don't hurt those people that love your application because of its scriptability. And you also want to make sure that your big customers, if you've got one big customer that accounts for two-thirds of your revenue, you definitely want to make sure you never hurt that guy. So what you want to do is communicate with him. Get his scripts that he actually uses on a day-to-day basis.

Put them in your test suite. Make sure that they never break. Retain the results. Compare the results from release to release. And validate your release before you send it out. You don't want your best and most loyal customers to be disappointed with your newest release because you broke their weekly workflow.

If you want more information, you've got lots of sources. Sal Sigourian is our Product Manager of Automation Technologies. His name gets longer and longer. And he's the Product Manager for both AppleScript and Automator and all the language bridges. John Montbriand is your Developer Technical Support contact. If you get a message to him, he'll make sure it gets to the right engineer in the right group and that you get some kind of answer back.

Lots of documentation online. Techno 2106, the scripting interface guidelines, the table of Apple event terms and codes so that you can pick your 4-byte codes correctly. Lots more documentation. And the last one up there is just the site for the show itself. Each session will have its own page. You can go to the page for this session and there'll be some wonderful links there for you to follow. Tomorrow at 10.30 a.m. downstairs in Mac Lab A.

We'll be there to help make your application scriptable. We can go through a blind interview and help you design your dictionary. We can help you write code. We can help you write tests. We can just give you general encouragement to make your application scriptable and show you how it'll interact with other people if you do, with other applications if you do.

And so, to sum it all up, customers get a chance to solve their own problems, which is great for them, and it's great for you. You spend less time hunting for regressions because they're caught automatically. You spend more time on beneficial features that you know lots of people are already interested in. The customers spend less time doing the same thing over and over, learning to hate your program. And they spend more money ordering upgrades and new desks for your program. Everybody wins. Thank you.