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-130
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 130
$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 130

Cocoa Scripting Enhancements in Leopard

Mac OS X Essentials • 57:57

Leopard significantly enhances Cocoa's scriptability support. You'll learn how to take advantage of improvements to Cocoa's support for .sdef scriptability declarations, error reporting, and type safety. Come see how providing rich scripting in your Cocoa app has never been easier.

Speaker: Mark Piccirelli

Unlisted on Apple Developer site

Transcript

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

My name is Mark Piccirelli, I'm an engineer in the Cocoa Frameworks Group. Today we are going to talk about the many improvements we've made to Cocoa support for scripting in Leopard. In particular, support for the .sdef file format which you use to declare new features, is much improved. Cocoa's default behaviors for a lot of the standard commands and things like that has gotten better and there are a bunch of new customization opportunities for your application.

First of all, I am going to give a brief introduction to making your apps scriptable. A little bit of overlap with what John Comiskey and Chris Nebel presented this morning in making your applications scriptable. But I'll go into a little bit more detail about the why in some things.

Talk about new features in the .sdef file format, including and also support for new kinds of types. Like custom value types, missing values and enumerator values. I'll talk about improvements to error sensing and reporting, a bunch of new APIs since last WWDC and finish of with some debugging tips.

Today's sample code, as I use so often, is Sketch. Sketch has been completely rewritten for Leopard. It's called Sketch 2 now and it's just lousy with bindings and key-value observing and key-value coding all over the place and it uses a lot of these new improvements, these scripting improvements that I'm going to talk about.

It's rewritten for Leopard. So first thing, declaring your application's scriptability. First word of terminology confusion to get out of the way. Cocoa and AppleScript both have long histories and so you know, use some words in different words. So first thing, what an AppleScript is called, a property. Cocoa programmers usually refer to it as an attribute or to-one-relationship.

An element class when I say that when talking about .sdef or AppleScript, I just mean a to-many relationship. You know, if you're familiar with Cocoa bindings or core data, that's what you know that concept as. And one more element of confusion that was pointed out is the word element is used in AppleScript and it's also used in XML. So I'll try and say XML element when I'm talking about the .sdef style file format.

So there's an old way and a new way to declare your application's scriptability. The really old way is this parafile format that Cocoa has supported since before Mac OS 10.0, called .scriptSuite files and .scriptTerminology files and they were kind of hard to work with. They drew this separation between like the implementation details and the localized things that you know, the users, people who write scripts would see and that turned out not be that useful of a distinction and it made things harder to work with. So you know, they're kind of on their way out. But of course, they're still supported for compatibility and you know, they will be for the foreseeable future.

But what I really want you to do is investigate this relatively new file format. Cocoa introduced support for it in Tiger, it's called .sdef file format. It's much easier to work with. It's more compact, it's an XML-based file format. It has a really direct mapping of the elements in an .sdef file to what you see in an AppleScript dictionary. The Cocoa-specific implementation details are in their own XML elements. You know, they're kept a little separate. So you know just you know, what only Cocoa is interested in reading.

So very brief refresher what a scripting dictionary looks like. This is part of Sketches as shown by script editor's dictionary viewer. Just showing you the class, the graphic class that has a bunch of properties. Simple ones like X position, Y position, width and height and some colors. And here's what the .sdef looks like that corresponds to that. I'll go into a little more detail about these different pieces as I go on, but you know, there's a class XML element with a optionally Cocoa XML sub element and property elements declaring you know, the various pieces of the class.

So and I'll go through the pieces of .sdef you know, in showing you the corresponding code that you get to write, you know that matches as I go. So the code you write. I'm just going to keep it pretty simple and quick. I'm just going to talk about the code you write to make our implementations of the standard commands work.

Get, set, count, exist, make, delete, duplicate, move, you know, these are commands that are supposed to operate on all sorts of objects. If you were in today's making your applications scriptable, you're encouraged to fix, fit your object model you know, into something that works with these commands. You can add more if you need to, but you need to surprisingly and frequently.

So the first thing you do is when you declare a class, like graphic, one of the things that you specify is the corresponding Cocoa class, the Objective-C class. And in .sdef, you do it like this. Cocoa class SKTGraphic and the corresponding code is not surprisingly a declaration of the SKT graphic class.

And why does Cocoa care about the classes? Why do you have to declare this in .sdef again? And well the first thing is the Make command. You know, it's one of the standard commands and you create a you know, you make a new graphic in Sketch for instance, by saying you know, make new graphic.

So and also it's important for type checking. When you declare a class, you're declaring a new type that matters to people that write scripts and also to your code. So you know, Cocoa uses that information to make sure that what's coming in, you know the Apple events that result from an AppleScript being interpreted, to make sure those are valid before it asks your application's code to do something.

Classes have properties. Here's a simple one. The fill color of a Sketch graphic and the property declaration looks like that. There's a type you have to specify and there's a Cocoa key and I'll tell you what that's for in a little bit. And here's the corresponding code. For every, for every property that's readable and writable, there is a getter and a setter that looks like this.

