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: wwdc2007-205
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 205
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC07 • Session 205

Making Your Application Scriptable

Leopard Innovations • 1:03:45

Scriptablity allows customers to use your application in ways you never thought of. Leopard brings the ability to script applications using Ruby and Python, as well as AppleScript-even Objective-C code can access an application's dictionary through the new Scripting Bridge technology. Learn how and why to facilitate and use effective application scripting in this eye-opening session.

Speakers: Chris Nebel, John Comiskey

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good morning, thank you all for getting out here at such a bright and really unnaturally early hour for programmers. Welcome to session 205, making your application scriptable. I'm Chris Nebel, I'm one of the engineers in the scripting and automation group. So, this morning, the wonders of scripting and scriptability.

Scriptability is wonderful for your users of your application. They can solve real problems on their own. It's great for you, for a whole host of reasons, we'll be going through a bunch of those, and it's not a whole lot of work actually. Possibly far less than you're expecting.

If you've been following standard design practices, you've probably written half the code you need already, and we'll talk some more about that in a bit more detail. Why be scriptable? We go through a bunch of the reasons what you'll get out of it. Some interesting new things in Leopard that you can do with scriptability. And then I'll hand over to John Kaminsky who will talk a bunch about the how. Designing a dictionary, writing code, and testing it all. So, let's begin.

Why be scriptable? What am I going to get out of this? So, first off, some of you are probably new to this. I should say, I should clarify what the heck I mean by scriptability. Scriptability is simply the ability of your application to be controlled by another application, a program, instead of somebody sitting there with a mouse and a keyboard.

Now, this is just like an API, and you know, you've seen lots of different APIs, they have lots of different shapes. We're not talking about just any sort of scriptability here, what we're really talking about is OSA compliant. OSA is short for Open Scripting Architecture. It's kind of this little universe of technologies that all work together, it's the languages, the scriptable applications, and it's the protocol between them using Apple events. All that together makes up the OSA.

And the system works a lot better if all of the applications play by similar rules, if they have a similar shape and protocol to their scripting interface. Means that all the applications work at least a little bit the same way. The Macintosh is kind of like this. You go to any application, you know that there's certain basic commands that work the same way, you know, a button always does a button like thing, a button like thing.

So, OSA compliant, and these are all written down, they're all, it's a whole set of guidelines, and the scripting interface guidelines which is techno 2106, but the one particular bit that I want to call out now is that we're talking about model scripting, not view scripting, and this is model and view in the same sense as model view controller, which most applications follow, even if they weren't doing it entirely intentionally.

So, the model is your, it's what your application actually does, it's your application's view of reality, as it defines it, it's the actual objects and operations that it defines. The view is simply what that looks like on screen, you know, the windows and menus and what not, how you present it to the user, and when some people hear scriptability, they think, you know, oh, I can, you know, there's an accessibility API, right, that I can get in by system calls and find out where all my menus and buttons are, and I can poke those, and you know, that's, I can write a program to do all that, I'm done. Well, except that what you've actually got there is view scripting, and view scripting by its nature is extremely brittle, it depends on precisely how your interface is laid out.

So, if you ever change your interface, if you ever move anything around, scripts break, and as we'll discuss more in a bit, that's bad. Did you really want to commit to exactly the interface you have now for all time? Probably not. Model scripting on the other hand, this talking directly to what your application does, that's much more durable. It's much more comprehensible, it changes, it changes fairly infrequently, and when it does change, it changes in relatively predictable ways, and it's also easy to put in backwards compatibility so the old scripts still keep working even though you aren't directly revealing that stuff anymore.

So, you decided, okay, I'll start scripting and I'll buy this, what are you going to get out of it? The first thing you'll get as soon as you implement any of it, is that you've got an automated testing tool now. You've got an automated testing tool that can test your application, and of course you can do this to view all sorts of useful things.

You can do regression tests, you can, you know, make big changes, and make sure that you didn't accidentally change anything you didn't want to. You can do stress testing, what happens when you throw 10,000 cards at address book, something like that. You can validate releases before you ship them, make sure that all the features that are there are still working the way they're supposed to.

So, this is all very useful. The next thing, and again, before you even ship this, is that you start getting to integrate with other scriptability, other parts of the OSA universe in the system, so you can start writing Automator actions for instance. You can start writing scripts, you can write entire applications that talk to your application using AppleScript Studio.

And you can even leverage other scriptable applications. You can get other applications to do your work for you. So, if you need to send a mail message, you can talk to mail. If you need something graphed, you can tell Microsoft Excel to do it, if you need a diagram drawn, you can get OmniGraph to do that for you.

There are entire applications that are written doing nothing but this. There's a Kinkless GTD, that's a task management software, that's really just a set of scripts that talks to Omni Outliner, right. They didn't write any of their own outlining or drawing code or anything like that, they let Omni Outliner do all of it.

Okay, so this is pretty cool already. Now you actually ship the thing, and now your users get to start playing with it. So, they get about as much of benefits out of this too. They get to solve real problems on their own, and invariably your application doesn't do precisely what they want, right, they need to do some sort of customization. Scripting gives them the tool to do that. And you know, they can do it on their own. They don't have to come to you saying oh, I need this feature, please, please, please.

No, they just get it done immediately, and if they, if they talk about it, they will, scripters love to brag, you get feedback on what they did, and so the customers get their work done faster not only because they get to write their own features effectively, but because now it's automated, right.

