AppleEvents (AE/IAC) - any experience out there?

I just read in this article about AppleEvents, but cannot find any examples etc.

How to use AppleScript in macOS

It says:

At an even lower level is another Apple technology called AppleEvents (AE). AppleEvents are an Inter-Application Communication (IAC) facility which allows apps and processes to communicate with each other.

AppleEvents usually have a four-character code for the class, or suite of events, then another four-character code for the event type itself. By sending and receiving AppleEvents, macOS apps can trigger each other to perform certain actions.

AppleScript is based on AppleEvents and uses the IAC mechanism to transport messages and data around the system.

I have two scripts which open two separate Numbers documents and coordinate activities. Presently I am using a table in a SQLite database to store semaphores and messages shared between the two scripts to control actions. Basically one of the scripts runs in the background with idle loops (using delay commands) but it would seem far more efficient if there was direct interprocess communication or a way to have the background script simply sit idle (no delay timed loops) until “woken” up by the control script.

I tried to find an AppleEvents dictionary but no luck only System Events, very cryptic.

Any ideas?

Try this. Put a handler like this in your applet with the idle handler

on WakeUpAndDoYourThing(stuff)
   tell me
      activate
      
      display dialog stuff
      
   end tell
end WakeUpAndDoYourThing

Then put this in the calling script:

tell application "Responder"
   WakeUpAndDoYourThing("worked")
end tell