And why do you have to do this? Well for a couple of reasons. Objects specifier evaluation is the main one. When somebody writes a script that says graphic one of document one, what that causes to happen in your Cocoa application is a bunch of getter methods to be invoked and also for the set command. When you say set fill color of you know, of last graphic of front document, you know the center method that you saw there is invoked. And also, some other places where properties come into play is the "with" properties arguments of the Make and Duplicate commands.

So back to that same declaration and that same code, if you notice the type as the scriptor sees it is RGB color and your code wants to handle, what's most natural to do in your code, is to use NSColor. That's what you turn as the fill color and take as the fill color.

So how do AppleScript types map to Objective-C classes? This is a pretty important thing you have to know. All of the types that you're using in your scripting are declared in .sdef files when you're using .sdef declared scriptability which is what I'm focusing on today. The standard types, let's see like text and file, things like that, are declared by foundation itself.

You don't have to declare them in your application .sdef. They're completely documented in the Cocoa scripting guide nowadays. If you want to dig around and see foundation's exact declarations of these, they're in the resources directory, the foundation framework in a file called intrinsics.sdef and you can add new types and I'll tell you how to do that in a little bit.

The other and going back to this same declaration and implementation pair, you'll see that the declaration has to specify a Cocoa key. A fill color in this case and that is used to identify these corresponding method names. fillColor and setFillColor. And the questions is, well how did that, how does that work? How does AppleScript property access map to Objective-C messages? It uses a mechanism called key-value coding, which if you've been doing you know, a substantial amount of modern Cocoa programming, you're probably getting pretty familiar with by now. Because it's also used by Cocoa bindings and core data. So you know, we use these same underlying mechanisms over and over again.

You know, the goal being to you know, make you write less code. So and if you read the documentation for key-value coding and read the header file for in the foundation framework, NSKey-valuecoding.h, you'll see there's lots and lots of implementation options. There's direct instance variable access, there's different method naming schemes that it can find, things like that. And of course, all this by the way, just works automatically with Objective-C 2.0 properties. When you declare a property in one of your classes, it just naturally works with key-value coding.

So that was properties. There's also elements. Element classes in .sdef files and here's a simple one. Sketch documents have graphics. So you declare that fact like this. Element type graphic in the document class. And I mentioned these Cocoa XML sub elements before, they're nearly always optional. So in this one, we left it out.

Because Cocoa's .sdef parser can do a pretty good job of guessing what key-value coding key you want to use for that. You know, it finds out the plural name of the type and gives that a try. But that's not what you want. You can override it pretty easily.

The corresponding code looks like this for a simple case in Sketch. There's a method graphics that returns an NSArray and there's an insertion method that takes an array and an IndexSet of you know, where objects are to be inserted in the array and a corresponding re movement that remove graphics at indexes.

So and by the way, this might be a little, I can't remember if this is different or the same as what you saw this morning in Making Your Applications Scriptable. But again there's a bunch of options in key-value coding including you know, single object, insert, inner move methods and multiple object, insert and remove methods like this one. And these are, these are about performance by the way. So you know, being able to insert like a whole ton of objects all at once, makes a, makes a you know, is a big deal sometimes.

So how does AppleScript element access map to Objective-C messages? Like when you ask for the you know, the front document of Sketch or the you know, so and so graphics of a Sketch document? Well it just more key-value coding. Since Panther anyway, key-value coding has had support for to-many relationships as well as attributes in to-many relationships.

So we just use more of that in scripting and again, there's many implementation options, including some complication caused by history here. Before there was this new support in Panther 10.3, for to-many relationships in regular key-value coding, there was this other set of key-value coding methods and foundations, NS script key-value coding header.

So you can still use NS scripting key-value coding methods that are described there and follow the method naming patterns that are described there, but if you want to, just use the newer stuff that's in NS key-value coding. You know, this concept of scripting key- value coding, it might be on it's way out.

There's still, you know, it's still needed in Leopard for some very scripting specific things like value with name and value with unique id, you know some very scripting-centric concepts. But for just regular stuff, use the regular key-value coding. And that will, because that will you know, you're model objects are more likely to work well with Cocoa bindings and core data when you do that.

So that was the quick overview. I didn't go into you know, implementing object specifier methods, that was mentioned this morning. Didn't talk about testing or logging or anything like that. So you know, if you want to catch up, if you're brand new to Cocoa scripting, go watch the video of making your application scriptable when it comes out. Right now though, I want to talk about new stuff in Cocoa scripting in Leopard.

The first new feature is support for dynamic .sdef and what this means is that you can build your application scriptability declaration on the fly. In Tiger, your only option when you're using the .sdef file format is to put it's name in the applications Info.plist and that's it, it's static.

It has to be built right into your application and you know, that's bad if you know, your application supports plug-ins. So but what's good now, if your application supports plug-ins, is that you can build your application scriptability, you know on the fly. So and there's more than one way to do it.

There is to specify it, this is happening. I happened to pick this one for this slide. OSAScriptingDefinition in your Info.plist. Instead of putting the name of a resource, put the word dynamic. And there's another Info.plist entry who's name I cannot remember now, which allows you to make your application's scriptability static on Tiger, where that's all that's supported. But dynamic on Leopard.

So when you declare that your application's scriptability is dynamic, what that means is something like AppleScript. The AppleScript interpreter, when it needs to you know, find out what is scriptable in your application, it may send your application and get .sdef Apple event. And here's one way to handle that.

In application will finish launching, use the NSAppleEventManager API, to set an event handler to something in your application and then you know, any method as long as it has a method signature like this one, that takes an apple event and also reply apple event and then you get to do whatever you want. You can you know, pick pieces out of plug-in bundles and things like that and assemble the .sdef yourself and you put it in an NSData, so that you can put it in an apply Apple event. You do it this way.

So by the way, when you're not using dynamic .sdef, you just have a static .sdef declaration, one thing that's new in Leopard, what's old is new again, is that your application doesn't have to get launched for script editor, for instance, to get the .sdef. AppleScript has been updated a little bit so it will just go grab the .sdef data if it's static, out of your application so that's nice when people open up a script in script editor and it doesn't launch your application automatically right away. So but when the scriptability is dynamic, then it really has to to find out what's going on with the scriptability right then.

Something else that's new is includable .sdef. So the .sdef file format as you briefly saw, is just XML and it's good XML with like a DTD and everything. With you know, the potential to be validated and there is a technology in the XML world called XInclude. It's been around for a while but now we let you use it. The various .sdef parsers in the system can handle it when you include you know, .sdef XML that's in one file, into another.

So and it might save you some programming time in apps that support plug-ins when you're, when you're putting together, you know an .sdef from scratch, it might be easier for one .sdef to point to other .sdefs that are in like plug-in bundles. But in the short term, the more interesting thing about this includable .sdef is, now that we give you something to include for free.

So you don't' have to go digging around in skeleton .sdef or find the right version of Sketch and pull the .sdef out of that. So if you look on your current seeds in system library scripting definitions, there's a file called CocoaStandard.sdef and it includes declarations of standard commands and classes, open, close, save, print, quit, the rest of the standard ones and some standard classes like application document window, that work with Cocoa, that work with you know, classes like NSApplication, NSDocument, NSWindow and are implementations of those standard commands.

And when you're importing or including CocoaStandard.sdef, very often you won't want to include the whole thing. For example, if you have an application that's scriptable but it doesn't print, because that's just not useful, it doesn't make any sense, you can leave our declaration of the print command out.

So and the way you do that is by using this pretty, pretty esoteric or at least very sophisticated and complete and futureful technology called XPointer, that lets you leave out the declarations you don't want, without discarding them all. So and the result of this is less copy and paste as you're making new applications. But there still may be some, because at least for Leopard, one thing that's not in Cocoa standard on .sdef, is declarations of the text suite. You still have to grab that from you know, Sketch or something like that if your application's text is scriptable.

And here's, here's what a sample usage looks like. This is the top of Sketch.sdef, maybe not the one on your seed, but any second now and it's, it specifies, you know, uses the XInclude name space and then actually just at the top of the file, imports from CocoaStandard.sdef everything.

And the way you specify everything, the way .sdef files are shaped, is by saying every suite in every dictionary the .sdef file and then it goes on to Sketches own stuff. So this saves lots of code and not lots of code, but lots of lines of you know, text in the .sdef file that you're editing for your own application.

Something that's been defined in .sdef for awhile but is now supported much better in Cocoa are class-extension elements and they're kind of like Objective-C categories, it's a way to add new properties and elements to scriptable classes. And it's again, useful in app support plug-ins and even in apps that don't have a need for a plug-in architecture, they're good as an organizational tool for you know, the people who have to look at your app scripting dictionary. Keeps them a little more organized.

For example, keep classes like documents showing in the standard suite. The standard suite, you know, the thing that all apps are supposed to have in common, you know, has a document class and if your app has documents, that's where it goes. If it doesn't have documents you just leave it out. But if it does, that's where it goes. But your application's documents you know, presumably do add something unique to the document class. And what you do is you put those in a class extension, so that way they show up in you know, your application's own scripting suite.

So here's a simple example from Sketch. Just class-extension extends= "document". This is all there is you know, in Sketch's .sdef for the document class. One important thing to keep in mind or one important use for this, even when you .sdef, so when your application imports CocoaStandard.sdef, CocoaStandard.sdef has a declaration of the document class. It specifies as the implementation class, the Cocoa class, NSDocument.

That's very often not good enough. When the user says you know, make new document, they want one of you know, your app's documents, not some generic object that's not even really instantiable. So one thing we just added to the class extension XML element is the ability in a Cocoa XML sub element, to specify the implementation class.

So yes, if more than one class extension tries to specify the implementation class, I don't really know what will happen. One of them will win. But in the meantime, this is a, you know, this is a good way to just you know, get the reuse of what's in CocoaStandard.sdef while still you know, being able to easily provide the information you have to provide.

Synonym elements and by the way, these next two slides, this is, these are the one bit of bunk in today's presentation where it's not in today's seed at all. Even a little bit. But they will be in there before we ship. So synonym elements are important. This is all about compatibility. You're not supposed to, you're supposed to try not to break existing scripts. As you add features to your application's scriptability, things like that, you want you know, existing scripts to keep running. John Kamiski (sp?) emphasized that a lot this morning in Making Your Applications Scriptable.

You know, when people build work flows with your application, you know, they've got more invested in your application. They're kind of locked in and you know, unless you ruin that by breaking their scripts, their work flows, that's a good business. So compatibility which has always been pretty important in you know, Apple scriptability, is now getting one step more important with scripting bridge.

Because it raises the compatibility bar in that now other applications are depending on your application's scriptability to stand still. They're writing Objective-C code that mentions you know, your application's pieces and parts that you've exposed through scriptability by name and if you change that in your app, you're effectively breaking other apps. So this is pretty important.

So you don't have the option of not changing your application's scriptability. You know, you want to keep it clean. For starters, you want to rename things, maybe to match your UI, maybe just to be cleaner and also you want to do things like fixing four-character code mistakes. I'm not going to go too much into four-character codes, it's something you have to deal with when you're making your applications scriptable because that's what NSAppleEvent or that's what AppleEvents use to identify different parts. So four-character codes are you know, look easy you know, use any four characters you want except for those four. So it's easy to make mistakes here, but you know, you want to fix them and this helps you fix them.

By synonym elements, what they do is when you know, when you have an element of your application's scriptability, a property or a command argument or something like that, you get to use a synonym, you get to change the name or the four-character code, but then use a synonym to specify the old name in four-character code. So scripts will still compile and complied scripts will still run.

So here's a simple example, something worth thinking of doing in CocoaStandard.sdef is, renaming one of the parameters of the Make command. You know, right now it's with data and you know, that's kind of nerdy and you know, scriptors are people too, they're actual users not programmers. So we're thinking of renaming it to "with contents", but of course lots of lots of scripts in the world you know, refer to this parameter, the Make command, as with data. So what we're probably going to do is just rename the parameter but then add a synonym "with data", so that existing scripts keep running.

So another compatibility issue that we let you handle a little easier now, well the same compatibility issue, different kind of fix, we now support in Cocoa, the "hidden" attribute and this is really in the seed you have. Some reasons why you might want to like hide you know, pieces of your application's scriptability from people who are coming to your application new, is that you know, it's just old cruft that you're leaving in there for compatibility. Here's a bit of you know, Cocoa history from scripting is, we've always had a path property on documents and its value is just typed, you know, its typed text.

It was a POSIX path string but that's really not what people in the AppleScript community are used to working with. So now for example, in CocoaStandard.sdef, the document class has a, has a file property and it's type is file. Just a regular AppleScript thing. But of course, we don't want to break existing scripts, so one thing you can do is leave the old path property in there and just mark it hidden. So you know, people don't have to look at it anymore in the dictionary viewer but current scripts keep running.

So those were, those were the kind of improvements that don't really have much to do. There's no corresponding coding work to do to take advantage of those features. This next section is going to be a bunch of things where what you declare in the .sdef file does effect what kind of code you write. The first thing is support for custom value types. You could declare value, value types in .sdef, in Tiger, but there are a bunch of limitations.

You can only do ones that AppleScript understood anyway. You know, ones that it found in some other dictionary or built into the language. And you could even implement value types in Tiger, like this one you know, the one I was talking about before, value type RGBColor and Cocoa class NSColor.

What's different now in Leopard is that we tell you how to implement custom value types and now they'll work even when they're genuinely custom. So and here's a pretty simple example. Well the declaration you just saw. For the RGBColor type, it's not actually something that's built right into AppleScript. So Sketches .sdef declares this.

When you declare a new value type though, there's, there's something that has to go on when AppleEventDescriptors of that type come into your application, they have to get converted into Objective-C objects for passing around to the implementations of custom commands or being passed into setter methods for properties and things like this.

So when you add a custom value type, here's how you make that work. You specified a class in the Cocoa class XML sub element of the value type, so in that class implement a class method who's name follows a pattern like this. ScriptingRGB color with the scriptor. RGB color is the name of the type with some extra capitalization and the space is removed.

So you just implement a method name that looks like that and inside there you, you know, you'll use NSAppleEventEescriptor to pick apart what's in there and return you know, an object of the right type. If you look in the Sketch sample code today, it has these methods. So you can see how it gets from a you know, an AppleScript style RGBColor into an NSColor.

