Help for debugging incorrect return type labels?

I’m trying to make my Swift app scriptable.

I’m getting weird labels on returned objects. For example if I run this script:

tell front document of application "Bike"
	outline items
end tell

I get this result (In both Script Debugger and Script Editor when the outline has a single item):

{export id "KS" of document "Untitled" of application "Bike"}

The result is weird because the class being returned is outline item, not export. Instead export is the name of a command following the outline item.

{outline item id "KS" of document "Untitled" of application "Bike"}

In particular notice the type table is export instead of outline item. Export is the name of a commands in my sdef, not the name of a type. Something is obviously wrong, but I’m unsure how to debug and fix. Also odd, generally things seem to work except for this incorrect label. I can read (outline item) attributes from the returned objects. I can make new outline items. It’s just they are always reported in results as using export label instead of outline item.

Any thoughts on where I go from here?


Here’s my .sdef:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">

<dictionary xmlns:xi="http://www.w3.org/2003/XInclude" title="Bikes Terminology">
    
    <xi:include href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite)" />
    
    <suite name="Bike Suite" code="BKsu">
        
        <enumeration name="saveable file format" code="savf">
            <enumerator name="Bike Format" code="BKff" description="Bike file format">
                <cocoa string-value="com.hogbaysoftware.bike.xml" />
            </enumerator>
            <enumerator name="OPML Format" code="OPml" description="OPML file format">
                <cocoa string-value="org.opml.opml" />
            </enumerator>
            <enumerator name="Plain Text Format" code="PTfm" description="Plain text file format">
                <cocoa string-value="public.utf8-plain-text" />
            </enumerator>
        </enumeration>
        
        <class name="outline item" code="BKoi" description="An outline item">
            <cocoa class="Bike.OutlineItem"/>
            <property name="id" code="ID  " type="text" access="r" description="The unique and persistent identifier of the item." />
            <property name="level" code="ILvl" type="integer" description="The indentation level of this item." />
            <property name="name" code="pnam" type="text" description="Plain text content of this item." />
        </class>
        
        <command name="export" code="BKsuexpt" description="Export outline items.">
            <direct-parameter type="document" description="The document to export items from."/>
            <parameter name="as" code="fltp" type="saveable file format" optional="yes" description="The file format to export.">
                <cocoa key="ContentFormat"/>
            </parameter>
            <result description="The exported outline items.">
                <type type="text" />
            </result>
        </command>
                
        <class-extension extends="document">
            <cocoa class="Bike.Document" />
            <element type="outline item" />
            <responds-to command="import">
                <cocoa method="handleImportScriptCommand:"/>
            </responds-to>
        </class-extension>
        
    </suite>
    
</dictionary>

And here’s when I see when I print(NSScriptSuiteRegistry.shared()):

Suite: Intrinsics ('intr'), hidden: yes
    Name: "Intrinsics", description: ""
    Class: item ('cobj'), superclass: <none>, hidden: no
        Implementation class: NSObject
        Name: "item", plural name: "items", description: "A scriptable object."
        Attribute: classCode ('pcls'), type: type ('type'), access: read-only, hidden: no
            Name: "class", description: "The class of the object."
        Attribute: scriptingProperties ('pALL'), type: record ('reco'), access: read-write, hidden: no
            Name: "properties", description: "All of the object's properties."
        Default subcontainer: <none>
        Supported command: delete, method: -
        Supported command: exists, method: -
        Supported command: set, method: -
        Supported command: get, method: -
        Supported command: count, method: -
        Supported command: move, method: -
        Supported command: duplicate, method: -
        Primitive type: <none>
    Command: get ('core'/'getd'), hidden: no
        Implementation class: NSGetCommand
        Name: "get", description: "Returns the value of the specified object(s)."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Result type: any ('****')
            Description: <none>
    Command: set ('core'/'setd'), hidden: no
        Implementation class: NSSetCommand
        Name: "set", description: "Sets the value of the specified object(s)."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: Value ('data'), type: any ('****'), optional: no, hidden: no
            Name: "to", description: "The new value."
        Result type: <none> ('null')
            Description: <none>
    Value type: any ('****')
        Implementation class: NSAppleEventDescriptor
    Value type: boolean ('bool')
        Implementation class: NSNumber
    Value type: date ('ldt ')
        Implementation class: NSDate
    Value type: file ('file')
        Implementation class: NSURL
    Value type: integer ('long')
        Implementation class: NSNumber
    Value type: location specifier ('insl')
        Implementation class: NSPositionalSpecifier
    Value type: missing value ('msng')
        Implementation class: NSNull
    Value type: number ('nmbr')
        Implementation class: NSNumber
    Value type: point ('QDpt')
        Implementation class: NSData
    Value type: real ('doub')
        Implementation class: NSNumber
    Value type: record ('reco')
        Implementation class: NSDictionary
    Value type: rectangle ('qdrt')
        Implementation class: NSData
    Value type: specifier ('obj ')
        Implementation class: NSScriptObjectSpecifier
    Value type: text ('ctxt')
        Implementation class: NSString
    Value type: type ('type')
        Implementation class: NSNumber
    Object type: item ('cobj')

