Can't get Apple SimpleScripting example to work when using NSApplicationDelegate
I am trying to write a trivial Objective-C application: I just want the application to display a word in the status bar and allow the word to be updated via AppleScript. To be honest, I don't know much about AppleScript and nothing about Objective-C. But it can't be that hard, because it only took me 2 hours to get a menu bar that has menu items and responds to basic AppleScript commands like "quit", so I'm 95% as good as it is there is. Unfortunately, I spent the next 6 hours figuring out a way to provide this application with a simple property that I can get and set via AppleScript.
Here is my .h code:
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
}
@property (assign) IBOutlet NSWindow *window;
- (NSString*) foobaz;
@end
The method foobaz
is the one I used to try and make a readable property ( *window
is part of the template I gave to Xcode and was not relevant to the problem). Here is my .sdef file:
<?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">
<xi:include
href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef"
xpointer="xpointer(/dictionary/suite)"/>
<suite name="StatusMenuApp suite" code="smas"
description="StatusMenuApp specific classes.">
<class name="application" code="smaa" inhereits="application"
description="Application object.">
<cocoa class="AppDelegate"/>
<property name="foobaz" code="smas" type="text" access="r"/>
</class>
</suite>
</dictionary>
So, I set it up the same as Apple's SimpleScripting example: https://developer.apple.com/library/mac/#samplecode/SimpleScripting/Introduction/Intro.html
However, I tried to run this in a script editor:
tell application "StautsMenuApp"
properties
end tell
And I just get a list of the main properties without being mentioned foobaz
, unlike Apple's example where the custom property appears next to the main properties when it is run.
I feel like I've tried a hundred variations and read a hundred examples, but can't get anything to work. Any help is appreciated.
source to share
There are many problems with your SDEF file.
-
While this is not a major issue, when defining your own "types" (
OSType
codes), all lowercase letters are historically reserved for Apple. So, you should be able to do something like'Smas'
or'Smas'
, but not'Smas'
. -
It might have been a typo in the web browser, but you got it wrong
inhereits
on the line<class name="application" code="smaa" inhereits="application"...
. It is important that the spelling is correct so that you can "extend" the base AppleScript classapplication
with your own custom properties. This may be the reason why you only see the default application properties in the AppleScript editor. (While just fixing the spelling might work, there are other fixes you should make.) -
You are trying to point out that the Cocoa class is for your extended
application
AppleScript classAppDelegate
, but this is problematic because itAppDelegate
does not inherit fromNSApplication
as shown in the Apple.sdef example, rather it inherits from plain oldNSObject
.
Before moving on to how I will rewrite SDEF, I will consider that the basic idea is how the Apple SDEF example works to add custom properties application
to the default AppleScript class . The AppleScript application
class corresponds to the NSApplication
Cocoa class . When you launch an application, OS X creates an instance NSApplication
(or a subclass of it, as specified in the NSPrincipalClass
application's Info.plist entry ). This instance manages the application. There are two possible ways in Cocoa to add additional AppleScript properties to an existing class NSApplication
: 1) create a custom subclass NSApplication
that contains additional properties, or 2) use the Objective-C category to extend the existing classNSApplication
as-is. (In Cocoa, the Objective-C categories are a way to add additional functionality to an existing class without the potential complexity that subclasses introduce.) Apple's example SimpleScripting
uses the latter approach (Objective-C categories) to add additional functionality.
Below I will discuss how to rewrite SDEF.
SimpleScriptingAppDelegate.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">
<xi:include
href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef"
xpointer="xpointer(/dictionary/suite)"/>
<suite name="StatusMenuApp suite" code="SSAD"
description="StatusMenuApp-specific classes.">
<class name="application" code="capp" inherits="application"
description="Application object.">
<cocoa class="NSApplication"/>
<property name="foobaz" code="Foob"
description="Description of the foobaz property." type="text">
<cocoa key="foobaz"/>
</property>
</class>
</suite>
</dictionary>
You will notice that I indicated that I am creating my own AppleScript class application
that inherits from the standard AppleScript class application
. I pointed out that the Cocoa class for this custom application
is NSApplication
. I added a custom AppleScript property whose name foobaz
and type text
. (The AppleScript class text
will correspond to the Cocoa class NSString
). Also important is that I have specified that the Cocoa key is to access this custom property foobaz
. This Cocoa key specifies the name of the Objective-C method that will be used to provide the property value. The configuration defined above indicates that a custom property foobaz
can be retrieved by calling a method foobaz
on a shared object instance NSApplication
.
To implement this on the Cocoa side, I defined the category (MDScriptingAdditions)
on NSApplication
as shown below:
MDNSApplicationScriptingAdditions.h:
@interface NSApplication (MDScriptingAdditions)
- (NSString *)foobaz;
- (void)setFoobaz:(NSString *)aFoobaz;
@end
MDNSApplicationScriptingAdditions.m:
#import "MDNSApplicationScriptingAdditions.h"
#import "AppDelegate.h"
@implementation NSApplication (MDScriptingAdditions)
- (NSString *)foobaz {
return [(AppDelegate *)[NSApp delegate] foobaz];
}
- (void)setFoobaz:(NSString *)aFoobaz {
return [(AppDelegate *)[NSApp delegate] setFoobaz:aFoobaz];
}
@end
You can see that I am basically redirecting the processing of these methods to the class AppDelegate
you would like to customize.
AppDelegate.h:
@interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSWindow *window;
IBOutlet NSTextField *foobazTextField;
NSString *foobaz;
}
- (NSString *)foobaz;
- (void)setFoobaz:(NSString *)aFoobaz;
@end
This setting allows you to get and set a custom property foobaz
via AppleScript. When you set a value, the value displayed in the text box foobaz:
will change to the value shown in the figure below:
Sample project: SimpleScriptingAppDelegate.zip
source to share
The only thing I can see is the problem in your sdef. The code for this line should be "capp". This standard and it will be the same in any sdef file. It is defined by Apple, so you need it.
<class name="application" code="smaa" inhereits="application"
description="Application object.">
After that, you will have access to this method like this ...
tell application "StautsMenuApp" to foobaz
source to share