Monday, July 27, 2009

Supporting AppleScript in a Cocoa Document-Based App

It seemed like it should be easy, and it was, but finding out how was hard. So I am posting what I did here in the hope that the next person wanting to do it finds the info they need here.

What I wanted to do


I had a Cocoa document-based app. I.e. I had followed my nose with Cocoa, and ended up with an app that supported multiple documents. Documents are represented by in subclass of NSDocument called MyDocument.

I wanted to support viewing and changing properties of the document from AppleScript.

Note that I wanted to support 10.5+, so the techniques here may not be appropriate for older versions.

How I ended up getting it working


  1. Specify things in the app's Info.plist:
    NSAppleSriptEnabled = YES

    OSAScriptingDefinition = MyApp.sdef


  2. Made an sdef file, which includes all the framework supplied stuff, and that MyDocument extends the framework document, and has a property, refered to by the key numBananas:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">

    <!-- declare the namespace for using XInclude so we can include the standard suite -->
    <dictionary xmlns:xi="http://www.w3.org/2003/XInclude">

    <!-- use XInclude to include the standard suite -->
    <xi:include href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite)"/>

    <suite name="MyApp Suite" code="LfMS" description="MyApp application specific scripting facilities.">

    <class-extension extends="document" code="docu">
    <cocoa class="MyDocument"/>
    <property name="number of bananas" code="Bana" type="integer" access="rw" description="The number of bananas in this document" >
    <cocoa key="numBananas"/>
    </property>
    </class-extension>

    </suite>

    </dictionary>

  3. Implement KVC accessors for the property. These looked (sortof) like:
    -(NSNumber*)numBananas
    {
    return [NSNumber numberWithInt:bananas];
    }

    -(void)setNumBananas:(NSNumber*)value
    {
    bananas = [value intValue];
    }



This was all that it took, and now I can enumerate all the docs, set their number of channels etc etc.

Apologies to all my regular readers if this is slightly outside your sphere of interest!

No comments: