I have a very basic Cocoa application written in Xcode, and I'm trying to access a property of my application delegate with a simple AppleScript script:
tell application "HelloWorld"
set appDelegateProperty to property1
end tell
The information in the Cocoa Scripting Guide seems pretty straight forward. While it seems like the code I've written is key-value coding (KVC) compliant, I am seeing errors that indicate otherwise.
I've added a synthesized property of type NSString
named property1
to my AppDelegate
class, setting it to @"test"
in the applicationDidFinishLaunching()
method. And I added entries to my scripting definition (SDEF) file to allow access to the property from AppleScript.
Here's the relevant code (you can download a zipped copy, or browse the source, of the Xcode project on this web page):
Info.plist
I added these keys to enable AppleScript support in the application:
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>OSAScriptingDefinition</key>
<string>HelloWorld.sdef</string>
AppDelegate.h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property NSString* property1;
@end
AppDelegate.m
#import "AppDelegate.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
@synthesize property1;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
property1 = @"test";
}
@end
HelloWorld.sdef
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="HelloWorld">
<suite name="HelloWorld" code="HELO" description="HelloWorld scripting suite">
<class name="application" id="HELO" code="capp" description="top-level scripting object">
<cocoa class="NSApplication"/>
<property name="property1" code="Hadp" description="property1" type="text" access="r">
<cocoa key="property1"/>
</property>
</class>
</suite>
</dictionary>
When I start the application in the Xcode debugger and execute this AppleScript code in Script Editor, here is the error I see in the Script Editor console:
Script Editor Console Output
error "HelloWorld got an error: AppleEvent handler failed." number -10000
Xcode Debugger Console Output
2016-07-01 15:29:18.449 HelloWorld[90280:3166995] Command: Intrinsics.get
Direct Parameter: <NSPropertySpecifier: property1>
Receivers: <NSPropertySpecifier: property1>
Arguments: {
}
2016-07-01 15:29:18.449 HelloWorld[90280:3166995] An exception was thrown during execution of an NSScriptCommand...
2016-07-01 15:29:18.449 HelloWorld[90280:3166995] [<NSApplication 0x620000101560> valueForUndefinedKey:]: this class is not key value coding-compliant for the key property1.
2016-07-01 15:29:18.449 HelloWorld[90280:3166995] Result: (null)
2016-07-01 15:29:18.449 HelloWorld[90280:3166995] Error: -10000 "(null)"
Troubleshooting Notes
Somehow, my code is not in fact KVC compliant, though I am unsure how or why.
I suspect there may be an issue with the fact that the property is in the application delegate rather than the NSApplication
class itself, but I could be wrong.
I've set the bindings debug log level as shown in the "Troubleshooting Cocoa Bindings" section of the Cocoa Bindings Programming Topics documentation to troubleshoot the bindings:
defaults write net.none.HelloWorld NSBindingDebugLogLevel 1
I've also set the scripting debug log level to 1 as shown in the "Turn On Debugging Output for Scripting" section of the Cocoa Scripting Guide to see more detailed debug output like so:
defaults write net.none.HelloWorld NSScriptingDebugLogLevel 1
Finally, I've followed instructions in the "Examining Scriptability Information in Your Application" section of the Cocoa Scripting Guide to output the descriptions of scripting information Cocoa pulls from the SDEF. Here's that debugger console command and its output:
po [NSClassFromString(@"NSScriptSuiteRegistry") sharedScriptSuiteRegistry]
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: HelloWorld ('HELO'), hidden: no
Name: "HelloWorld", description: "HelloWorld scripting suite"
Class: application ('capp'), superclass: item, hidden: no
Implementation class: NSApplication
Name: "application", plural name: "applications", description: "top-level scripting object"
Attribute: property1 ('Hadp'), type: text ('ctxt'), access: read-only, hidden: no
Name: "property1", description: "property1"
Default subcontainer: <none>
Primitive type: <none>
Object type: application ('capp')
Any ideas?
If the key is in AppDelegate
you have to implement
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
{
return [key isEqualToString:@"property1"];
}
For multiple keys declare a NSSet
property keySet
containing all keys and return
return [keySet containsObject:key];
You don't need to specify the cocoa key
property1
in the sdef file since the name match the selector.