Suite: Standard Suite ('????'), hidden: no
    Name: "Standard Suite", description: "Common classes and commands for all applications."
    Class: application ('capp'), superclass: item, hidden: no
        Implementation class: NSApplication
        Name: "application", plural name: "applications", description: "The application's top-level scripting object."
        Attribute: name ('pnam'), type: text ('ctxt'), access: read-only, hidden: no
            Name: "name", description: "The name of the application."
        Attribute: isActive ('pisf'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "frontmost", description: "Is this the active application?"
        Attribute: version ('vers'), type: text ('ctxt'), access: read-only, hidden: no
            Name: "version", description: "The version number of the application."
        To-many relationship: orderedDocuments ('docu'), type: document ('docu'), access: read-write, hidden: no, insert at beginning by default: no
            (No user-readable name or description needed for to-many relationships)
        To-many relationship: orderedWindows ('cwin'), type: window ('cwin'), access: read-only, hidden: no, insert at beginning by default: no
            (No user-readable name or description needed for to-many relationships)
        Default subcontainer: <none>
        Supported command: print, method: -handlePrintScriptCommand:
        Supported command: quit, method: -handleQuitScriptCommand:
        Supported command: open, method: -handleOpenScriptCommand:
        Primitive type: <none>
    Class: document ('docu'), superclass: item, hidden: no
        Implementation class: Bike.Document
        Name: "document", plural name: "documents", description: "A document."
        Attribute: displayName ('pnam'), type: text ('ctxt'), access: read-only, hidden: no
            Name: "name", description: "Its name."
        Attribute: isDocumentEdited ('imod'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "modified", description: "Has it been modified since the last save?"
        Attribute: fileURL ('file'), type: file ('file'), access: read-only, hidden: no
            Name: "file", description: "Its location on disk, if it has one."
        To-many relationship: outlineItems ('BKoi'), type: outline item ('BKoi'), access: read-write, hidden: no, insert at beginning by default: no
            (No user-readable name or description needed for to-many relationships)
        Default subcontainer: <none>
        Supported command: import, method: -handleImportScriptCommand:
        Supported command: close, method: -handleCloseScriptCommand:
        Supported command: print, method: -handlePrintScriptCommand:
        Supported command: save, method: -handleSaveScriptCommand:
        Primitive type: <none>
    Class: window ('cwin'), superclass: item, hidden: no
        Implementation class: NSWindow
        Name: "window", plural name: "windows", description: "A window."
        Attribute: title ('pnam'), type: text ('ctxt'), access: read-only, hidden: no
            Name: "name", description: "The title of the window."
        Attribute: uniqueID ('ID  '), type: integer ('long'), access: read-only, hidden: no
            Name: "id", description: "The unique identifier of the window."
        Attribute: orderedIndex ('pidx'), type: integer ('long'), access: read-write, hidden: no
            Name: "index", description: "The index of the window, ordered front to back."
        Attribute: boundsAsQDRect ('pbnd'), type: rectangle ('qdrt'), access: read-write, hidden: no
            Name: "bounds", description: "The bounding rectangle of the window."
        Attribute: hasCloseBox ('hclb'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "closeable", description: "Does the window have a close button?"
        Attribute: isMiniaturizable ('ismn'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "miniaturizable", description: "Does the window have a minimize button?"
        Attribute: isMiniaturized ('pmnd'), type: boolean ('bool'), access: read-write, hidden: no
            Name: "miniaturized", description: "Is the window minimized right now?"
        Attribute: isResizable ('prsz'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "resizable", description: "Can the window be resized?"
        Attribute: isVisible ('pvis'), type: boolean ('bool'), access: read-write, hidden: no
            Name: "visible", description: "Is the window visible right now?"
        Attribute: isZoomable ('iszm'), type: boolean ('bool'), access: read-only, hidden: no
            Name: "zoomable", description: "Does the window have a zoom button?"
        Attribute: isZoomed ('pzum'), type: boolean ('bool'), access: read-write, hidden: no
            Name: "zoomed", description: "Is the window zoomed right now?"
        Attribute: document ('docu'), type: document ('docu'), access: read-only, hidden: no
            Name: "document", description: "The document whose contents are displayed in the window."
        Default subcontainer: <none>
        Supported command: close, method: -handleCloseScriptCommand:
        Supported command: print, method: -handlePrintScriptCommand:
        Supported command: save, method: -handleSaveScriptCommand:
        Primitive type: <none>
    Command: open ('aevt'/'odoc'), hidden: no
        Implementation class: NSScriptCommand
        Name: "open", description: "Open a document."
        Unnamed argument ('----'), type: file | list of file ('cct1'), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Result type: document | list of document ('cct0')
            Description: "The opened document(s)."
    Command: close ('core'/'clos'), hidden: no
        Implementation class: NSCloseCommand
        Name: "close", description: "Close a document."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: SaveOptions ('savo'), type: save options ('savo'), optional: yes, hidden: no
            Name: "saving", description: "Should changes be saved before closing?"
        Argument: File ('kfil'), type: file ('file'), optional: yes, hidden: no
            Name: "saving in", description: "The file in which to save the document, if so."
        Result type: <none> ('null')
            Description: <none>
    Command: save ('core'/'save'), hidden: no
        Implementation class: NSScriptCommand
        Name: "save", description: "Save a document."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: File ('kfil'), type: file ('file'), optional: yes, hidden: no
            Name: "in", description: "The file in which to save the document."
        Argument: FileType ('fltp'), type: saveable file format ('savf'), optional: yes, hidden: no
            Name: "as", description: "The file format to use."
        Result type: <none> ('null')
            Description: <none>
    Command: print ('aevt'/'pdoc'), hidden: no
        Implementation class: NSScriptCommand
        Name: "print", description: "Print a document."
        Unnamed argument ('----'), type: list of file | specifier ('cct2'), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: PrintSettings ('prdt'), type: print settings ('pset'), optional: yes, hidden: no
            Name: "with properties", description: "The print settings to use."
        Argument: ShowPrintDialog ('pdlg'), type: boolean ('bool'), optional: yes, hidden: no
            Name: "print dialog", description: "Should the application show the print dialog?"
        Result type: <none> ('null')
            Description: <none>
    Command: quit ('aevt'/'quit'), hidden: no
        Implementation class: NSQuitCommand
        Name: "quit", description: "Quit the application."
        Argument: SaveOptions ('savo'), type: save options ('savo'), optional: yes, hidden: no
            Name: "saving", description: "Should changes be saved before quitting?"
        Result type: <none> ('null')
            Description: <none>
    Command: count ('core'/'cnte'), hidden: no
        Implementation class: NSCountCommand
        Name: "count", description: "Return the number of elements of a particular class within an object."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: ObjectClass ('kocl'), type: type ('type'), optional: yes, hidden: yes
            Name: "each", description: "The class of objects to be counted."
        Result type: integer ('long')
            Description: "The count."
    Command: delete ('core'/'delo'), hidden: no
        Implementation class: NSDeleteCommand
        Name: "delete", description: "Delete an object."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Result type: <none> ('null')
            Description: <none>
    Command: duplicate ('core'/'clon'), hidden: no
        Implementation class: NSCloneCommand
        Name: "duplicate", description: "Copy an object."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: ToLocation ('insh'), type: location specifier ('insl'), optional: yes, hidden: no
            Name: "to", description: "The location for the new copy or copies."
        Argument: WithProperties ('prdt'), type: record ('reco'), optional: yes, hidden: no
            Name: "with properties", description: "Properties to set in the new copy or copies right away."
        Result type: <none> ('null')
            Description: <none>
    Command: exists ('core'/'doex'), hidden: no
        Implementation class: NSExistsCommand
        Name: "exists", description: "Verify that an object exists."
        Unnamed argument ('----'), type: any ('****'), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Result type: boolean ('bool')
            Description: "Did the object(s) exist?"
    Command: make ('core'/'crel'), hidden: no
        Implementation class: NSCreateCommand
        Name: "make", description: "Create a new object."
        Argument: ObjectClass ('kocl'), type: type ('type'), optional: no, hidden: no
            Name: "new", description: "The class of the new object."
        Argument: Location ('insh'), type: location specifier ('insl'), optional: yes, hidden: no
            Name: "at", description: "The location at which to insert the object."
        Argument: ObjectData ('data'), type: any ('****'), optional: yes, hidden: no
            Name: "with data", description: "The initial contents of the object."
        Argument: KeyDictionary ('prdt'), type: record ('reco'), optional: yes, hidden: no
            Name: "with properties", description: "The initial values for properties of the object."
        Result type: specifier ('obj ')
            Description: "The new object."
    Command: move ('core'/'move'), hidden: no
        Implementation class: NSMoveCommand
        Name: "move", description: "Move an object to a new location."
        Unnamed argument ('----'), type: specifier ('obj '), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: ToLocation ('insh'), type: location specifier ('insl'), optional: no, hidden: no
            Name: "to", description: "The new location for the object(s)."
        Result type: <none> ('null')
            Description: <none>
    Enumeration: save options ('savo'), hidden: no
        Enumerator: ('yes '), name: "yes", description: "Save the file.", hidden: no, value: 2036691744
        Enumerator: ('no  '), name: "no", description: "Do not save the file.", hidden: no, value: 1852776480
        Enumerator: ('ask '), name: "ask", description: "Ask the user whether or not to save the file.", hidden: no, value: 1634954016
    Enumeration: printing error handling ('enum'), hidden: no
        Enumerator: ('lwst'), name: "standard", description: "Standard PostScript error handling", hidden: no, value: 0
        Enumerator: ('lwdt'), name: "detailed", description: "print a detailed report of PostScript errors", hidden: no, value: 1
    Record type: print settings ('pset'), hidden: no
        Description: <none>
        Field: NSCopies ('lwcp'), type: integer ('long'), hidden: no
            Name: "copies", description: "the number of copies of a document to be printed"
        Field: NSMustCollate ('lwcl'), type: boolean ('bool'), hidden: no
            Name: "collating", description: "Should printed copies be collated?"
        Field: NSFirstPage ('lwfp'), type: integer ('long'), hidden: no
            Name: "starting page", description: "the first page of the document to be printed"
        Field: NSLastPage ('lwlp'), type: integer ('long'), hidden: no
            Name: "ending page", description: "the last page of the document to be printed"
        Field: NSPagesAcross ('lwla'), type: integer ('long'), hidden: no
            Name: "pages across", description: "number of logical pages laid across a physical page"
        Field: NSPagesDown ('lwld'), type: integer ('long'), hidden: no
            Name: "pages down", description: "number of logical pages laid out down a physical page"
        Field: NSPrintTime ('lwqt'), type: date ('ldt '), hidden: no
            Name: "requested print time", description: "the time at which the desktop printer should print the document"
        Field: NSDetailedErrorReporting ('lweh'), type: printing error handling ('enum'), hidden: no
            Name: "error handling", description: "how errors are handled"
        Field: NSFaxNumber ('faxn'), type: text ('ctxt'), hidden: no
            Name: "fax number", description: "for fax number"
        Field: NSPrinterName ('trpr'), type: text ('ctxt'), hidden: no
            Name: "target printer", description: "for target printer"
    Object type: application ('capp')
    Object type: document ('docu')
    Object type: window ('cwin')

Suite: Bike Suite ('BKsu'), hidden: no
    Name: "Bike Suite", description: <none>
    Class: outline item ('BKoi'), superclass: item, hidden: no
        Implementation class: Bike.OutlineItem
        Name: "outline item", plural name: "outline items", description: "An outline item"
        Attribute: id ('ID  '), type: text ('ctxt'), access: read-only, hidden: no
            Name: "id", description: "The unique and persistent identifier of the item."
        Attribute: level ('ILvl'), type: integer ('long'), access: read-write, hidden: no
            Name: "level", description: "The indentation level of this item."
        Attribute: name ('pnam'), type: text ('ctxt'), access: read-write, hidden: no
            Name: "name", description: "Plain text content of this item."
        Default subcontainer: <none>
        Primitive type: <none>
    Command: export ('BKsu'/'expt'), hidden: no
        Implementation class: NSScriptCommand
        Name: "export", description: "Export outline items."
        Unnamed argument ('----'), type: document ('docu'), optional: no
            (No user-readable name or description needed for unnamed arguments)
        Argument: ContentFormat ('fltp'), type: saveable file format ('savf'), optional: yes, hidden: no
            Name: "as", description: "The file format to export."
        Result type: text ('ctxt')
            Description: "The exported outline items."
    Enumeration: saveable file format ('savf'), hidden: no
        Enumerator: ('BKff'), name: "Bike Format", description: "Bike file format", hidden: no, value: com.hogbaysoftware.bike.xml
        Enumerator: ('OPml'), name: "OPML Format", description: "OPML file format", hidden: no, value: org.opml.opml
        Enumerator: ('PTfm'), name: "Plain Text Format", description: "Plain text file format", hidden: no, value: public.utf8-plain-text
    Object type: outline item ('BKoi')

List type: list of document ('docu')
List type: list of file ('file')
Complex type: document | list of document ('cct0')
Complex type: file | list of file ('cct1')
Complex type: list of file | specifier ('cct2')

I quick look suggests you at least need to specify a cocoa key for your document’s outline item collection.

Thanks for taking a look.

Adding a <cocoa key="outlineItems"/> didn’t seem to make a difference, but I do now think I know root cause of the problem. A proper classDescription wasn’t being set on my outline items object specifier.

My OutlineItem objects are contained by a NSDocument subclass. They have a references to this document through document variable in following code:

override var objectSpecifier: NSScriptObjectSpecifier {
    return NSUniqueIDSpecifier(
        containerClassDescription: document!.classDescription as! NSScriptClassDescription,
        containerSpecifier: document!.objectSpecifier,
        key: "outlineItems",
        uniqueID: item.id
    )
}

In the above code it turned out that document!.classDescription was always <uninitialized>. Why that is the case I have no idea, but I do know that if I change code to this:

override var objectSpecifier: NSScriptObjectSpecifier {
    let containerSpecifier = document!.objectSpecifier
    return NSUniqueIDSpecifier(
        containerClassDescription: containerSpecifier.keyClassDescription!,
        containerSpecifier: containerSpecifier,
        key: "outlineItems",
        uniqueID: item.id
    )
}

Then everything works as expected. Yeah! … until I run into the next AppleScript implementation mystery. Grrr.

1 Like