Going in the other direction, when one of your accessors returns an NSColor, how does that get turned into an AppleEventDescriptor and it's with a method that looks like this. Scripting RGBColor descriptor. So this is very much inspired by key-value coding where, you know, we take the names that you put in you know the .sdef and we make methods out of them. And if you get it wrong and you know, these methods don't exist or their methods there that have the wrong name, it throws an exception. It says you know, the method's not implemented.

So missing values. Something that's actually pretty important in AppleScript is support for missing values which is distinct from no values by the way. For example, here's the declaration of fill color for Sketch graphics and it uses something that's been in .sdef by the way, called complex types. A complex type is when the type of a value can be, can be you know, one, you know this type, that type or another type. Alternative types. So we use that in this example, for the fill color property. A fill color can be RGB color or it can be missing value. So in Sketch, not all you know, graphics have to be filled.

And a little background for you, you know is SKT graphic, you will see properties declared in the header. Things like fill color and stroke color and is drawing fill, is drawing stroke. Those are there for use by Cocoa bindings. You know, there's a graphics inspector in Sketch where, you know, there's a color well and there's a check box right next to it.

So you know, in AppleScript you could do the same thing. You could say people have to turn on filling and then set the fill color. But you know, that's not convenient enough for, for people writing scripts. So instead we, you know, let them specify either a color or that there is no color all on the same property and here's what the setter would end up looking like for that. setScriptingFillColor, a fill color is passed in, it might be nil. So if it's not nil, you know, there's code that you know, turns on the filling and then set the fill color. But if it is nil, just turn off filling of that graphic.

So there's a couple different ways to do this by the way. For example, in you know Sketch graphic, you can also have you know, transparent colors and things like that or you could have an enumerator that means no color and things like that. I'm just using missing value in Sketch because I have to demonstrate this concept you know, somewhere simple so.

Just using fill color for that. Missing values are also good for to-one relationships. So if you have you know, a class that's modeled that way, where you know, different scriptable objects point to other scriptable objects or maybe they don't, it's you know, missing value is a good way to indicate that this object might point to nothing in this example.

An implementation note is that you never have to fool around with NSNull. I know some developers have discovered that if they write a getter method that returns an NSNull object, Cocoa scripting for the last several releases, will convert that to a missing value that shows up in the you know, the results of running the AppleScript and that continues to work.

But you know, we never published that because it wasn't, it wasn't that great. So you know, NSNulls are just for putting an NSArrays in dictionaries. You know, they're placeholder values. For returning from getters or passing into setters, they don't really belong. Because all of a sudden, all sorts of code has to specify, you know, check for them.

So if you're doing this that's fine, but going forward in .sdef using this missing value support, you never have to write getters that return NSNull anymore and the reason you don't want to by the way, what's particularly bad about that is it might confuse your other key-value coding clients. Like Cocoa bindings. A lot of them will be surprised. So by avoiding this whole NSNull issue, you know, we keep your getter methods reusable by different technologies in Cocoa.

Something else that's new is custom enumerator values. .sdef has support for a concept called enumerations, which is a type that defines that the value is you know, one of a couple different values. So and in Tiger when you did that, what was passed to your setter methods are found in the arguments dictionary, one of your commands, was an NSNumber who's value was a four-character code made into an integer.

So that's wasn't very convenient. So what we've done now is, in the Cocoa XML sub element of enumerator XML elements in .sdef files, you can now specify an integer-value attribute or a boolean-value attribute or a string-value attribute and this is what your code will see. So and this feature by the way, is strictly for making programming a little easier and you know, Cocoa parameters in general get you know, the old school ones anyway from, from next land, get a little grumpy when they see four-character codes so.

Now there's really no more reason to see them at all in, in you know, your source code. And this turned out to be surprisingly useful in a couple situations. The really neat one was in the as parameter of the save command. The save command has an as parameter, it's optional. It's where a person writing a script gets to specify what file format should be used when saving a document. So Sketch for example, can save documents in PDF format, TIFF format or Sketch's own format.

And so we want to let scriptors write things that are pretty natural, save the document in the file as TIFF and I guess I'm claiming that this crazy four-character acronym is pretty natural because it's been around for a long time. Everybody knows what it means and nobody actually wants to write out tagged image file format.

But that string, that's not the kind of thing that gets passed around in code. So one thing that's new in Leopard is the save command, the default implementation of it, in NSDocument, now does a good job of handling this, this as parameter. So but what it has to do to do that is to pass to the code that actually saves the file, a file type name.

That's the way it works in NSDocument. So and for example of a file type name, something like public.tiff, that's a uniform type identifier, which is the new way to do that. We introduced this in Leopard and talked about it a little bit at WWDC last year, the new uniform type identifier system.

