AppleScript framework: architecture proposal
AppleScript framework: architecture proposal
- Subject: AppleScript framework: architecture proposal
- From: Eric Peden <email@hidden>
- Date: Sat, 17 Jul 2004 01:14:18 -0500
For awhile now I've been wanting to write a little Cocoa app to automate
some iTunes tasks for me (I know, there are a ton of these things
already out there; stick with me, this gets more interesting). Doing so
clearly involves sending Apple Events to iTunes, and after some research
into the topic, I've decided I don't want to mess with raw Apple Events.
I can crank out AppleScript routines to do the actual controlling in
just a few minutes; surely there's a way to leverage them from within a
Cocoa app.
I found some sample code from Apple called "EmbeddedAppleScripts" that
calls pre-compiled AppleScript routines using Carbon. Unfortunately, the
interface it exposes, while easier to use than generating raw Apple
Events, isn't much prettier. It seems to me that an Objective-C wrapper
around this would make it much more useful to Cocoa developers. One
could build a small library of AppleScript routines for interapplication
communication, and access them through a friendly API.
I've come up with the following API proposal for such a framework.
* class ASEmbeddedAppleScript
- (id)initWithCompiledScriptNamed:(NSString *)scriptName;
Loads the pre-compiled script. Default would grab it from the main
bundle; init(...) variants would look for script in other bundles, via a
file path, using a URL, whatever.
- (void)registerSubroutineNamed:(NSString *)subroutineName
withParameters:(ASParameterDescriptionList *)parameters
returning:(ASReturnTypeDescription *)returnDescription;
The sample code provided by Apple requires writing little glue functions
for every unique AppleScript routine signature. E.g., parameterless
routines that return no value can be handled by a single glue function,
but a routine that takes a string and a routine that takes an alias
require two additional glue functions. I don't want to write glue code;
I want Objective-C to do it for me.
The registerSubroutineNamed: method would add its parameters to an
internal table and in effect "create" a glue method for the associated
AppleScript routine. This would be done through a fairly involved
forwardInvocation: method in the ASEmbeddedAppleScript class. Once all
of the AppleScript handlers are registered, the pre-compiled AppleScript
can be called as if it was implemented as an Objective-C class.
- (id)executeSubroutineNamed:(NSString *)name
otherParameters:(???)foo, ...;
There should also be a means of executing a subroutine directly, without
having to register it and let forwardInvocation: take care of the actual
method call. Direct execution would let a class provide prototypes for
the AppleScript handlers, facilitating compile-time parameter checking.
* class ASParameterDescriptionList
- (void)addParameterNamed:(NSString *)name
ofType:(NSString *)type;
Adds a parameter to the list. The type is something like "string," or
"alias," or "list," and describes the type of the parameter as it
appears to the AppleScript. Internally, the framework knows how to
convert from the AppleScript type to an associated Cocoa type. The
built-in types will be provided by the framework ("string" to NSString,
"list" to NSArray, etc.), and some mechanism should be provided to let
users add their own conversions (more on this later). The usage examples
later on should make this clearer.
* class ASReturnTypeDescription
- (id)initWithType:(NSString *)type;
Analogous to the ASParameterDescriptionList, but since only one object
can be returned, there's no need to keep a list.
* Examples
Let's say we have this script:
on sayHi(prompt, button_names)
display dialog prompt with buttons button_names
return the result
end sayHi
Setting up an embedded script might look something like this:
/*-------------------------*/
ASEmbeddedAppleScript myScript = [[ASEmbeddedAppleScript alloc]
initWithCompiledScriptNamed:@"my_compiled_script"];
ASParameterDescriptionList sayHiParams =
[[ASParameterDescriptionList alloc] init];
[sayHiParams addParameterNamed:@"" // name doesn't matter; first parameter
ofType:@"string"];
[sayHiParams addParameterNamed:@"buttonNames"
ofType:@"list"];
ASReturnTypeDescription sayHiReturn = [[ASReturnTypeDescription alloc]
initWithType:@"string"];
[myScript registerSubroutineNamed:@"sayHiWithPromp"
withParameters:sayHiParams
returning:sayHiReturn];
/*-------------------------*/
Then, executing the sayHi subroutine would go like this:
NSArray *buttonNames = [NSArray arrayWithObjects:@"OK", @"Cancel", nil];
NSString *buttonClicked;
buttonClicked = [myScript sayHiWithPrompt:@"Hello, World!"
buttonNames:buttonNames];
Easy, right?
Any comments on this API? Am I missing a much more elegant way of doing
this? Is there already something like this out there? One thing I'm
particularly worried about is extensibility. There needs to be an easy
way to add custom AppleScript->Cocoa object conversions, and I'm not
sure what the best way to register these would be. There's also the
issue of error handling: Apple's code returns an OSStatus, which the
caller must then check to verify the subroutine executed properly.
What's the "Cocoa way" of handling this? Returning "nil" on an error?
Feedback is welcome. This little project may never actually see the
light of day, but if I actually get it coded I'd like to provide it to
the community. And if I don't, perhaps someone else will pick up the
torch.
--
eric
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.