(In the example I’m using “Responder” as the name of the applet. AppleScript should be able to find the applet (unless there are others with the same name running). You can also use the path rather than the app.

1 Like

As far as AppleEvents go, that’s really the under the hood stuff that makes AppleScript work. Basically an appleScript command and its parameters or arguments are translated into the appleEvent code structure and that’s what’s sent back and forth between apps. Even something simple like sending a handler call from one app to another uses apple events, but it happens invisibly.

1 Like

Perfect. Can’t wait to try this. Appreciate when an expert takes the time to reply, thank you.

Question - Outside the Wake handler, what command can Responder use to “wait”? delay forever?

1 Like

Relative novice here with AppleScript so I might be misunderstanding some architectural aspects.

My background script might not technically qualify as an applet or stay-open app. I need it running only while my other script, the interactive control script, runs separately. So maybe an hour or so, then the background script will terminate. So maybe an “idle handler” is not appropriate.

Let’s call the interactive control script App A, and the background script App B.

App B presently opens a Numbers document with its window on display to a group of users, puts data into ranges of cells, then sits in a small repeat loop with a delay, checking a semaphore (in an SQLite database table) at regular intervals until that status changes to cause an exit from its delay loop, refreshes the Numbers table/window then re-enters the delay loop. And so on.

So a small repeat loop with delay and few operations nested inside a large repeat loop with much more extensive operations.

The control semaphore is set by App A under certain circumstances, and at repeated but irregular intervals.

The inner loop is set to delay 1.5 seconds because I need the script to respond quickly and refresh the window when the proper conditions are set. But that eats up CPU so I’d much prefer the script just sit and do nothing until awakened.

I’ll try to put together some sample code.

Idle handlers only work in Applets saved as Stay Open. So in your scenario, you would save your script as a Stay Open Applet. When you launch it after it executes the run hander it will go to the idle handler. But while it’s open you can call its other handlers.

I am completely unfamiliar with this stay-open architecture, so here is the essence of my App B, background app which I describe above. How in this script would I implement a “run handler” and “idle handler”? How are they invoked? Where do I put the “wake handler”?

— This is so called App B, the background app.
— This script opens a Numbers document with a table displayed on a screen
— viewed by a group of users. The table contains a full matrix of rows and
— columns which are periodically refreshed with data. Might take anywhere from 
— 30-45 minutes to several hours at most before the control App A stops and App B stops also.
— There is an external control semaphore with binary status of STOP or GO.
— This semaphore is in the “Status” table in an SQLite database

use sqlLib : script “SQLite Lib2” version “1.1.0”
use scripting additions

set NUMfile to “DisplayTable.numbers”
set DBfile to “ControlDB.db”

set theDB to open db in file NUMfile

tell application “Numbers”
set theDoc to open alias NUMfile
activate

— Lots of processing to initialize the table data.

repeat until TableIsFull

— More processing to fill the table, row by row, column by column
— depending on what is fed by control App A. 

— Get control status (predominantly STOP)
set {{theState}} to query db theDB sql string “Select State from Status”

— Current idle loop
repeat while (theState = “STOP”) -- Exit when "GO"
delay 1.5
set {{theState}} to query db theDB sql string “Select State from Status”
— Refresh message row in display table.

end repeat — while STOP

end repeat — until TableIsFull

In case you haven’t seen this yet…

Here is how I would approach it.

Copy the script below into a new script and save it as an AppleScript application, and make sure the “Stay Open after Run Handler” box is checked.

Launch the application and it should execute your run handler (with all the calls to the SQL data base and Numbers added). When the runhandler stops the idle handler will execute, calling the handler doing the work. When the idle handler reaches its return statement, the application will pause for the number of seconds specified, then the idle handler will run again, and keep going until the quit command is issued.

use script "SQLite Lib2" version "1.1.2"
use scripting additions


-- This is so called App B, the background app.
-- This script opens a Numbers document with a table displayed on a screen
-- viewed by a group of users. The table contains a full matrix of rows and
-- columns which are periodically refreshed with data. Might take anywhere from 
-- 30-45 minutes to several hours at most before the control App A stops and App B stops also.
-- There is an external control semaphore with binary status of STOP or GO.
-- This semaphore is in the "Status" table in an SQLite database
on idle
   set {{theState}} to query db theDb sql string "Select State from Status"
   if theState = "STOP" then tell me to quit
   my ProcessingToFillTheTable()
   --when idler handler completes script pauses for 1.5 seconds (or whatever return value is specified)
   return 1.5
end idle

on run
   set NUMfile to "DisplayTable.numbers"
   set DBfile to "ControlDB.db"
   
   set theDb to open db in file NUMfile
   
   tell application "Numbers"
      set theDoc to open alias NUMfile
      activate
      
      -- Lots of processing to initialize the table data.
      
   end tell
end run
--When run handler stops idle handler executes

--This handler can be called from another script
on ProcessingToFillTheTable()
   -- More processing to fill the table, row by row, column by column
   -- depending on what is fed by control App A. 
end ProcessingToFillTheTable

on quit
   continue quit--exits the stay open applet
end quit


More than I could ask for, thank you very much estockly and Mockman. I am traveling and will pick up on all this toward the end of the week.

Thank you. Yes I had already studied this but still unfamiliar how to apply.

I will ponder this on the plane. The light came on and I believe App B as written can easily be transformed to stay-open architecture. Key seems to simply replace repeat loops with handlers.

A few questions for clarification:

The run handler is invoked and executed only once, sort of like an initialization handler, only when the script app is activated - correct?

In your example, ProcessingToFillTheTable can access “theDoc” and “theDB” variables opened by the run handler - correct?

Back to your first example 3 days ago, it would seem the WakeUpAndDoYourThing handler might not be needed or applicable - correct?

When a handler (in App B) is called from another script (App A), that interrupts whatever App B is doing in its idle handler, so in other words it asynchronously jumps out of the idle handler, executes the called handler, then returns to wherever it was in the idle handler - correct?

In other words, and back to my original question, it would seem in order to limit CPU cycles, I might want the idle handler to basically do nothing, like this:

set LongTime to 60 * 60 -- Sit idle for as long as 1 hour (not likely)

on idle
return LongTime -- Just sit tight until there is something to do (i.e. another handler is called)
end idle

I can design a variety of handlers to perform the activities I want when called from App A, anything from as simple as writing data to a single cell, to writing rows and columns of data to large arrays of cells - correct?

This would eliminate my need for the so-called Status table in my SQL database as I could simply send these State variables (plus other limited data) directly from App A to App B.

As a side note, my SQL database will contain thousands of rows of data in a few core tables which are central to both apps A and B.

I don’t believe the idle handler will be asynchronously jumped out of. It will let any handler that is currently running complete before it then runs the handler you called.

Just took a stab at all this. Error right out of the gate cannot open db blah blah. But of more concern, is how does one debug these stay-open scripts??

This occurs when I save the script

And this at run time
Error

More info, it’s an ‘open db’ which works fine when not stay-open

But at least my quit handler works.

What is the difference between Application (Apple) and Application (Enhanced) format?

I’m glad to see you’re using Script Debugger.

This forum is run by Late Night Software which is the Script Debugger developer and they have a forum that is dedicated to Script Debugger users here:

To help diagnose it may be useful to try to save it from Apple’s Script Editor and see if you get the same result.

I am not familiar with those error messages, but I suspect they are related.

The failure to open the database is related to SQLite Lib2, and that is developed by Shane Stanley who is active on both forums, but mostly the Script Debugger forum, and can probably help.

The Script Debugger help documentation fully explains what the Enhanced app format is and its additional functionality. From what I can tell you don’t really need that here. (In my opinion the Script Debugger help is the best written documentation of any app on the mac and the best implementation of Apple’s help system)

As for how to debug a stay open app, in Script Debugger there is a menu option for executing the idle handler of the open script.

Script>Execute>Idle

All very helpful thanks.
I moved the SQL DB to my Databases folder and somehow that solved the open problem.
Also took a few runs of the script to perhaps close already open channels to the DB and now the script actually works.
The Cannot Sign Document error still persists but I will take that to the Script Debugger forum, thanks.
Step at a time!

Cannot Sign Document error solved by moving the app to my Scripts folder, FYI.

1 Like

I managed to build this app and it is fully working. I can open it from a control app, send it commands, and quit it.

Yes. In fact I have the typical run, idle and quit handlers, plus several other handlers which perform a variety of activities.

It’s all working like a charm. Thank you.

1 Like