Implementing a Scriptable Application

This chapter lists the key steps for implementing a scriptable Cocoa application, with links to more detailed information where necessary.

Implementation Guidelines

Once you have completed one of the design phases described in “Designing for Scriptability,” you use steps like the following to implement a scriptable Cocoa application:

  1. For a new application, implementing scriptability should be an integral part of creating the application. That is, you’ll be creating scriptable classes, adding scripting accessor methods, and so on, as you implement other parts of the application.

    When you’re adding scriptability to an existing application, there is more opportunity for a staged or incremental approach. That is, you may want to test your approach by retrofitting one or a small number of classes, before extending it to the entire application.

    In either case, your work should include milestones to test each phase of scriptability, as spelled out in your test plan.

  2. Cocoa follows the Model-View-Controller (MVC) design pattern, where model objects encapsulate and manipulate the data used by the application. You should generally support scriptability in your model objects, which tend to be more persistent. Although there may be some cases where you want to allow scripting of your view objects, keep in mind that scripts that operate on the user interface tend to be fragile, and they can also be less efficient.

    For more information, see “Concentrate Scriptable Behavior in Model Objects.”

  3. Provide an sdef file with the scriptability information for your application.

    For more information, see “Supply a Scripting Definition File.”

  4. Maintain key-value coding (KVC) naming compliance for instance variables or accessor methods for scriptable properties and elements, based on the keys in your sdef file. Cocoa scripting support relies on this naming compliance.

    For details, see “Maintain KVC Compliance.”

  5. Include the sdef file in the Xcode project for your application, as described in “Add the Scripting Definition File to Your Xcode Project.”

  6. Modify your application’s Info.plist file to turn on Cocoa scripting support and identify your sdef file, as described in “Turn On Scripting Support in Your Application.”

  7. Cocoa implements the NSScriptCommand class and a number of specific subclasses, such as NSDeleteCommand, NSGetCommand, NSMoveCommand, and NSSetCommand. However, for some commands implemented by Cocoa, your application may need to provide a different implementation.

    For information on how to do this, see “Steps for Implementing a New or Modified Script Command.”

  8. Implement objectSpecifier methods for scriptable classes in your object model. These methods describe the object and point to its parent in the object containment hierarchy (with the application object generally serving as the outermost container). They are invoked by an instance of NSGetCommand when it works with your application to obtain the requested information.

    If you’ve created helper classes to add scriptability to an existing application, these classes also need to implement object specifier methods.

    For more information, see “Implement Object Specifier Methods for Scriptable Classes.”

  9. Implement any new script command subclasses your application requires.

    Many applications provide unique capabilities, such as rotating an image or converting between two audio formats. To make these features scriptable, you may need to define new script command classes that are subclasses of NSScriptCommand or one of the other command classes provided by Cocoa.

    For more information, see “Subclasses for Standard AppleScript Commands” and“Script Commands Overview.”

  10. To take advantage of Cocoa scripting support that works with document and window classes, your application should use the Cocoa document architecture.

    For more information, see “Use the Document Architecture.”

  11. To take advantage of Cocoa scripting support that works with text, your application can take advantage of Cocoa's built-in support.

    For more information, see “Access the Text Suite.”

  12. Throughout the implementation process, test your application according to the test plan you developed.

    For tips and suggestions, see “Testing, Debugging, and Performance.”

Supply a Scripting Definition File

Every scriptable application must provide a definition of its scriptability information—the terminology available for use in scripts that target the application, as well as the implementation information used to support that terminology. This information includes a set of keys for the scriptable properties accessible in the application through key-value coding (described in “Provide Keys for Key-Value Coding”).

If you developed an sdef file during the design phase, you've already completed this step. If not, see “Preparing a Scripting Definition File” for a description of the steps you take to create an sdef file and add scriptability information to it.

For information on working with the older scriptability format, see “Script Suite and Script Terminology Files.”

Concentrate Scriptable Behavior in Model Objects

The Model-View-Controller (MVC) paradigm is one of the central design patterns for Cocoa applications. MVC assigns objects in an application to one of three roles and recommends that you try to maintain a separation among objects of different roles.

Generally, the objects that you make scriptable should be model objects. The most efficient way for a script to perform a task generally involves modifying the model and is often not the same as the best way for a user to do the same task through the user interface (or view). This is consistent with how AppleScript works, and Cocoa accordingly gears its scripting support to the model layer.

A script should not require the user’s involvement, unless it is intended more as a macro than as a form of batch processing. In a macro-like script, the user must prepare things for the script (such as opening a window and creating or selecting certain objects), and then invoke it. If you anticipate that your application will be scripted for this purpose, you may want to provide scriptable behavior to the appropriate nonmodel objects, such as windows and selections. If so, be sure to confine your scripting support in nonmodel objects to those specific purposes.

Provide Keys for Key-Value Coding

Recall that key-value coding (KVC) is a mechanism for accessing object properties indirectly by key, where a key is just a string that represents a property name (such as "xPosition" for the horizontal coordinate of a graphic object). Cocoa scripting relies on KVC, both for finding the specified objects for a command to operate on and for getting and setting values in the specified objects.

Your application provides keys for its scriptable properties and elements in class definitions in its sdef file. The property and element names themselves serve as keys, unless you specify a different key explicitly. Cocoa scripting adjusts the key names as necessary according to the following rules, which are consistent with standard Cocoa naming conventions for accessors:

You can override the default naming conventions to specify arbitrary key values where necessary. For example, suppose you want a scripter to be able to use color in a script, but your application refers to the underlying property as foregroundColor (as in the NSTextStorage class). You can specify "foregroundColor" as the key for the "color" property by adding a cocoa key entry to the sdef property definition:

<property name="color" code="colr" ...
    <cocoa key="foregroundColor"/>

To support getting and setting scriptable properties and elements in your application, you define accessor methods that match the keys in your sdef, as described in “Maintain KVC Compliance.” For additional information on default naming and working with keys, see “Cocoa Elements.”

Add the Scripting Definition File to Your Xcode Project

Once you have created an sdef file, you'll need to add it to the Xcode project for your application. Place the file in the project folder (or other appropriate location) and use Project > Add to Project, which also lets you add the sdef file to application targets. Adding the sdef file to the project automatically adds it to the Copy Bundle Resources build phase, so it will be included in the application.

Turn On Scripting Support in Your Application

To turn on Cocoa’s built-in scripting support, you add the following key to your application's Info.plist file:

    <key>NSAppleScriptEnabled</key>
    <string>YES</string>

To provide your application's scriptability information through an sdef file, you add a second key to the property list to specify the sdef file. Here's the entry for the Sketch application:

    <key>OSAScriptingDefinition</key>
    <string>Sketch.sdef</string>

Implement Object Specifier Methods for Scriptable Classes

An object specifier locates a scriptable object or objects within the containment hierarchy in which they reside.

When a script statement targets an application, the application may need to return a reply. For example, the result of a get command is an object or a list of objects. When Cocoa returns these objects in the reply Apple event, it does not return pointers to Objective-C objects, it returns object specifiers.

To obtain the object specifiers, Cocoa sends objectSpecifier messages to the objects to be returned. Therefore, for any class of object that is part of your containment hierarchy of scriptable objects, you must implement the objectSpecifier method. This method is declared in NSScriptObjectSpecifiers, a category on NSObject that implements a version that just returns nil.

For more information, including code examples, see “Object Specifiers.”

Use the Document Architecture

The NSApplication, NSDocument, NSDocumentController, NSWindow, and NSWindowController classes form the basic structure for the Cocoa document architecture. Together with the terminology defined in the Standard suite, these classes provide direct support for the standard AppleScript document scripting model, including classes such as application, document, and window. When you use these classes to implement a document-based application, that application automatically supports a number of scripting features.

For example, the NSApplication, NSDocument, and NSWindow classes are KVC-compliant for standard scriptable properties. NSApplication provides methods for accessing the application's documents as an ordered list. The NSDocument class provides support for the close, print, and save commands by implementing the handleCloseScriptCommand:, handlePrintScriptCommand:, and handleSaveScriptCommand: methods. NSWindowScripting also implements default versions of these methods, which in many cases pass control to the window's document. It also implements methods for scriptable access to window attributes, such as the close box, title bar, and so on.

Applications that take advantage of Cocoa’s document architecture put themselves in a better position to support scripting generally. A document in Cocoa applications typically owns and manages one or more model objects of the application. It therefore provides a hub for scripted access to the model objects in your application, which are the ones that load and save data.

Table 3-1 lists Cocoa classes that correspond to AppleScript classes in the Standard suite, along with attributes and relationships (properties and elements) used by those classes.

Table 3-1  Standard suite attributes and relationships

Objective-C and AppleScript class

Attributes (script term, if different)

Relationships

NSObject

Implements the item AppleScript class. For any scriptable Objective-C class that inherits from NSObject, the AppleScript class it implements inherits from the item class (and inherits the class property and the properties property).

class name (class), properties

NSApplication

Implements the application AppleScript class.

name, active flag (frontMost), version

documents, windows (both accessible as ordered relationship)

NSDocument

Implements the document AppleScript class.

location of the document's on-disk representation (path); last component of filename (name); edited flag (modified)

NSWindow

Implements the window AppleScript class.

title (name); various binary-state attributes: closeable, floating, miniaturized, modal, resizable, titled, visible, zoomable

document

Access the Text Suite

The Text suite defines terminology that allows scripts to request or select textual elements at different levels of granularity: character, word, paragraph, or entire body of text. The NSTextStorage class, provided by the Application Kit, defines a corresponding set of methods for getting and setting scriptable properties of NSTextStorage objects.

To gain access to this text scripting support, use an NSTextStorage object as the content for one of your scriptable classes. The TextEdit sample code (available at <Xcode>/Examples/AppKit/TextEdit) demonstrates a scriptable application that supports text scripting, as well as scripting support for printing.

Table 3-2 lists Cocoa classes for working with text, along with attributes and relationships used by those classes.

Table 3-2  Text Suite Attributes and Relationships

Class

Attributes (script term, if different)

Relationships

NSTextStorage

font name (name), font size (size), foreground color (color)

attribute runs, characters, paragraphs, text, words