Humans are slow and, you know, they get bored, and they get distracted, ooh, look iPhone. Computers don't have that problem, they'll do, you know, they're about as smart as a box of hammers, but they will do the same thing every time repeatedly, and they don't mind working weekends, or if they do, we haven't heard about it.

So, once your customers start using it, you know, it starts to flow back to you again. When a customer buys your application, they aren't really buying your application. What they're buying is a part of a solution for them, and once they build your application into your, you know, they're building a solution, they're building a workflow.

Once your application becomes part of that workflow, that's the product they actually care about, that's the product they want to keep, and they will never get rid of your application, as long as you're part of their workflow. Never. They will just keep buying your application, keep buying reps, until you break their workflow.

This is where that, you know, don't break scriptability part comes in. If you break their scripts, then you've broken their solution, and yeah, they're going to start looking around. So, finally, you get to, you get to spend less, spend less money, or spend your money more effectively, because you're letting your customers doing the customization.

You don't have to write every single feature for them, they can do a lot of it for themselves, and you can watch what they customize, you know, by feature customize is custom, a lot of people will be doing things that are interesting only to them, but you may notice that, oh, hmm, there are a bunch of customers doing this particular task over here, maybe that would actually be a good thing to add as a real feature. So, you get out of this is that you get to spend your time and money more effectively doing the things that actually pay off for you and your customers.

So, that's the virtues, and there's some new stuff in Leopard. Some new, interesting things you can do playing with scriptability. First up is AppleScript. There we go. AppleScript is the original scripting language on the Macintosh, it's been around for about 14 years now. We expect it to be around for much longer.

And, you know, it's been working fine for that time, but we're continuing to make improvements, so in Leopard there are, there are actually lots and lots of little things, but two really big ones is one it's all Unicode all the time now, so you don't have to worry about, you know, there used to be two different string classes in AppleScript that are like, as many as five depending on how you counted. You don't have to worry about that any of that stuff anymore, and we improved the application objects. Obviously this is kind of the art and soul of AppleScript. You tell applications to do stuff for you.

It's a bit more flexible, and a bit easier now. You can tell applications by ID directly, you used to have to do this four line dance to do that. If you need to find out an application's running, there're a bunch of built in properties now that work just built in to AppleScript, so if you need to find out if an application's running, you can ask exactly that now, and it works.

So, the really, the really interesting one is something we call scripting bridge. So, you know, you want to, you want to control iTunes. You have an application, you're doing some sort of little I thing, iTunes control, or you need to talk to iTunes. Now, the traditional way, you know, like I said, Apple Scripts been around for 14 years, the traditional way to do that is you write an AppleScript to do that, so okay, but you know, say you're going, you're writing a real application here, you're actually working in Objective C, so now what do you do? Well, the gut of this communication is a protocol called Apple Events, you know, inner process communication.

And there's a perfectly well defined API for sending Apple events from Objective C. It's called NS Apple event scripter, you know, been around since I think it's been around since Mac was ten point oh, and so, you know, say you want to get the name of the current track of iTunes, you know, slap that up on the screen. Okay, no problem, all you have to do is write this.

Naturally, people lack enthusiasm for this. So, what most people actually do is they go back to AppleScript because there's a class to do that to, so what you do is you write a little script, that just, you know, literally a little AppleScript, stick that in your application, and when time comes, you compile and run, and you get your answer back. And obviously this is a lot simpler, and it works, but it doesn't work real well.

Compared to the NS Apple Event scripter, yes, it's less effort for you, but it's a lot more effort for the computer. You wind up spending somewhere between ten and a hundred or even a thousand times of execution at a time in really bad cases. Takes a bunch of additional memory.

Getting values into the script and back out to objective C can be kind of a pain, and the larger your script gets, the more logic you try to stuff in there, the worse the three other problems get, so that's problem of scaling. So, what we wanted was something that, you know, is easy to use, but still has the efficiency of the direct apple events, and that's what scripting bridge is.

So, three steps to use it. Step one, generate a header file for the application you're interested in, iTunes, and add it to your project, as the command line tool to do this, this is what it looks like. Step two is you link to scripting bridge dot framework, new in Leopard.

Step three, profit. This is what the code looks like. The first line gets a reference to the iTunes application, and the second line gets the current tracks name, and returns the result as a NS string, there's no translation to, you know, NS Apple event scripter needed, and since this is Leopard, we have Objective C too, it even works with property syntax. Doesn't get much simpler than that.

( Applause )

Thank you. Talk is cheap. Let's see a demo.

( Silence )

All right, so, little iTunes demo here. So, this was actually inspired by Skype, a voice over IP application, and Skype has this clever little feature that if you're running iTunes, you're playing your music, and a call comes in, it'll automatically pause iTunes, and then when the, when you hang up the call, it'll start iTunes playing again, and it's actually a little clever about it, it fades in, so watch the volume slider when I do this.

( Music )

So, this is written going the kind of traditional way. It's using NS AppleScript to compile and execute an AppleScript on the fly, and for these two buttons, it works well enough, because these are only sending a few events, but if you wind up in a position where you have to send a lot of events, like this is going to crank through, whoops, haha, I just stole my own thunder, and ran the wrong version.

( Silence )

ITunes demo of NS AppleScript.

( Silence )

Okay, if you have to send a few thousand events, be sure to act really surprised when the next one comes up. It's actually drawing on every twentieth one, because otherwise you might have testing drawing performance, but it's cranking all the song titles in my library, about 13 hundred of them, one at a time. So, you know, we could probably do better than this.

So, if we use the scripting bridge version, then the, you know, we can still play, and pause, and song titles are just a little bit faster.

( Applause )

