Cocoa • 53:05
Cocoa includes built-in AppleScript support designed to make it easy to create scriptable applications. Learn about the powerful Cocoa scripting architecture and upcoming Cocoa enhancements, as well as specific techniques that make it easier than ever for developers to deliver applications with complete, robust scripting support. This session is aimed at both beginning and experienced Cocoa developers. Familiarity with AppleScript is recommended.
Speaker: Mark Piccirelli
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning. Welcome to session 303, Cocoa Scripting. What I'm going to talk about today, I'm going to start with the basics of Cocoa Scripting, for those of you who haven't worked with it yet. The scripting commands and classes that are built into Cocoa, so you don't have to do anything to get that support for those. How you would declare new scripting classes, and how to implement scripting classes.
I'll talk about, go into specifics about the types you use in declaring scripting suites. will talk about the new features that are appearing in the Jaguar seed, and I'll share with you some of our future plans for Cocoa scripting. So the basics, starting from the ground level. AppleScript presents scriptability as commands and classes that are grouped into suites. These are what appear in the scripting dictionary window of an application like Script Editor. And what we care about here today are the commands that applications handle and the classes that applications expose as scriptable.
Cocoa scripting does a bunch of things for you. It gives you a way to declare commands and classes. I'll show you how to do that. It handles incoming Apple events, converts them to command objects, and executes those command objects for you. And it declares and implements most of the standard suite and tech suite commands and classes ahead of time. You don't even have to do anything for those.
So what you have to do to take advantage of Cocoa scripting is, well, for starters, very simply, you have to include an NSAppleScript enable entry in your property list whose value is not surprisingly yes. And you have to include some files in file format so we define in the resources directory of your bundle to declare the scripting support. You have to add methods to your application to handle any custom classes you define for your scripting. And you have to add methods, accessor methods, on the classes that you've declared to be scriptable.
So the files in the apps bundle that I'm talking about. There are two file formats that you support, that we support, and to implement scriptability you have to provide both of these. There's a script suite file and a script terminology file. These are actually property lists. They're parallel property lists, and by that I mean the dictionaries in them have roughly the same top-level entries in both cases.
The script suite file is for declaring the scripting model of the scripting and how it maps to the Objective-C, or Java actually, classes and methods that are in your application. The script terminology provides all the human readable strings that will appear in scripting dictionaries that scripters see. And if you're wondering, if you already know the history of AppleScript, you know there's an AETE format that has to be supported, and we do that automatically. We, uh... When a scripting... When a scripting event first comes into your application, we parse the relevant script suite and script terminology files and merge them and return the AETE data.
Both ScriptSuite and ScriptTerminology files, at their top level, they're pretty much dictionaries. They both contain commands and classes sub-dictionaries. A ScriptSuite file also contains an Apple event code. In the open scripting architecture, each suite has to be identified by a unique four-character code. I'll have more news about that later.
There's also a name. The one in the ScriptSuite is not human-readable. It's a programmatic name that's used to identify the ScriptSuite. It appears in a few other places. The ScriptTerminology file, at its top level, also has a description and a name entry for the suite, the human-readable ones. There's other stuff I'll go into later to support custom value types.
So, how do you declare commands? Well, you have to declare them in two places. First of all, you have to declare them in the script suite file. You have to provide two four-character codes for each command, the AppleEvent class code and the AppleEvent code. And some of the documentation I'll point you to at the end of the talk will give you some advice on how to make these things up.
You have to declare for each command arguments, and for each argument another AppleEvent code, and a type. And I'll talk further about what types are available for you to use. And if the command returns a result, a result AppleEvent code and another type string. And a unique entry in that command dictionary is command class.
and Command Class is typically the name of an Objective-C subclass of NSScript command or NSScript command itself. This is important because scripted operations, after they've been sent to a scriptable application via Apple event, are converted to instances of this command object, whichever one you've specified. It's very often useful to subclass NSScript command, but also very often it's not necessary. NSScript command, that class itself has everything included with it to encapsulate the arguments that were part of the command and things like that.
You also have to declare commands in the script terminology file. This is where you present all the user-visible, human-readable strings, both for the command, you provide a description and a name, and for each of the arguments, you provide a description and a name. So, for example, I'm not going to, I'm going to spend more time on classes than commands. But if you want to see a quick example, look in the foundation frameworks bundle.
There's two files there, nscoresuite.scriptsuite and nscoresuite.scriptterminology. You'll see all of our declared standard commands. An easy example is the nsclose command, which encapsulates everything associated with an AppleScript close command. As an example of something in Cocoa that knows how to handle that, our nsdocument class knows how to handle that command.
is the founder of the AppleScript standard suite. The AppleScript standard suite has for years now declared a reasonably sized batch of standard commands, open, print, quit, close, save, count, delete, etc. We provide support for pretty much all of those, not quite, but almost all of them. Very often our support takes the form of a subclass Venice script command that encapsulates instances of those commands.
and something that's actually not part of the AppleScript standard suite, the get and set commands are considered implicit. Everybody has to support them, so nobody bothers, nobody has to declare them in their scripting support, but of course we still have to handle them, and we do that with the ns get command and ns set command classes.
I'm going to be talking a lot more about our standard command classes because your scriptable classes, when they're being queried for properties or elements or something like that, have to know how they'll be queried. They'll be queried using key value coding. It's also what's used to set the values of properties and elements.
A quick pointer, you shouldn't have to declare too many new commands yourself. Text data, for instance, does not have to declare any new commands. There are plenty of good reasons to declare new commands, but it's very often not necessary. The documentation I'll point you to at the end of the presentation describes some of the thinking behind that.
So just as classes or as commands are declared in ScriptSuite and ScriptTerminology files, so are classes. And a quick rundown through the entries that you use when declaring them in the ScriptSuite file. The superclass, which is a string. An Apple event code. You have to provide that still. Attributes. We call them attributes in our ScriptSuite file format, but they correspond to AppleScript properties. To many relationships, which correspond to elements.
And a few other things. Apple event code. Read only and type for each attribute and to many relationship of the class. Any supported commands if the class handles commands that are sent to it itself. And at the bottom of the slide, there's two on relationships. If you're digging around in our ScriptSuite and ScriptTerminology files, you'll see that in a few places. You probably won't have to use it yourself, though, so I won't go into it too much today. But you should be able to do everything you need to do just about with attributes and too many relationships.
When you declare a class in the script suite, you have to pick a name for it. That name is the key in the class's dictionary for that entry. That class name should just about always be an Objective-C or Java class name, for example, NSWindow. As part of the class declaration, you always have to provide a superclass also.
You can use the name of another class in that same script suite, or you can use the name of a class in another script suite, for example, our NSCore suite, which is part of Foundation. When you do that, you have to specify that using the little dot in the middle like you see there.
Just as with commands, classes, you have to provide information in the script terminology file. So the human readable description and name and plural name, which is usually appropriate in scripting dictionaries, and for each attribute, a description and name. So, for example, to show you some of these files, I've decided to add something that should probably have already been in TextEdit, scriptability of the page setup of a document, what appears in the page setup dialog and those parameters. So, on the Demo 1 machine, let's quickly just look at some of those declarations.
So this is a text edit scripting dictionary. It's pretty much unchanged from what's on the Jaguar seed CD that you have there, except I've added this to document, a page setup attribute, which is of class page setup object. Very often you want to use a different name there to avoid some ugly naming conflicts that cause AppleScript to be a little bit harder to write for people.
So I decided to call the class page setup object. It's something that other people do, too. The declaration of that class is down here. and it has the things you would expect to be associated with page setup, the paper width and the name and the margins and the orientation and the scaling.
That's how it appears to the scripter. Here's what you would have to write to do that. and I've highlighted the new things in color. There's a couple different ways to edit a property list. You can use Property List Editor or another program that'll point you to at the end of the presentation.
Or you could just edit them as ASCII. I usually don't edit them as ASCII, but for demonstration purposes, it's a little bit easier to show it to you like this. And I actually made an RTF file out of the ScriptSuite file so I could add some color to it.
So this is the declaration of that page setup attribute in the document class that we just saw in the scripting dictionary. I picked an Apple event code for it, and I said it was read-only. You can change the things within a document's page setup, but you can't take a page setup and slap it onto another document the way I have it set up here.
Page Setup class is declared as nscourseSuite.abstractObject. AbstractObject is a class declared in foundations nscourseSuite. When you're not subclassing from something else, that's the superclass you should use. I've declared a bunch of attributes on the page setup class, providing a four-character code for each one and whether or not it's read-only and a type.
I'll discuss these types in more detail later on, but for now, they should look pretty familiar for you. NSString is the Objective-C class name. NSNumberFloat. An NSNumber will be moving around in the code that I'll show you, but we let you specify a more specific subtype of it. Something more informative can show in the scripting dictionary. Another type that I'll discuss is enumerations.
and enumeration for the orientation of the page setup and its possible values are landscape and portrait. So the things in the script terminology file that correspond to those declarations in the script suite file are here. For that page setup attribute that we've added to the document class, you have to provide a description and a name, and these strings right here are what eventually appear here. Page Setup Class Description and Name Description and Name for each of the attributes For that enumeration that we'll discuss later, same thing, description and name. Go back to the slides.
Before we get much more into classes and the implementation of classes, like that one that I just showed you the declaration for right there, it's useful for you to know what classes are already built into Cocoa. The standard suite declares classes like Application, Document, and Window, and we provide scriptable support for all of those using, not surprisingly, NSApplication, NSDocument, and NSWindow.
All of the text suite stuff is declared and is implemented by our NS Text Storage class. A little word of advice for when you're working with this stuff yourself. Subclassing just to add scripting properties and elements is very often unnecessary with Objective-C. You can use categories instead. You might have good reasons to create a subclass of NS Window, for instance, but if you're just adding scripting properties and you're adding accessor methods for those, you really don't need to subclass it just for that. You can just add category methods.
One more thing before I show you how to implement these scriptable classes. A few of the details about what happens during command execution. An NSScript command encapsulates pretty much everything from the Apple event that came into our application. The direct parameter from the Apple event, in most cases, becomes what we call an object specifier for the receivers of the command.
The other parameters in the event become arguments in our NSScript command object. Some of those might also be object specifiers, some of which might just be plain value objects like NSStrings and NSNumbers. When I say object specifier, I mean an instance of NSScript object specifier or one of its subclasses.
So, for example, the AppleScript reference, word for of text of front document. It actually becomes a chain of object specifiers, NSIndexSpecifier that encapsulates the concept of word for which points to a parent contain and its property specifier which encapsulates the concept of the text property of something and then its parent container specifier is NSIndexSpecifier that encapsulates document one.
And anything that doesn't have a parent container specifier is assumed that that's a top level object and that the application is the container of that. and we have full support for all the reference forms you can read about in the AppleScript language guide. Index references become index specifiers, property references become NS property specifiers, and so on.
Object specifiers are evaluated during command execution. And what I mean by that is that the exact set of specified objects is determined, usually ends up in an NSArray in most cases. And those are the actual objects, in the case of the receiver specifier for a command, that are going to receive the command. So those are actual Objective-C objects.
During that evaluation of object specifiers, containers are asked for attribute values and relationship objects. In that example I just showed you, during the evaluation of that chain of object specifiers, the application would be asked for the first document, and it would be asked for its text object, and so on.
So, in general, during command execution, that evaluation of the receiver's object specifier is the first step. If there are any argument object specifiers, they're also evaluated. If a command has been, or I'm sorry, if a scripting class has been declared to support commands of a specific type, usually using a supported commands declaration in the script suite file, a message will be sent to each of those receivers that which message is part of the declaration for the supported commands in the script suite file. More likely, a script command can override NS script command perform default implementation, and it'll take care of managing the execution of the command itself.
This is the typical case for most of the commands that are built right into Foundation, for get and set and make and move and things like that. Your objects aren't sent to command and then asked to just deal with it. We do all the work of picking it apart and calling accessor methods in your objects and things like that. The way we call these accessor methods is we use key value coding. Your scriptable classes have to conform to key value coding.
What is key value coding? It's the system that lets us invoke methods in your class without knowing their names ahead of time. This is how we can convert attribute or relationship names into method names. What this means for you is that you get to write your scripting support in a very natural fashion, writing methods that don't have to switch on Apple event codes or something like that. You get to just write methods and we'll find them and call them at the right time. and there's a couple places in scripting that uses key value coding.
When objects are Thank you. So, and there's a couple places where we use key value coding. During the evaluation of object specifiers, when objects are being found, it's used to query the container objects. And during the execution of get and set and things like that, that's what we use to poke at your scriptable objects. So, and it's a great demonstration of the dynamic binding and the runtime introspection features of Objective-C.
So I've been talking about accessor methods. What do they look like? Well, first of all, what we send your object is a message that looks like this, value for key attribute name, where the attribute name is the string that was used to declare that attribute on that class in the script suite file. Now, you could override this method. This is a method on NSObject, but that's not as convenient as it could be. What you can do instead is provide a method that's simply just named attribute name.
And key value coding will find that and invoke that, and you return, very simply, the object that is the value for that attribute. And what type should it be? It should correspond to the type that was used to declare that attribute in the script suite file. A similar thing happens during, for instance, set commands.
Just as key value coding has a value for key method, it also has a take value for key method. And the method that you implement that corresponds to that is called setAttributeName, where attribute name there should be replaced by the actual name that was used in the script suite file.
So, one thing, AppleScript doesn't do too much type checking in a lot of places, and right now neither do we. So, you should use isKindOfClass or something like that in Objective-C to make sure that that object you're being passed is the right type and react accordingly. So, example. To go back to demo one, let's look at the code to implement that stuff that we were talking about before.
So here's the TextEdit project. I've added a single method to document and a new class called Page Setup. So this is the accessor for that page setup attribute on document. It corresponds... is a great example of this. This attribute name corresponds to this method name. And this will be called automatically at all the right times. What TextEdit's Document class is doing in this case is it's creating a new object, a new temporary object, of the PageSetup class, giving it a pointer to itself, and then auto-releasing it. Objective-C's, or our reference counting system, makes this kind of thing pretty convenient.
You may be wondering why we have a separate PageSetup class instead of just giving direct access to the print info or something like that. There's a little bit of a complication in this example. TextEdit needs to know when the print info has been changed. And print infos don't notify anybody when they've been changed. So we've added this one level of indirection, which isn't a great complication, but you do have to know about it. And it's something-- It's a very handy thing to do in quite a few places, actually.
So the page setup class itself, its public declaration is very simple, just init with document. Objective C, you don't have to declare every method that it implements over and over again, both in the header and in the source code file. This is public because TextEdit's document class uses this.
The actual methods in the page setup class, in it with document, all this does is initialize itself using the typical Cocoa pattern. Make that a little bigger. Initializes itself using the typical Cocoa pattern. And then all the other methods on this, and that's about how many of them there are, correspond to those attributes that we declared in the script suite file.
So there was a paper name method, or I'm sorry, there was a paper name attribute in that script suite file. So here we have set paper name, this is the setting accessor, and the get accessor, paper name. And this is it. This is about as simple as it gets.
The implementation of these particular ones asks the document for the print info object. It's an instance of the NSPrintInfo class from AppKit. And set the value. And then tell the document that the print info has been updated so that it can update the screen and things like that. So real quick, just to show this kind of stuff in action.
[Transcript missing]
So, wrap to page, because that better illustrates the page orientation. So this is it. Set orientation of page setup to landscape. Typically simple AppleScript. And it does that. So, no, no, no. Let's see that in slow motion, though, because some interesting things are happening, and they can be seen with the debugger.
So I have a couple breakpoints set here already. That page setup accessor on the document class, and because we're filling the orientation right there, the set orientation accessor method that I'll explain when we hit it. So run that. Stopped at breakpoint. Good.
[Transcript missing]
And what happens during the handling of one of those Apple events after it's been converted to an instance of NSScript command, the NSScript command is executed. And the first step of executing a command like this one is evaluating the receivers for the command.
An NSScript object specifier takes care of that. Objects by evaluating specifier, by evaluating itself, really. And then this is the neat part right here. NSScript object specifier, at this point, it's actually already found the document object. Now it wants to find the page setup attribute of the document object. What it does is it sends the document object a value for key message. We didn't bother overriding that in document.
We just let NSObject's implementation of that method handle it. And NSObject value for key calls document page setup. This stuff in between here and all the rest of it should just be called a miracle occurs because an attribute name became a method name without us having to do very much of anything.
I'll continue there. We'll hit this other breakpoint on our page set of classes, set orientation method. Stacktrace is interesting for this one, too. AppleScript has lost patience with us. That's okay. We're executing the command. We're not evaluating the receivers at this point. And at this point, NSSetCommand has determined that the receivers for the command don't handle it themselves. Because in the Text-as-Document class, there was no supported commands declaration for set commands, not surprisingly.
So what it did is it's doing its own default implementation. Its default implementation, this is NSSet command, calls take value for key. Value in this case is an NSNumber that contains that four-character code enumeration, and the key is orientation. And the key actually was declared as orientation, all lowercase, in the script suite file. That's okay. We're smart enough to capitalize for you.
So another miracle occurs, and page setup orientation is invoked. One of the interesting things that key value coding did for us, by the way, here, is even though the type for this attribute was to be declared a certain kind of NSNumber, and NSNumbers really were flying about internally. That's what was passed to that take value for key invocation. Key value coding is smart enough to use Objective-C's introspection features to find out that this method is declared to take, in this case, an enum declared like this, and actually becomes an integer.
So it actually pulled the integer out of the NSNumber for us, and passed that instead. So saving us a line of code there. And NS print info is not so smart. It doesn't understand AppleScript four-character codes, and definitely never will. So we had to write a little bit of code here to take that four-character code, and to make it into one of the CE nums that print info understands. So, and just, I'm going to go back to the slide. Just to show you something else while I'm up here.
So the margins here, I think, are about an inch on each side. That's the default. Here's another script. Set margins to one half inch. We had declared attributes like left margin, top margin, right margin, along with all the paper size and scaling and things like that on our page setup class. And that, of course, works.
I want you to take note of this right here. Set properties of page setup. There's actually a property called properties. Its value, either going in or coming out, is a record of potential properties. That isn't something that's part of text edit, and it's not something that we just added. That's automatic, and I'll explain that later. So to go back to the slides.
So, just as there's key value coding for attributes, there's key value coding for too many relationships. It's one more level of complication, not really complication, but something you have to deal with because you're dealing with groups of objects instead of single objects. We use the same method, value for key, in a lot of cases for relationships also.
Cocoa scripting, we'll call that on your scriptable classes when you've declared too many relationships. In this case, though, if it's a relationship, you should return an array of objects instead of a single object. If that sounds inefficient to you, if you don't already have an array hanging around to return, know that in more and more cases, this is evolving right now as we speak, we try to call methods like ValuateIndex and PropertyKey relationship name messages.
and what you implement there is a method named value and relationship name and index, and you just return a single object. You don't have to make up an array if you don't already have one around. And that's potentially, of course, much more efficient than when we ask your container class for an array of objects that might not already be nicely bundled into an array.
and something we added pretty recently, in addition to Value-It Index, which has been there for a while, there's now Value-With-Name and Property-With-Key and Value-With-Unique-ID and Property-With-Key. These correspond to the AppleScript name and ID reference forms. And what you do to support those is implement methods whose names take that form right there.
Value and RelationshipNameWithName. Again, RelationshipName is the name that was used for the relationship when you declared it in the script suite file. And those return a single object. And these aren't used for every kind of element, only elements whose class declare name and ID properties. Name or ID properties.
And there are three more possible messages for key value coding that relate to too many relationships. We call things like replace value in index and property with key with value. And your container class has to implement a method whose name takes the form replace in relationship name at index. And those are used during, for example, the copy command, or the duplicate command, rather, the delete command, the make command, things like that.
So in addition to supporting those key value coding methods, which is pretty easy, one more thing that you should be aware of that you might have to do, if you support classes in your scripting system that are creatable, for example, in TextEdit, make new document, you do have to make sure that that class has a good init method.
And that's just the plain init method. There are no parameters passed into that. What we do, though, because the make command has a with properties parameter that is optional, we'll call the set accessors for all the attributes on that freshly created object that correspond to those that were in the record that appeared with the with properties parameter. So.
and it's not really possible to make an object that just hangs in the middle of nowhere. You always have to specify what container it's going to be inserted into immediately. So make sure the valid container classes for that class that might be credible implement the proper replace in and insert in accessor methods.
And one more thing to deal with. This is the big complexity, actually, relatively speaking. Your scriptable object may be sent an object specifier message. Just as object specifiers come in, very often they go out. The result of a make command is a reference in AppleScript, which means we have to return an object specifier. So your newly created object might be asked pretty much immediately for its object specifier. And another case like that is in the get command.
When the reference that goes with the get command refers to something that is not a simple value type, is an actual object, the object that your accessor has returned will be asked for an object specifier. And what you're expected to do there is to create some subclass of an script object specifier, index specifier, name specifier, unique ID specifier, becoming the typical ones, and return that.
A big implementation detail about this is that you'll probably have to ask the container of the object, of the element object, for its object specifier, which is actually kind of inconvenient in a lot of people's code, and we are doing something to address that that I'll discuss later.
So I showed you a little bit about script suite files, and I've been talking about types, but I haven't gotten specific about what exactly you can use as types in script suite files. You have to specify type names when declaring class attributes, class relationships, command arguments, and command results. The valid types include things like the names of scriptable classes, a few selected foundation classes, and a little more complicated thing that I'll talk about.
Scriptable class names. You can use as type names classes that were declared in the same script suite in which that declaration appears, and also classes that were declared in other script suites if you specify which script suite. You should know that we declare a few classes in Foundation's own NSCoreSuite script suite file, including things like NSApplication and NSDocument and NSWindow that are actually implemented in AppKit, but nonetheless declared in Foundation's NSCoreSuite.script suite. That's where to go to look for that.
Some value types that we support: NSString, which will always appear in scripting dictionaries as Unicode text. NSDate, which corresponds to date, of course. NSHorray. If you've been working with Cocoa scripting already and you tried to declare the type of a property to be a list in NSHorray, it didn't work at all. It was a big bug. It was a whopper.
And we took care of that, though, so that will work in the Jaguar seed that you have. NSDictionary's. That's what you use to declare something that you want to appear as a record in a scripting suite. And references and location references. We have classes that correspond to those.
You saw some of the NSNumber types that were in the script suite file there before. We support a big long list of those. These get moved around as NSNumbers, but appear as specific friendly AppleScript types in the scripting dictionary. and the one little complication as far as types go is for enumerations.
When you specify that an enumeration is the type of a value, you have to also declare elsewhere in the ScriptSuite and script terminology files information about that enumeration. The four character codes that correspond to each enumerator ask yes and no, for instance, and you have to provide descriptions and names for each of those, too.
So, let's talk about the new stuff that's appearing in Jaguar. I already alluded to this. We have a new class called NSNameSpecifier. It supports AppleScript's name reference form, so something like window named Cocoa Scripting. is an example of this. We used to ask for the application for all of its windows and poke through each one of them asking for its name.
That's not so bad in the case of windows, but it's potentially very awful in the case of other things. The first example anybody ever asked me for was a stock ticker application of some sort where they wanted to refer to stock information by the symbol. Of course, there are thousands of those possible.
They had a way to look it up quickly using a dictionary, and now that quickness can flow through to the Cocoa scripting system this way. So by implementing a method named value in relationship name with name, when a name reference form comes in, we'll send you that, and you can make it as fast as you can to return the named object. To trigger this, the element class has to have an attribute that's going to be called the element class. The element class is a code whose Apple event code is PNAM, the traditional four-character code for that.
And the same thing for the ID reference form. We now have a class named NSUniqueIDSpecifier. It's triggered when you have a four character code ID space space. There are two spaces in that four character code there. And there's a method that corresponds to that, value and relationship name with unique ID.
So one thing I showed you with that page setup example was setting all those margins at one time by setting the properties of that page setup object to a record that included values for each of the four margins there. And that is now just built into Cocoa Scripting. We have all the information necessary to support this because you put it all in the script suite file. So you don't have to do anything beyond that. Each one of your classes will have a properties property in it.
Mark Piccirelli Actually, it's in our item class, which you will be a subclass of if you refer to nscoresuite.abstractobject in your script suite file. And just as for any other attribute, there are accessor methods for these. We've made ours public for this in a category on nsobject with the predictable names.
I'm not expecting you to probably ever have to override those, but they're there just in case. A neat little feature we added recently. Lots of other scriptable applications were doing this, so we thought we'd make it easy for you to do it, too. In an application like TextEdit, for instance, people used to have to write fourth word of text to front document.
Well, when they say fourth word, what else would it be in a TextEdit document but the text? So we now let you add support for that little bit of ease of use, and you do that with a single entry in the script suite file called default subcontainer attribute.
NSAppleEventDescriptor, a class that's been in Cocoa for a while. We've added a few methods on it. NSAppleEventDescriptor appears in our API in a few places, and it's going to be appearing in a few others potentially in the future. So we've started making it a little bit easier to pick these things apart with methods like bool value and string value when you know the AppleEventDescriptor contains one of those, or contains, for instance, something that is coercible to one of those. And to create them, we added a few convenient class methods too.
I know people want this class because I've been reading the mailing list and it's becoming sort of a frequently asked question. How do I execute AppleScripts from within Cocoa? And there are actually a couple of freely available classes that do this now, but built into the system very often is one step better than free, so we've done that. You no longer have to resort to the Carbon APIs to execute AppleScripts. And it has the things that you would expect.
You can initialize an NSAppleScript that corresponds to the contents of a file, including a compiled file. And despite the name of the class, any OSA language is good. You can initialize from source code. You can compile and execute, and we give you ways to get at all the error information that might be returned during that.
And one neat feature, NSAppleScript is actually a class in Foundation, but there's a category on it in the AppKit with a single method, rich text source. When you invoke this on a script that's compiled successfully, it will do pretty much the same thing that Script Editor does and pretty print the source code, except it returns an NS attributed string to you that you can just slam into an NSTextView or something like that without any trouble and show good looking AppleScript code to your users.
So all this, of course, is described in the release notes, deep within the turgid prose of those pages and pages of scripting release notes. Absolutely every new feature in BugFix is described in great, grueling detail. This is on your CD right now, one of your six CDs right now, at developer documentation release notes, foundation.html. Most of scripting is in foundation.
So, future directions. If you were at the AppleScript update session yesterday, you saw the introduction of the new SDEF file format for defining suites. And, of course, we're going to adopt this eventually. Editing script suite and script terminology files is all well and good, but editing SDEF files looks like it's going to be one step easier and will allow us to add a few other features when we adopt that.
That's not going to happen for the final release of Jaguar, though, almost certainly. So, even before then, even with our existing script suite and script terminology file formats, we're going to make those a little bit better so that the scripting dictionaries that the scripter sees will be a little prettier.
Right now, you don't have control over the order in which things appear in those scripting dictionaries, which is a little bit of an omission, and we're going to take care of that. And also, there's going to be a mechanism that will be reminiscent of Objective-C categories. So that you can add attributes onto scriptable classes that are declared in other suites without making a redundant subclass appear in the scripting dictionary.
We're going to work on our type checking. We already do a bunch, but we can do a lot more. Our goal is to eventually fix it so that you specify in good detail the scripting definition file, what types an attribute can have, or relationship. And then we'll take care of absolutely everything for making sure that things that are coming in conform to those types without you having to do anything at all.
And a closely related subject, we're working on our error reporting. If you've been working with scripting a Cocoa application, you might have seen it return some truly horrifying error messages that no scripter could ever parse or make head or tail of. And we're working on replacing those with something more informative.
So other things we still plan on adding. Cocoa API for sending Apple events. Right now you can call just about any Carbon function from a Cocoa app, but it seems like a lot of people don't like to. People want that kind of functionality wrapped up in an Objective-C or Java class and made a little more convenient, a little more pretty, and of course we want to do that too, so we will.
There are a couple of complicated implementation details that you might run into as far as how your scripting support interacts with your undo support. Some things that you have to take care of that might surprise you when you start working with it. And we have ways in mind actually to automate all of that. So scriptable commands will automatically be undoable without you doing much of anything, probably adding a single method to container classes.
I talked before about the inconvenience of what goes on when you implement an object specifier method, when an object is asked to return one of these for itself. That does cause a good bit of trouble, and we're probably going to change that. Of course, we'll keep supporting the old way for backward compatibility. But we're going to start asking objects' containers for object specifiers instead of the objects themselves. This way, objects won't have to be cognizant of their containers just for this sake.
There are plenty of places in coding where that is appropriate, but to have to add it just for the sake of this one method is a great inconvenience. And the last thing, of course, definitely not the least, is recordability. You're not a perfect scriptable application in a lot of cases until you support recordability. And right now, we don't do anything to help you with this. And, of course, we do intend to.
So the roadmap, the AppleScript update yesterday was fantastic. I hope you all saw it. AppleScript studio talks are always good. There's a feedback forum, and that was yesterday too. There's a feedback forum for AppleScript. That's where you can go to let them know what you think about how excited you are about AppleScript 10 that's coming up and general scripting issues like that. Cocoa's own feedback forum is tomorrow at 5 o'clock. So, who to contact? Heather Hickman is our technology manager. If you've been on the Cocoa track, you've been seeing our email address flash up and down a couple of times.
Documentation. There is plenty of documentation for Cocoa scripting. It's spread out a little bit, though. In particular, in the Cocoa topics, there's two very relevant topics. First one is application architecture. It explains the model view controller pattern. Even if you're not working on scriptability for your application right away, you should read this documentation before you start writing, because if you follow a lot of the patterns it discusses, when you do add scriptability, it'll be a good bit easier, probably. The really specific Cocoa Scripting documentation is in its own topic, also available from the typical web pages. So it gives you a pretty good overview of the programming tasks that have to be dealt with, and it links to the class reference and things like that.
I wanted to bring this one to everybody's attention. Designing a scripting implementation. A seven-year-old article that appeared in Develop Issue 21 by Cal Simone. I keep referring back to this. This is a fantastic article. A lot of the implementation details are pretty irrelevant for Cocoa scripting now, but it's really easy to pick those out.
What's important about this article is he presents guidelines for what makes a good scriptable application. How do you decide when something should be a command, when should it be a property, when should it be an element, things like that. It's still a very valuable document. And he also gives you there some advice about picking four-character codes. and for more information, the usual suspects, Cocoa developer documentation, iServices technical training. On the O'Reilly webpage, somebody's writing some very interesting articles about AppleScript in general on Mac OS X, and some of them are specifically about Cocoa scripting.
and before I go, I want to bring this application to your attention. It's called Suite Modeler. It's by a guy named Don Briggs. It's available on his iDisk. It's a shareware application that lets you edit script suite and script terminology files together. It makes a lot of things much more convenient, and it does a good job of checking for things like Apple event code conflicts and other mistakes. He's actually used it to find a few bugs in our own script suite and script terminology files that appear on the system.
And he sent those to me, and we've taken care of that, of course. And, of course, on his iDisk, he has a very interesting tutorial that goes along with that, too. So, that's it. Go make your applications scriptable, but before you do that, I'm sure you'll have a few questions, so I'd like to invite Heather Hickman and others up to the stage to walk us through some of it.