So how do we get from TIFF to public.tiff for passing around in NSDocument messages? Well, Sketch does it this way. It declares a type savable file format, it's an enumeration and then enumerators are Sketch, TIFF and PDF. That's what scriptors see, but the values, the string values, NS strings that Cocoa programmers see or Cocoa code sees, is a com.apple.sketch2, com.adobe.pdf or public.tiff. Like that.

So and where does this type get used? Here's the example and the as parameter of the save command that you just get to use the enumeration name as a type "saveable file format". So it ended up, it doesn't you know, it doesn't make any difference to people writing scripts but it made the code a lot more convenient. So it'll, it'll probably make a few things that you do in your own code more convenient too. So error sensing and reporting.

A quick quiz. When there are no documents open in Sketch, what will this script return? First graphic of front document. It's a rhetorical question. You don't have to yell anything out. It returns bleh. It returns, Sketch got an error, NSReceiverEvaluationScriptError and just to add insult to injury, 4.

( laughter )

So you know, if you seen this you are writing your you know, your scriptable application or you're scripting another application, you know, you are right to be horrified. I mean, the reaction is ah that's great.

Thanks for the help. Oh god. So now in Leopard when you do that, ah, just the regular Apple event error. Can't get document. Invalid index. So all over Cocoa scripting and pretty much hundreds of places now, where we were returning these strange little error codes that were defined by NSScript command like, cannot create command and receiver evaluation error, we just used the regular Apple event error codes that have been defined for many, many years. Like 12 or 15 or whatever.

( applause )

So it's, it's, it's, it's definitely going to make things a little bit better. And actually you know, I think, I think we've been making Cocoa, we've been making AppleScript look bad in this regard. People go AppleScript is you know, much easier to read than to write and I think you know, a good portion of that is you know, AppleScript itself has very little error checking and in particular, even less type checking you know, saying things like set the fill color of a graphic to front window you know ah, it's pretty happy to you know, bundle that up into an AppleEvent and send it to the application.

So you know, the trouble that people have been having learning AppleScript is because we've just been punishing you and you experiment and get it wrong and you know, we're not doing that anymore. So Cocoa now has much better error checking and in particular, type checking. .sdef, the you know, it's much cleaner file format, more rational where you know, everything is pretty clearly defined, allows us to do better type checking in a lot of cases.

So and you know, one more upshot in addition to what scriptors see, your code won't be invoked strangely. You know, the error checking that you know, the very most conscientious among you who've been you know, testing your stuff and then and then you know finding your methods being passed in these crazy objects because you know, you put a crazy object in it and you know in the script, you know, you don't have to worry about that anymore.

You don't have to do things like invoking is kind of of class and setter methods to be perfectly safe. The error checking moves from the code to where it belongs and the .sdef, in the declaration of what your code can do and now Cocoa scripting will just you know, catch all this.

And there's a reusability implication here again. Your KVC compliance methods, your getters and setters and insertion and removal methods and things like that become a little more reusable because they're not doing freaky things that you know, like peeking in the current NSScript command and stuff like that. Things that you know, you like to keep out of the way of you know, their use by Cocoa bindings or something like that.

So a new API. Boy you've been making a lot of requests for a new API and we have been listening to all of them. So and we have had some ideas of our own, too. So the first one is customizing object specifier evaluation. With this new method, scripting value for specifier, it's declared in NSObjectScripting.h. It's sent to the container of specified objects.

So when somebody writes a script that asks a Sketch document for every graphic who's fill color you know, is so and so, it results in you know, the object specifier being passed to this method that's sent to the document. The container of the things being dug around in.

So and you get to override it to do pretty much whatever you want. If you don't know what you want, invoke super. If you you know, NSScriptObjectSpecifier has a few different subclasses of it. If you recognize a subclass and you know, there's something you can do with it, then go ahead and do it. Otherwise just invoke super and the default implementation does what Cocoa scripting was going to do anyway.

So this name, scriptingValueForSpecifier, value here it's meaning is the key-value coding sense of the word and then some. And what that means is you know so what, this is very abstract looking, what am I supposed to return. Well, it depends on what's in the object specifier. If it's say, if it's a range specifier, you turn an array of objects. If it's an index specifier, you turn one object.

If it's a whose specifier, you've gotta check and see what kind of whose specifier it is. So the rules aren't that simple but they are very predictable so you know, if you get it right once, you'll keep getting it right so. Eventually I think these will probably be all written down somewhere. You know, what kind of object specifier gets you know results in what being returned.

So and I keep talking about whose specifier is very important and powerful piece of AppleScript. You know, one of the main reasons that people've been asking for a method like this is for optimization. So they can do fast things with whose specifiers. Like convert them to predicates and pass that to core data. So all the API you need to do that now is public.

So one little bit of information in the interest of full disclosure is, this is not invoked all the time. There are still places where Cocoa's implementation of some standard commands like delete and move don't actually want the objects, they want the indexes at which the object exists, so they can you know, delete or move them. So if you're doing a really serious customization and you're overriding the existing indexes of objects by evaluating specifier method, you'll probably want to keep you know, just leave that override in.