Wasn't that amazing? Yeah. And there's one more thing to this too. You know, I showed you the slides working, and it's all about Objective C. This program is not actually written in Objective C, it's written in Python.

( Applause )

Stop that.

There we go. So, what's happening here is kind of clever. The, there is a bridge from Python to Objective C, it's called PyObC, it's part of the system in Leopard. And this lets you get at basically any Objective C class. You can just talk directly to the Objective C runtime from Python.

So, for the old version, we're just using NS AppleScript to do this. And like I said, this works, but notice that, you know, an awful lot of my code here is actually these strings which are AppleScript, and I have to do this odd little dance to get a result from them. So, this is, you know, this is less than ideal. This is what the scripting bridge version looks like.

And what happens here is actually kind of clever. When I get a reference to the application what it actually does is it goes out and gets the scripting interface from iTunes, and uses that to construct Objective C classes and methods that back everything that iTunes does, and once you're there, I can use them just like any other framework class, in, you know, foundation or AppKit. So, here it is getting the name of the current track. Here is that fade in loop again, let me get the side by side with the AppleScript version.

So, instead of using just kind of the string, I'm actually using, you know, looks like real Python, or real Python luke, and it even works with collections. So, you know, get all the tracks for I and P, whereas with AppleScript, I had to actually, you know, go get the count separately and then do an injure count, and then, yeah, whatever. All right. Slides, please.

( silence )

So, the scripting bridge works very nicely with Python. I use the name of the current track bit again, and this same stunt works equally well with any language that has equivalent Objective C bridge. For instance Ruby. Ruby Cocoa is built into the system in Leopard. So, here's what the name of the current track looks like in Ruby.

It's really quite straightforward, works very well. So, the practical upshot of this, there, well there are two actually. One is that if you're on the scripting side, then your choice of languages just got a lot bigger. You can, you don't have to use AppleScript if you don't want to. If you do want to, great, believe me, we're continuing to support it, but if you don't, then that's fine too.

Go use Ruby, go use Python, go use Objective C, those work fine, and on the flip side, if you are an application developer, there are a lot more possible people using your application, and its scriptability, and some of them are written in objective C, maybe, you know, these people are going to be shipping real applications based on your scripting interface.

You really don't want to break them. Your scripting interface is API now, you need to take it a bit more seriously. But a whole new world of possibilities there for development and using scriptable applications. And with that, I'm going to hand over to John Kaminsky who will talk about the how.

( Applause )

Good morning. My name's John Kaminsky, and I'm an engineer in the AppleScript group, and thanks very much for being here. So, we're part way through our talk. We talked about why be scriptable and what's new in Leopard, now we're going to talk about actually how to construct this.

First step's designing a dictionary. Very important. We want you to spend a good chunk of time on that up front. Then you've got to write some code to support that, make it all work, and most importantly we want to test it very thoroughly before you send it out.

Designing a dictionary. This is about empowering the end user. Chris said that you give the end user the ability to solve real problems on their own, and constructing a good dictionary is the way that you do that. We want you, and we encourage you very strongly, to deliver an object oriented solution. This is the most powerful thing for the scripter.

If you simply create a bunch of commands that they can run, then you constantly have to anticipate what their needs are going to be. You're constantly racing to stay out in front of them. If you give them an object oriented solution instead, you've given them a set of tools that they can use to construct their own solutions. So, we really want to empower your users rather than try to anticipate their needs. You'll never be able to stay out ahead of them if you do.

You also achieve interoperability. If other applications have followed our recommendations, and given their users object oriented solutions, and you do the same, then your application will interact better with theirs. The scripters will be able to create these solutions more seamlessly, and will be less apparent that you're using two, three, maybe even five different programs to get the work done.

The interoperability will flow more smoothly because similar applications behave similarly. I draw your attention again to tech note 2106, the scripting interface guidelines. These are a lot like the human interface guidelines. The purpose of those was to make applications look and feel the same. We want to achieve the same thing with scriptability, that the scriptability of one application looks and feels the same as another. That allows your scripters to construct their solutions more seamlessly, they flow, the applications work together as one, and whoever uses this solution begins to think of it as just a single application.

There's a lot of information in the scripting interface guidelines, and we want you to read it all and take it to heart and do things the way that the guidelines say. There's a few red flags that'll come up right away as soon as you start designing your dictionary, and I want to point some of them out to you. You want to favor objects over commands. Like I said, if you've just got a big list of commands, you're constantly trying to anticipate what your scripters are going to want to do.

If you favor objects over commands, then they can recombine things in fashions that you may not have anticipated that solve their problems. One indication that you might be doing this wrong, is if you've got class names that appear in your commands. This first one here, delete front window, that's supposed to be close front window, sorry.

That's a perfectly good line of AppleScript, that's something that people are going to want to do, but you don't want that to be a command name. Delete is a verb. Windows an object. Front is an index. They should really be three different things. So, in your dictionary you're going to want to define a window object and a delete command and allow your users, allow your scripters to recombine those.

Likewise, conversely, if you've got verbs in your class or property names, that's an indication that your dictionary might not be designed according to the guidelines. A very common occurrence is for the word is to appear in the name of a Boolean property. This is great inside your code.

This is very common inside Cocoa applications to see method names that begin with the word is. You want to avoid having that in your scripting dictionary, AppleScript is an English like language. It flows. It's been described as a read only language. Easy to read in AppleScript and see what it does, not necessarily easy to write one.

