Integrating AppleScript and Cocoa
Pages: 1, 2, 3, 4
AppleScript's Object Hierarchy
If you are familiar with AppleScript, you might already see the link between the AppleScript syntax and this object heirarchy.
tell application "MyApp"
get the first word of the first paragraph of document 1
end tell
This is a typical AppleScript snippet. The heirarchy here goes from the
generic application object MyApp to the document object document 1
to paragraph object first paragraph to word object first word. These
map, in AppleScript speak, to elements and their properties. Let's look
at a little more AppleScript syntax, and then we'll put it all together.
tell application "Graphics App"
set the color of window 1 to "blue"
end tell
|
Breakdown of "ScriptableApp" line by line "activate" is a command to application "ScriptableApp" the set command to AppleScript with the arguments a script variable "appName" and the results of the command "name" sent to the application. the set command to AppleScript with the arguments a script variable "appVersion" and the results of the command "version" sent to the application. the set command to AppleScript with the arguments a script variable "numWins" and the count property of the collection of window elements in the application. the set command to AppleScript with the arguments a script variable "numDocs" and the count property of the collection of document elements in the application. the set command to AppleScript with the arguments a script variable "w" and the index property of the last element of the collection of windows returned by the get command with the identifier of every sent to the application. close is a command sent to the application. |
The topmost scripting object is the application Graphics App. This
script is looking for an element identified by the word window with
the properties of that element being color and an index of 1 (this
could have also been first, last, mid, or any collection identifier), and
we send the set command to the application with the arguments of the
property of the element identifier and the new value of that property.
tell application "ScriptableApp"
activate
set appName to name
set appVersion to version
set numWins to count of windows
set numDocs to count of documents
set w to the last Abstract object of (get every window)
close w
end tell
Now, this one is a little more complicated, but try to find the elements, properties, and commands, and which object is being asked to do what. You should try to imagine what the object hierarchy for this application might look like.
Adding AppleScript support to a Cocoa application is a matter of providing a map between the elements and their properties of a scriptable application, and the Cocoa objects or other objects that make up that application, including the application object iteslf. Now while this may seem daunting, we are fortunate that Apple has already done this for us with the Core Suite for the most common Cocoa objects used in applications that are normally scriptable.
Core Suite and "For Free"
In the first article, we set the key NSAppleScriptEnabled to YES in the
ScriptableApp's info.plist and gained a lot of scriptability right away.
This scriptability comes from activating the Core Suite available to all
Cocoa applications. The Core Suite provides a map for us.
Remember that
our Cocoa application is a bunch of objects, primarily an instance of
NSApplication (from main.m) and an instance of NSWindow (from
MainMenu.nib). The window may be a little confusing, if you haven't
looked at other tutorials on Cocoa and using Project Builder and
Interface Builder.
You can use Interface Builder to create instances of
UI objects, which are stored in .nib files and are loaded automatically
when the application launches. This is all done graphically, so there is
no corresponding source code viewable in Project Builder. There are some
other objects floating around, like NSMenu, NSMenuItem,
NSWindowController, and others, but they are not typically scriptable, so
we will not focus on them. The Core Suite identifies certain Cocoa
classes that will be scriptable. We can see these classes in a file
called NSCoreSuite.scriptSuite, located at
/System/Library/Frameworks/Foundation.framework/Resources/NSCoreSuite.scriptSuite
You can look at this file in a text editor, or in a program called
Property List Editor in the /Developer/Applications directory. This XML
file has a lot of information in it, so I would recommend using the
Property List Editor, just to make it easier to see what is going on.
NSCoreSuite.scriptSuite has several top-level keys: AppleEventCode,
Classes, Commands, Enumerations, Name, and ValueTypes. For now, let us
look at the Classes and Commands sections. The Classes mentioned are
AbstractObject, NSApplication, NSColor, NSDocument, and NSWindow.
For Commands, we see Close, Copy, Count, Create, Delete, Exists, Get, Move, Open, Print, Quite, Save, snd Set. If you look at ScriptableApp's script dictionary, you'll see just these items. Every item has its own AppleEventCode associated with it, and if you look through the Classes, you'll see keys for the Superclass of the class and the SupportedCommands. Also, keep in mind that these classes can inherit attributes and commands from their Superclass, as each of the later classes do from AbstractObject.
NSCoreSuite.scriptSuite in Property List Editor |
The Commands section matches each command up with a Cocoa object that is
a subclass of NSScriptCommand. This is the CommandClass key found in
each command.
For example, Copy is matched with an NSCloneCommand
object. Each time a command is used in a script, an object of the
corresponding type is created to ensure that the command is carried out. If
you are more experienced with object-oriented programming, this may seem
a little odd, as commands (methods) are usually defined along with
their objects, so this breaks the OOP paradigm. However, AppleScript is
designed to have a small number of commands that work on a large number
of elements, so this arrangment works best for that idea.
The commands
also define an AppleEventClassCode, in additional to the AppleEventCode,
and their Arguments and return Type. A command may not have either
arguments or return types, and if so, those keys are simply left blank.
Return types are usually of the type NSString or NSNumber, but they can
be other classes as well. There are many more items in this file, and we
will talk more about them in later articles, especially when we start to
create our own script suites, but now let us turn our focus more towards
the AppleScript side.