But in a bunch of other cases, this will be the thing to override. The next thing is customizing object creation. This new method, new scripting object of class forValueForKey with contents value properties is sent by NSCreateCommand to the future container of an object being created. And after this method is invoked and returns a value, that value is inserted into the container's relationship, identified by that key using regular key-value coding.

So and we predict this will be good for overriding and NSManagedObject subclasses. Because Cocoa scripting's default behavior is to take the name of a class or take the class, the implementation class, send it in alec method, send it a simple init method and then you know set contents and properties in it, stuff like that and that is not the right thing to do in a core data app. In a core data app you're supposed to use NSManagedObject init with entity description managed object context. So now overriding this is your opportunity to do that.

And again, if you don't know what to do, you're customization is not applicable, just invoke super and when I say not applicable by the way, as a general rule when you're overriding key-value code or key-value coding style methods like this that take a key, if you don't recognize the key, if it's not in a set of keys that you're customizing, just invoke super and let the default implementation take care of it. In this NSManagedObject example for instance, not all managed object properties are modeled.

They might be added you know, by a property or something like that or a category rather. Sorry. So then arguments that are passed to this, well the first one is the object class. And you can ignore it if you don't need it, if you can infer the class of object that needs instantiation just from the you know, the key that identifies relationship.

But in other cases it is important. For example, in something simple like make new circle at end of graphics in Sketch, the to-many relationship key is graphics, but the class is SKT circle there. So very often that object class parameter is useful. The contents value and properties parameters correspond pretty exactly to the make commands with contents or with the data still actually parameter and the with properties parameters.

So and contents value might be nil because that's an optional argument to make and the properties dictionary, you don't have to worry about that being nil. You know, in Cocoa when we're following our own rules, we don't pass around empty or nil instead of collections, we pass empty collections as in this case so. Another new method along the same lines, you can also customize object duplication. So copy scripting value for key with properties and this message might be sent by our default implementation of the clone command to the future container of objects being copied.

And it's the same sort of thing right. It's sent to the container and once it returns it, it's inserted using regular key-value coding. So and this might eventually be used by the set command also by the way. We've noticed a few cases where objects should really be being copied more than they are. So that might, this might start being used there too.

So and naming-wise again, value in the key-value coding sense and what that means you know again, is that the correct type of the value is inferable from the key. If the key identifies a to-many relationship, you'll be passed an array of objects to be copied, might just be an array of one object, but the important thing is that you know, in methods like this that are you know, meant to be very flexible and particularly reusable, you know there has to be some convention that's easy to get. So in this case you know, if the key identifies a to-many relationship you'll be passed an array. If the key identifies an attribute or a to-one relationship, you know, an AppleScript property, then what'll be passed in is a single object.

So those are the big ones. The rest of this is just a grab bag of things we've, we've you know, you've asked for and you know, we've tried to respond to. First one is very simple. NSScriptClassDescription now has an implementationClassName method. So just sending class name to a ClassDescription in an app with .sdef declared scriptability, returns the human readable name of the class because that's how they're keyed in .sdef files.

So one thing you can use this for is if you're doing your own customization of NSCreateCommand and you send, you send the create class description message to find out what should be created, this is for like really you know, really heave customization otherwise you use one of those methods I just showed you. You know, this is how you find out the name of the Objective-C class to be allocated.

More new things on NSScriptClassDescription, hasPropertyForKey, hasOrderedToManyRelationshipsForKey, hasReadablePropertyForKey and hasWritablePropertyForKey. If you customize heavily enough, you'll need some of these. For example, Cocoa's own default implementation of scripting value for specifier uses these things. Like hasPropertyForKey and hasReadablePropertyForKey and if you override it, you might have to invoke them also.

And by the way, one reason we're getting really good at error checking now, so who would ever have to ask hasPropertyForKey like, shouldn't you have caught that already? Well there's a specific case, it's a popular case though that we always let them go through. For example, when you, Sketch has a graphic class, subclasses like text area and circle, you can ask a text area for it's contents. So but you don't always have to refer to text areas as text areas, you can call them graphics.

So if you ask for the text contents of graphic one, something has to make sure that graphic one is actually a text area and not a circle for instance, which wouldn't have text contents and that's what you know, a method like this has property for key is for. For heavy customization of objects placement, we've added a couple of accessories to NSPositionalSpecifier, like position in objectSpecifier.

These just match what's you know, passed into the initializer when we create those from an AppleEventDescriptor. So typically if you're doing something with NSPositionalSpecifier, you ask it to evaluate and then you get an insertion container, an insertion key that identifies the to-many relationship and an insertion index that identifies where in that too many relationship a new object should be put.

So that should be good, you know good enough 99% of the time. But there's not reason to keep these methods secret anyway. So now they're public. Taking control of object specifier life cycle. First thing is letting you create them. NSScriptObjectSpecifier now has a new class method called ObjectSpecifierWithDescriptor.

So if you have, if you have an NSScript command, a current script command or one that's being executed and you've asked it for it's Apple event and you're picking it around in there to get AppleEventDescriptors for specific parameters and things like that and you want to get an object specifier but you still want to use Cocoa's object specifier evaluation, here's how you get from an AppleEventDescriptor to an NSScriptObjectSpecifier.

The other half is given an object specifier, give me the AppleEventEescriptor that corresponds to it and the main reason you might want to do this is because you're taking fine-grained control over error reporting. You're invoking this method and passing it to the first method in this list. setScriptErrorOffendingObjectDescriptor so and there's also a getter for that and there's setScriptErrorExpectedTypeDescriptor and a getter for that.

And what these are, if you know a bunch about apple events, you know what flies back and forth between something like script editor that's running the AppleScript interpreter and your application that's receiving you know, commands to do scriptable things, in the ReplyAppleEvent, in addition to setting an error number, there's a couple other things you can set.

Like an offending object descriptor, an expected type descriptor and these are things that AppleScript or the open scripting architecture actually, these are things that it uses to make good error messages. So these, these things end up getting converted into text and inserted into the templates that correspond to those standard error numbers.

So we of course, are using these all over the place now in Cocoa itself and if you're doing you know, heavy customization and you know, you want to continue the new tradition of good error reporting, you know Cocoa won't get in your way anymore. And to finish up, just some debugging tips.

Keep your debugger console window open. Cocoa's own .sdef parser which gets run when your application, for example receives an apple event that requires the Cocoa scripting machinery to handle, you know when it parses that .sdef file lazily, it's pretty good about logging you know, errors and also warnings. Like this type doesn't make any sense. You know, your XML file's syntactically correct but it's still not going to work. So that kind of stuff shows up in the console.

And also information about exceptions. When you're depending on key-value coding, you know, it's easy to mistype, spell things wrong, stuff like that. But you know, we don't crash. Key-value coding you know, doesn't crash much, it throws exceptions. And they're supposed to be really informative to help you debug.

So, so you want to make sure that you actually see those exceptions. An easy way to do that is to leave you know, that window open in Xcode while you're debugging. Another reason to leave the console window open is to take advantage of Cocoa's pretty heavy logging. About every scriptable command as it's executed. So to turn this on, try something like this. Default's right and this example is for Sketch and there's a typo there, it should not have .plist at the end of it. com.apple.CocoaExamples.Sketch NSScriptingDebugLogLevel 1.

Just turns on this feature in Sketch. Personally, because I work on Cocoa scripting and I want to see everything that's going on in scripting on my machine. I discover a lot of bugs in other people's apps this way. Defaults write -g in a ScriptingDebugLogLevel. -g means you know, the global domain for our user default system. So it just turns it on at every end.

And in Cocoa, there's this, there's it's a little stronger than a convention but, there's a method that lots of objects can implement called description and in Cocoa scripting you know, NSScriptClassDescription, NSScriptCommandDescription, NSScriptSuiteRegistry, all these things implement these description methods and what that means is that and what these description methods return is an NSString that's meant to be informative to people.

So what this means is that you can break anywhere while debugging for example and just type this command in the debugger console, po [NSClassFromString (@NSScriptSuiteRegistry) sharedScriptSuiteRegistry]. You know, just invoke this public class method and that will send a description message to the result and just a whole ton of stuff will appear in your console and you can see everything that Cocoa thinks it found in your .sdef file. It's pretty handy.

So that's it. There are many, many improvements to Cocoa scripting in Leopard and what I want you to come away with is that switch to .sdef. If you haven't done that yet, it's, it's, it's far easier to work with. It enables a little bit better error you know, type checking in your application.

There's better error checking all over Cocoa scripting, but it's you know, it's better still in applications that declare their scriptability with this .sdef file format. There's a bunch of new customization opportunities for you, a new API and in Cocoa itself, the default behavior, a lot of things has gotten better. Especially in the realm of error reporting.

So for more information send an email to Deric Horn, [email protected] or to Matt Drance, [email protected]. As far as documentation goes, you should always start out, if you're new to scripting with reading Technical Note TN2106 and that is! what you have to type into developer.apple.com search field, if you want to get anywhere. TN2106. That's the Scripting Interface Guidelines. They tell you, you know, what to do, to do it right.

The Cocoa Scripting Guide which was updated about a pretty comprehensively about a year ago still only, is where to go for all this stuff about Cocoa scripting. Foundation Release Notes is where we leave the newest information. The ones for Leopard describe many, many things, including you know, a lot of bug fixes and stuff like that that I haven't talked about here today.

And have a look at the Sketch sample code. You know, that's what we keep working on. That's like, it's not just sample code, it's also what we test with as we add these new features. We make Sketch take advantage of it. That's how we know it worked so.