If this word is pops up in the middle of property names, it's going to make your AppleScript read in a clunky fashion. It's going to make it hard for people to understand what it's doing. So, these are things to avoid. Another thing that you want to do in your Apple Script dictionary is hide some of the grittier details of what's going on inside your application. You don't necessarily want to turn every scripter into a Cocoa programmer.

You want to let them write something that flows naturally, makes sense, is easy to read, and actually does the job. So, when you're hiding these gritty details, one of the things that's going to happen is you're going to avoid intercaps, you don't want intercaps in your dictionary anywhere, and likewise, you don't want any underbars in there. If you're doing these things, it's an indication that you're objective C backed, you're objective C back end is leaking into your dictionary, and it's going to make your dictionary harder for people to use.

What are you going to design? It's hard to get started. You don't always know what to do first, and what we suggest that people do is to design globally and implement locally. By that, we mean, think about the entirety of your application. Everything it can do. Inventory all the objects that it has. Inventory all the various operations it can perform on those objects, and do that up front.

The reason being, you're going to want to support all of this stuff at some point in time in the future, and it's better to have names for everything before you get started. It's terrible to get halfway through your implementation and realize you gave something the wrong name, and now it's going to be very difficult for you to add the next feature, or extend what you've already got.

Now, after you've done that, after you've created this massive dictionary that does everything that exposes everything that you're application does, you're going to look at that and you're going to gasp, and you're going to say I cannot possibly do this in one release, what am I going to do? We want you to scale that back, and break it into phases for each release, and the suggestion that I give to people is draw a little line around a few objects and a few commands in your dictionary, and say can I solve one or two interesting problems with just these few objects. Am I getting feedback from the field that people need solutions for certain things, and if I just give them maybe three objects and two commands, they'll be able to do that.

That's doable in a single release. Get that done, get that dictionary into the hands of your users, and they'll, they won't be shy, they'll create solutions using that dictionary, and they'll feed back to you what it is they think they need next. And then you'll be able to spend your time on implementing those things that your scripters are going to be using right away. We said before, you spend your time where it does you the most good. This is one of the ways. You start this two way communication with your scripters, and then your scripting dictionary grows release over release, in the most important and effective way that it can.

It's also okay to have features that are in your GUI that don't appear in your scripting dictionary. One example of this is finding things. Various applications have very sophisticated ways of finding things. Smart folders, and smart collections exist in a lot of applications today. They're usually defined using a big dialogue that allows you to construct Boolean algebra to select things based on their properties, and create a collection that way.

It isn't absolutely necessary that you replicate that in your scripting. That's because AppleScript already has whose clauses. Who's clauses are a very powerful way of selecting objects based on their properties. It isn't necessary that you reconstruct that. So, you're probably not going to even if you have a very powerful find feature in your application. It's highly probable that you don't have to reconstruct that in scripting, because whose clauses actually already do that for you.

Likewise, it's okay to have scripting only features in your application. One example of that is the address book has a lot of additional fields like nickname, anniversaries, friends, spouses. These things all appeared in scripting first, and didn't show up in the user interface until later. Once these properties were named, it was pretty easy to put them in the dictionary. It took a little bit longer to design an effective user interface, and effective graphical user interface for them.

So, what the guys that take care of address book did, they went ahead and put them in the scripting interface right away, and it wasn't until the subsequent release that they showed up in the UI. That's okay. All you're looking for is down the line, when you feel you're done, that you have parallel functionality between your graphical user interface and your scripting interface, even if they don't look precisely the same Where do I get this design. Excuse me.

( silence )

The important thing is that you want to, you want to design to an end user perception. It's going to be end users of your application that write these scripts, and to the extent that you can satisfy their expectations, to the extent that you can use the same terminology that they're going to use, they'll find your scripting dictionary easier to use, and to do this I've used a technique called a blind interview. What's that? It's not really like this. You don't need a blindfold. You don't need the bare bulb, but the fedora, the fedora helps a lot.

Your scripting dictionary's going to reflect the internal structure of your program to a certain extent. Again, I said you don't want to turn every scripter into a Cocoa programmer. You don't have to show them all the nasty internals of your application, but if you use Cocoa Scripting and you will, because that's the way to go, your internal structure, and your scripting dictionary do have to align to a certain extent.

So, feel natural doing that. If your internal structure and your Apple Script dictionary parallel each other to a certain extent, that's a good thing. But stay within reason. You're going to want to simplify that for people. If you've got little helper objects that the scripter doesn't really need to know about.

If you've got collections of things that end up acting like a single thing, you can hide that from the user, and this is just the classic data hiding capability of object oriented programming. An object behaves in a certain way. It's not necessary that the scripter know exactly how all of those internals are done.

As Chris mentioned, AppleScript is 14 years old now, and 14 years ago, things were very different. Computers were smaller, networks were slower. This is a USB thumb drive. It's got as much memory on it as everybody had in this room had on their Macintosh 14 years ago. So, 14 years ago, the correct solution was 4 byte codes.

Every entry in your dictionary has a human readable term associated with it. It's also got a 4 byte code associated with it. That's the information that travels back and forth in the apple events, so when you're creating your dictionary you're going to need to assign 4 byte codes to the human readable terms, and one of the biggest stumbling blocks is how do I do this? Where do I get this information.

The important thing, the important message that we want to get to you is consistency with other existing applications. If a human readable term is in use, in other AppleScript dictionaries, you want to copy that, and you want to copy the 4 byte code that goes with it. This makes your application interact better with other applications. It makes your application interact better with scripting additions which behave as though they were part of the language, and they make your application interact better with AppleScript itself which has a lot of predefined terminology.

So, the bottom line is if you use the same human readable term, use the same 4 byte code and vice versa. Where do I find them? If you go to the developer site and search for apple event codes, the first hit you get will be a web page that we've constructed that's got 3000 pairs of human readable terms and 4 byte codes.

Start there, and if you can find the human term that you're looking for on that list, go ahead and use the 4 byte code that's associated with it. If you don't fine your term and you have to invent something new, then the converse is true. Make sure you pair that term up with a 4 byte code that is not in use by anyone else.

And when you're done, all of this goes into a scripting definition file. The scripting definition file is the newest format. We've gong through a series of iterations on this, back in system 7, 8, and 9, we had AETE resources which were difficult to construct and read. When system 10 came along, we had script suite, and script terminology files, which were a little bit better, but sometimes difficult to work with, and now we've gone to scripting definition files.

All the old ones still work. If you've got an existing scriptable application that uses an AETE or script suite or script terminology, don't worry, it's going to keep working, but the scripting definition file is more expressive. You can include documentation, you can include examples, you can include links to outside information that will help people interpret your dictionary and use it better, and here in orange are the things that you're going to have to invent.

There's the term, this is a chunk from the sketch scripting definition. Sketch is the canonical sample code that we make available to you for Cocoa and AppKit, and since Cocoa Scripting is part of AppKit, sketch is a Cocoa scriptable application, and in its dictionary, it defines something called a graphic, and a graphic is just anything you can draw in sketch.

A rectangle, a circle, a straight line, a chunk of text, and it's associated with the 4byte code GRPH, and it's implemented by a Cocoa class SKT graphic, and it's in the scripting definition file that you associate these three things together, so when your scripter types the word graphic, when your script editor translates that into a 4 byte code in order to send an apple event and when that apple event arrives in the sketch application, these three terms are tied together. Once you're done designing your dictionary, you're going to need to write some code to back it up, and as Chris said, if you're following existing conventions, a lot of this code is going to already be there.

And there's several different things that you're going to have to create. You're going to have to create element accessors that allow you to get at the objects that your application knows about, you're going to have to do some element management so that you can grow and shrink those collections as appropriate. You're going to want to have property accessors that allow you to get at the information that makes one object different form another, and you're going to have to implement some commands.

Element accessors come in several different types, but you're going to have to do at least one of these two. The first one here is also from the sketch application. It's the accessor that allows you to get at the graphics that are inside a given document. Since the number of graphics inside a given document is likely to be relatively small, and that collection of graphics is going to be around all the time because it needs to be redrawn, it's really going to be pretty easy to just return an array of graphics objects.

It already exists inside your application, you don't have to write any new code to do this. So, the first accessor here just provides the entire collection at once. Cocoa Scripting will grab the entire collection and do whatever is necessary to select the object the scripter is looking for.

If your collections are bigger, or perhaps don't exist at all times, you're going to want to be able to do this in a more efficient way. The example here still uses graphics. You notice that we're establishing a naming convention here. The term graphics is repeated over and over again in these accessors and that relates them all together, and relates them back to the scripting definition so that Cocoa Scripting can find them. Cocoa Scripting uses your scripting definition file at runtime to dispatch the events to the proper accessors.

If you collection is large, or does not always exist, a good example here would be all of the characters in document. You don't necessarily want a Cocoa object representing every character in a document hanging around all the time. That's a lot of overhead for something that might never get used.

You can implement access to those characters, or in this case still graphics, by providing a pair of accessors, one which will give the count of how many of these things there are, and one that will give you back exactly one object at a time based on the index, and again, Cocoa Scripting knows how to use this, and will use it to construct an answer to whatever it is the scripters looking for. You can implement one or the other of these or both, but you're going to have to have one or the other.

  • ( Pause ) >> Probably.
  • ( Pause )
  • Yeah. There's other ways of getting at your elements as well that if your backend supports an efficient way of getting at things by name, perhaps you've got some hashing algorithm that can take the name, munch on it somehow, and then grab the object directly, you can do that, and Cocoa Scripting will help you.

You provide another accessor called valuing graphics with name, and then instead of asking for the entire collection, or asking for something by index or going through the entire collection looking for one that happens to match a certain name, Cocoa Scripting will ask for you to hunt through the collection and get something by name. If you believe that you can do a better job than a simple linear search, then you'll want to implement something like this.

Likewise you can access objects by a unique ID. If the back end of your application is a real live database and it can get at things using a key and it can do it really fast, then you're probably going to want to implement valuing graphics with unique ID or valuing object name with unique ID, and that'll pass back one object that matches that unique ID, giving you access to the power of your backend which happens to be a database.

Some collections are static, for example the disc drives on the, oh no, that's not exactly, that's not a good one. The monitors, the monitors available on your machine is relatively static. You can't connect a new monitor without turning your machine off and back on again, so what you want to, but not all collections are like that.

Most collections vary in size over time, and you're going to want to be able to create new things and get rid of old things, and again, Cocoa script is going to help you do that, and extending this naming pattern, you can insert a graphic into the collection of graphics. I've got two different accessors here. That's because you've got two different options of how to do this. If order is important you might want to be able to, and your scripter might want to be able to insert an object at a particular point in the collection.

If order isn't important, or order has some kind of natural definition, then you can insert objects into the collection without specifying an index. A good example of both, actually, is a window. Normally, if you create a new window, you simply want it to show up in front of all the other the other windows.

You're creating it because you're going to do something with it right now. And in that case, the first accessor here would be sufficient, but it's also true if you're doing something in an automated fashion, that you might want to create a window, and you don't want it in front. You want it behind all the other windows, so in that case you'd want to use the second accessor, so again you can do one of these or both. There's a good chance that you're going to end up doing both.

When you're done with a window, you're going to want to get rid of it. You're going to want to remove it from the collection, and you do that with the remove from collection name and index, and this is going to find the object using the index, and tell the owner of that collection to get rid of it. So, you can manage the size and contents of your collections this way, and the last one here is object specifier. Element identification via an object specifier.

Every good scriptable object is capable of providing an object specifier for itself, and you can think of an object specifier as like a return address. If I wanted to get back to this object later, how would I do that? A command that creates an object should return as its result an object specifier.

The scripter can then take that object specifier, stick it in a variable, hang onto it, and get back to that same object later on. Some object specifiers are better than others. Index object specifiers are somewhat weak. If the contents of the collection changes, then the meaning of an index object specifier may also change.

Object specifiers by name are better, but because Apple Script allows non-unique names, they can be ambiguous. The most specific and exacting kind of object specifier is an object specifier by unique ID. It's up to you when you write this object specifier routine what kind you're going to return.

You're going to want to have your scripter's end use perception in mind when you do this. If people normally call things by name, you're probably going to want to use by name object specifiers. Again if your backend is a database, and your scriptors know,hey, I'm manipulating a database, then unique ID objects specifiers are the right thing, and if you don't have that kind of information available to you, index object specifiers will work, but like I said, they have their limitations.

The thing that makes one object different than another is the value of its properties. If you want to do interesting things with your objects, search through them, find objects that have certain properties, you're going to want to have property accessors. You're going to want to be able to get and set the values of properties, and here's a pair of property accessors for the name of an object.

These fit a pattern called KVC, Key Value Coding. Key Value Coding is a technology that allows cocoa applications to introspect their object and find out what properties they have. It's used in lots of different places. It's used in bindings, it's used in core image, it's used in core data. If you're using any of these technologies, you probably already have a lot of Key Value Coding compliant code in your application already. And that's great. Because when you go to do scripting, that means you don't need to write anything new.

But Key Value Coding is actually more powerful than that. It can actually get right at the instance variables of your objects directly without you creating an accessors at all, as long as you stick to this naming convention. That instance variable there underscore name will be treated exactly the same way as that pair of accessors get name and set name.

So, in this instance you don't have to write any kind of code at all, and if you're objective c two point oh, and you use objective c two point oh property definitions, they're automatically KVC compliant. You're going to get that functionality for free just because you're using the at property definition for your instance variables. So, like we said, you probably already have a lot of this code already and don't need to write anything knew.

The last thing you're going to want to do is commands, and I do mean the last thing. There's 13 built in commands in the AppleScript language. We believe that those 13 commands will cover about 80 percent of what you need to do. You don't want to add new commands if an existing command will do.

Particularly get and set. Getting and setting properties can often do what you want done rather than creating a complex custom command. But it is going to happen. Your application does something unique. Your application has some value added functionality that no one else has so there is no command to do what your application does. In that case, you're going to want to go ahead and create commands.

The examples that we gave last year, we used the sketch sample code again, was to take a couple of features that were present in the graphical user interface, but not in the scriptability, and to go ahead and implement them, and we chose two different ones to give an example of the two different kinds of commands. The first kind of command is object first dispatching.

A lot of objects act independently and can implement their responses to certain commands independently. Good example of this is rotate. If you tell something to rotate in sketch, you can tell one object to rotate, you can tell a collection of objects to rotate, but in the end, each object functions independently. It rotates through 90 degrees all by itself, and it doesn't need to know anything about anything that's going on with any of the other objects.

In that case, you're going to want to use object first dispatching, and the way that you do that is that you simply add a method to the graphics object itself called rotate, reference it in your scripting definition, and then Cocoa Scripting will route any rotate commands directly to that object, and if the scripter actually tells a collection of objects to rotate, Cocoa Scripting will call each of those objects one at a time, and tell them to rotate.

There's other kinds of commands that aren't like that. There's other kinds of commands that act in aggregate on an entire collection. And example from sketch is align. You can't tell one object to align. An object has to align to something else. Typically what you do is you tell a collection of objects to align their vertical centers.

Well, you can't do that by sending a message to each object individually. The objects are going to have to know about each other somehow, so that they can find their vertical centers and align them. In that case, you're going to want to do command first dispatching. The way you do that is you create a sub class of the NS script command class, and you implement a perform default implementation method, and the perform default implementation method doesn't have any inputs at all, reason being it's a method on the command itself. If you need to know anything about the command, whether it's align to the vertical centers, or the horizontal centers, or the left edges, that's present in the command itself, and the command can just introspect itself to find that information.

And the difference between a command first dispatched command, and an object first dispatch, is that the command first dispatch can act on the entire collection of objects all at once. So, you can see, depending upon the functionality it will be appropriate to do one of these or the other.

Cocoa Scripting uses your application as a set of call backs, and it will call your element accessors, and your property accessors in order to answer the questions that the scripter has asked. And it does this in a very predictable and logical fashion, but when you first get started, there's going to be a lot that you don't know, and typically the way that you figure out how your programs working or not working is by setting break points. It can be very frustrating setting break points when Cocoa Scriptings using your application as a set of callbacks.

