1

Adding scriptability to my Mac app, I wonder if I can invoke handlers on the script's end from my app. If so, how does that work?

As I understand it, handlers are like functions (as in "on run") that can be called by events coming from outside of the script's own code. And the Sdef file understands the event tag, seeing that I can enter events using the Sdef Editor. But I cannot find any documentation on this in the Cocoa Scripting Guide.

My app records the clipboard, so I wonder if I could let running scripts know that a new clipboard has been recorded so that the script can operate on it.

Now, instead of invoking separate (stand-alone) scripts that I locate and load and run for such an event, I would rather like it if the user could have a script run constantly that declares an event that my app then invokes. (Whether that's really a good idea should not be discussed here, I'm just using this as an example for understanding AppleScript events.)

Is that possible? If not, what are the event entries in the sdef meant for?

Update: Here's an intro on handlers in AppleScript: MacScripter: Getting Started with Handlers

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149

1 Answers1

1

The difficulty to implement AppleScript Event Handlers is that the application must have a permanent reference to the script.

For example the scripts to handle event handlers in Finder, Messages, Mail must be registered in the target application to keep the reference to the script.

When the script implements one of the provided event handlers, the target application creates an NSAppleEventDescriptor with

initWithEventClass:kHandlerEventClass 
           eventID:kEventID
  targetDescriptor:kEventTargetDescriptor 
          returnID:kAutoGenerateReturnID // predefined constant in CarbonCore.h
     transactionID:kAnyTransactionID]; // predefined constant in CarbonCore.h

including the sub-descriptors for the parameters, and send it via executeAppleEvent on the target script reference.

  • kEventID is the least 4 significant bits of the 8 char code in the sdef file ('EfgH').
  • kHandlerEventClass are the most 4 significant bits of the 8 char code in the sdef file ('abcD').
  • kEventTargetDescriptor is an NSAppleEventDescriptor representing the client or target application as return address .

The event handler works similar to a command, here an very basic example

<event name="did appear something" code="abcDEfgH" description="This handler is called when something appears.">
     <direct-parameter description="The names of the appeared something.">
        <type type="text" list="yes"/>
     </direct-parameter>
     <parameter name="with result" code="smTS" description="A record of some information about the names" type="something reply"/>
</event>

in AppleScript the handler is implemented

on did appear something theNames with result theResult
  • theNames is a list of text
  • theResult is a custom record type something reply
vadian
  • 274,689
  • 30
  • 353
  • 361
  • I had already been able to invoke handlers of a script I'm loading via NSAppleScript, by using hard-coded handler names. I guess this is the cleaner version of it. I had a look at a script for Messages that uses such events, and I think I can pull it all together now. I guess lots of this knowledge still comes from the pre-Cocoa Scripting APIs and Inside Mac, which is nowadays hard to find, with Apple having removed all the old docs from their servers. As if they're not useful any more :) – Thomas Tempelmann Apr 21 '16 at 17:05
  • So, how do you suggest I manage the connection between the script and my app? I suppose my app has to choose (fixed name?) and load the script itself, then run it with its default handler ("on run") to get it up and ready? Should the app also monitor the mod date stamp to reload it when it changes? Or is that bad because the script may be storing its properties in there, causing problems when reloading it that way?Or is there a way where the script gets invoked by the user and the app recognizes the script and keeps connected to it? – Thomas Tempelmann Apr 21 '16 at 17:10
  • Running scripts via `NSAppleScript` and sending Apple Events to handlers defined in the sdef file are two completely different things. It depends on whether the target application needs to notify the script about a particular occurrence of something directly (unidirectional) or to use a script like a library (bidirectional). There is a third way to use `NSAppleScriptTask` with scripts located in the designated `Application Scripts` folder. – vadian Apr 22 '16 at 07:28
  • 1
    I can't make sense of your answer. I know about NSAppleScript and NSAppleScriptTask. I use them both. The latter always runs the default handler (on run), whereas the former lets me invoke other handlers via executeAppleEvent. That's all clear. That's not what I meant to ask, though. Sorry for being unclear but I don't know if you misunderstood me or if I misunderstand you. Your first 2 sentences in your answer make it sound more complicated, hence my follow-up questions. If I only have to let the user choose a script file and I load it with NSAppleScript - then that's easy. – Thomas Tempelmann Apr 22 '16 at 12:14
  • I just answered the question how to use AppleEvents defined in the sdef file. `NSAppleScript` is something else: It's for simply running scripts (from literal string source or loaded from disk). `NSUserAppleScriptTask` (I forgot `User`) is very similar to `NSAppleScript` – the big difference is that you can't compile and run scripts from a literal string, on the other hand you are able to run scripts in a sandboxed app without restrictions and particular Apple Events entitlements. – vadian Apr 22 '16 at 14:20