You don't know what's going to happen next. You can set a break point, and it'll never get hit, and then you're left wondering what's going on? Why am I not coming through this part of my code? What did I do wrong in my code? What did I do wrong in my dictionary? One way to learn how Cocoa Scripting uses your application, what it calls and when, is to put in some kind of logging mechanism, and when I first started doing this, I used a very simple logging mechanism, just a couple of macros that allowed me to call NS log, and allowed me to turn this off when I built my deployment version.

And from this I learned a lot about how Cocoa Scripting worked, what it called me for, when, and what it expected back as its answers. And I strongly encourage that you do this. That you log every accessor call, every event handler, and any significant processing that you do. If you take a branch inside your code that's going to cause you to do something significantly different, log that as well, so that you can follow what's going on.

After awhile, with the help of Brooke Callahan, one of the members of our group, I made this considerably fancier, and added a lot of automated stuff. I found myself typing over and over again the name of the method that I was in, the name of the file that I was in.

I've added this all to my logging macro automatically. Now, simply by changing the value on that if statement, I can turn this logging mechanism on and off, and use it to find out what Cocoa Scripting is doing, and again, I put these things everywhere. Every single routine and every single object I've put one of these logging macros in there, and now since I don't have to type in a lot of additional information, it's really very easy, just a lot of copy and paste.

So, when you turn this on, well, you can't turn this on because you don't have the source to system advance, but I can turn it on, these are the messages that I get. If I tell system events to get script menu enabled, script menu enabled means that up in the upper right hand corner of my menu bar, there's a little script icon that I can pull down and select scripts that I use commonly and frequently to control my scriptable applications. Again, this is part of that lock in. If you put scripts in this, if you make your application scriptable, and you put scripts in the script menu, people will get used to using them, and they will use your program over and over again because it's easy.

When you run this, these are some examples of the logging that you'll get back. First thing in there is my initials. I put my initials in there because there maybe more than one engineer working on this project, and I want to be able to tell what in the console log came from me.

Mark Pitcherelli will be speaking this afternoon, and he'll tell you that when you're debugging your scriptable application, you should leave the console window open all the time because there's a wealth of information in there. To filter out my logging from that wealth of information, I put my initials in every logging message.

I also have the name of the file, and the line number that I'm on. This gives me pretty specific information. If something goes wrong, if some part of the foundation throws an exception because I've sent it some bad data, right before that, there's a line of code, if I go look at that line of code, I almost always find my problem.

Again, I put in the name of the method that I'm executing at the time, which can be helpful as well, and it's also possible for me to put in specific information about the input to that particular method. If I'm in the delicate handles key method, it's interesting to know what the key is, because that will tell me what I expect the answer to be.

And again, I've put these things everywhere. Put them in your application, turn them on, run a couple of sample scripts, see what Cocoa Scripting is doing. That makes it a lot easier for you to go back later and set break points and see what is going right and what's going wrong. When you're done, you're going to want to test this all.

You're going to want to make sure it works right, and probably the most important reason why you want to do this is you've given your end users automated regression testing tool, it's called scriptability. They will run their scripts that they've been running on previous versions of your application. They'll write new scripts. They'll find your bugs. They'll find your mistakes. If you create an embarrassing regression, and let it slip through, they'll find it. So, what you want to do is make sure that you do that first.

Your testing is going to parallel your code development. You're going to test your element accessors, your element management, your property access, and your commands. All that code that you wrote, you're going to want to have test cases for each and every one of them. When you're accessing your elements, there's lots of different ways you can do that in AppleScript and you want to make sure you test them all.

You can get the entire collection. You can get all the graphics that sketch has in a particular document at a particular time. Regardless of how that's implemented, whether it's done through the entire collection accessor, or if it's done through the count and index accessor, you want to make sure that it works. You're also going to want to get things by name and by ID.

Whether you've implemented those special accessors or not, somebody has to match that name. Somebody has to match that ID. It's either your code, or Cocoa Scripting. You want to make sure that that works Accessing by index is relatively simple, but again, you want to test it, because if there's anything wrong your users, your scripters, will catch it.

There's some other ways to access things. There's some indexes that have names. You can grab the middle object out of a collection, or you can grab some randomly chosen object out of a collection. You want to test those and make sure that they work. AppleScript supports negative indexes, which count backwards from the end of a collection.

You're going to want to test that and make sure that works, and the last and most importantly, you're going to want to test whose clauses. The power of the AppleScript language is in the whose clause. It allows the scripter to describe what it is he wants rather than just say this one here, number five. If he wants to describe an object that he's seeking or a collection of objects that he's seeking based on the values of their properties, say all of the customers in the local zip code, he's going to use a whose clause to do that.

Cocoa Scripting is going to help you out a lot in implementing these whose clauses. You don't have to write much code, if any, to get these things to work, but you want to test them. You want to make sure that they work. You want to make sure that your scripters get sensible answers, and that they get the same answers over and over again from release to release. So, make sure that you've got some interesting and powerful whose clauses as part of testing.

Element management, you're going to want to create and delete things. Again, here's a sketch example of making a new circle with certain properties, and again, you're going to want to delete things from your collection. Very often, everything works until you try to delete the last object, and then something goes horribly wrong. You want to make sure that you test that situation before your code gets out to your scripters.

You're going to want to get out all the properties of an object. Cocoa Scripting supports for you getting all the properties at once, but again you want to make sure that it works. It uses your application as a set of callbacks. When the scripter says get properties of rectangle one, the first rectangle object is going to be called over and over again to query each of its properties, and Cocoa Scriptings going to do that for you, but you want to make sure that that kind of access works. And you want to be able to get at each of the properties by its name, so you want to include all of that in your testing as well.

Some properties are read only, some properties are read write. For the read write properties in your application, you're going to want to test that that works, and we want you to do repeatable tests. We want you to put things back they way they were when you started, so we suggest a get set rest pattern. So, get the width of an object of one of the graphics in sketch, and save a new variable. Then change the width of that object, then change it back to what it was before, and make sure that that all works.

There are edge cases. The width of something probably shouldn't be any less than 0, but it could conceivably be 0. You want to make sure that that works correctly. And then there's error cases. You want to force your application right up to the limits of what it can do, and then push it over the edge case into the error case, and make sure that the scripter gets back some kind of descriptive error message that they can use to solve the problem.

Again Mark Pitcherelli will be talking this afternoon about the greatly improved error reporting in Cocoa Scripting. You want to make sure that you get those errors, and that your scripters will be able to interpret them and fix their scripts, so you want to make that part of your testing.

With the commands, if you create any new commands of your own, you're certainly going to want to make sure that they work, and you're also going to want to test the built in commands that are part of the AppleScript language, and you're going to want to do this in several different ways. You're going to want to make sure that if you don't provide any parameters at all, you just simply tell sketch to make a new circle, that it does something predictable and perhaps even useful.

If you make a new circle in sketch, it's going to put it at some default location in the document. It's going to give it some default size. You want to make sure that's what the scripters going to expect. You also want to make sure that when you specify all the properties of a newly created object, that that works as well.

And again, you want to take edge cases and error cases and make sure that they work, and give the scripter feedback that's going to be meaningful to them, so that they aren't surprised when they write their script and it doesn't do exactly what they wanted it to do.

And here is a good place to mention stress testing. Chris said earlier what happens to Address Book when you throw 10,000 cards at it. You're going to want to test your application that way as well. What happens to sketch when you make 10,000 circles. It's slow, that's what happens to sketch when you make 10,000 circles, but it's sample code, and it's instructive. You want to make sure that your application responds in a way that's going to make the scripters happy.

And this is a really important part. We told you earlier that scriptability will build loyalty with your customers. Once they've made your application part of an automated workflow, they're not going to get rid of you. They're going to keep you around forever, but only if you don't break their scripts. One of the best ways to make sure that you don't break their scripts is to get a hold of their scripts and make them part of your testing.

Chris mentioned Kinkless GTD. We use that as a test ourselves to make sure that we haven't done anything that breaks the scripting of Omni Outliner to the extent that Kinkless GTD doesn't work anymore. If that happened, that would be bad. There's lots and lots of people out there that use Kinkless GTD. They have a tendency to be CEOs and CFOs and we don't want to disappoint them. We want to make sure that they keep signing those checks and keep buying our products.

So, if you get your real world scripters, real world scripts, and make them part of your testing, then you know that you're not going to break their automated workflow, and you do this by monitoring your feedback sources. If you've got a mailing list, if you've got a blog, if you've got a user community out there that's active and interchanges this information, you're going to want to tap into that, and if you see a really interesting script, you're going to want to grab it, and you're going to add appropriate to your testing methodology to make sure that you don't break any of these things, and again, you're going to want this all to be repeatable.

You want to retain your prior results, and you want to do some kind comparison between what's happening now, and what was happening in the prior release, and you want to detect regressions. Some of the changes are going to be there because you want them there. Some of the changes are going to be there because you fixed the bug.

But there's going to be other changes that crop up that you don't want to have there. You want to catch those at the earliest possible moment and fix them. Like I said, you're giving your scripters an automated regression testing tool. They are going to catch your mistakes better than you catch them first.

So, we've dumped a lot on you today, and if you need more information, here's some people that you contact. You may already know Sal Soghoian, our product marketing manager. He can help you in many, many ways. He's a very experienced scripter himself, and knows a lot of the scriptable applications out there. If you're looking for solutions, he may have the answer.

John Montbriand is our contact at developer technical services. If you have questions about coding about developing dictionaries, go straight to DTS and ask for John. And we talked about the Technote 2106, the scripting interface guidelines, there's the URL for that. I talked about the webpage with the 3000 4 byte codes, there's a URL for that, and there's going to be a page for this particular session. If you go to developer dot apple dot com, wwwdc 2007 and look up session 205, you'll find a list of about eight or ten documents and links to sample code, guidelines, and other documentation that can help you get started in making your application scriptable.

There's also going to be a lab this afternoon. I mentioned Mark Piccirelli's session, those of you that want more, who are maybe a little bit farther along in making your application scriptable, or are just interested in what the newest developments in Leopard are, are going to want to go to that. Immediately after that, in the labs downstairs in section D, we're going to be having our application scripting laboratory.

If you're eager to get started, you can start before that. After this session's over, I'm going to take a little break, and then I'll be down in the lab. Just come down, tell the concierge there that you're looking for AppleScript help, somebody will be there to help you. You don't have to wait till 3:30 this afternoo0n. But don't miss Mark's session, there's a lot of important stuff there too.

So, in summary, what this is about is giving your customers, your scripters the ability to solve their real world problems themselves, so that you don't have to constantly be at their beck and call to add tiny little features that have small audiences. The developers, you don't spend as much time hunting for regressions.

Your automated scripting, your automated testing can do that for you, and you spend more time on the most beneficial features because you've gotten feedback from your customers as to what those beneficial features ought to look like. And your customers are going to spend a lot less time doing the same thing over and over and over again.

Their experience with your application is going to be much more friendly, much more happy, because they're going to do something a couple of times, they're going to like it, and then they're going to automate it. And in the end, they're going to spend more money on your software because it's now part of their everyday lives.