Passing Data Structures to KM on a Trigger Call via External AppleScript

I'm calling a KM Macro via external ApplesScript. As part of the call, I'm also passing a parameter—which KM drops into the Trigger variable.

But I want to pass 32 variables when I call the Macro. What's the best way to do that in my AppleScript calling code, and where would the data go in KM? Ideally, I would pass an array. (Well, most ideally would be passing a pointer to an array, but that could get messy.)

EDIT: Here is a more structured description of the issue that I posted on MIDI Translator's forum:

Thank you.

The best solution might be to pass a single variable in the shape of a single JSON dictionary object with 32 keys and values.

(You can then read the values of each each in KM by using %JSONValue% tokens in place of %Variable% tokens – See token:JSONValue [Keyboard Maestro Wiki])

(A JavaScript for Automation script would prove simpler than an AppleScript, but it can be done in AppleScript too)

2 Likes

Note that AppleScript records are prone to change the case of your key names, and that the references are case sensitive.

(JavaScript's records/objects are more flexible and relaxed):

Expand disclosure triangle to view example AppleScript
use framework "Foundation"
use scripting additions

on run
	set recVars to {Alpha:1, Beta:2, Gamma:3, Delta:"Some change or other"} & ¬
		{Epsilon:"Something tiny", lambda:"Something functional"} & ¬
		{omnicron:"smaller o", omega:"larger O"}
	
	set jsonString to my showJSON(recVars)
	
	tell application "Keyboard Maestro Engine"
		setvariable "jsonVar" to jsonString
	end tell
	
	jsonString
end run


-- showJSON :: a -> String
on showJSON(x)
	set c to class of x
	if (c is list) or (c is record) then
		set ca to current application
		set {json, e} to ca's NSJSONSerialization's dataWithJSONObject:x options:1 |error|:(reference)
		if json is missing value then
			e's localizedDescription() as text
		else
			(ca's NSString's alloc()'s initWithData:json encoding:(ca's NSUTF8StringEncoding)) as text
		end if
	else if c is date then
		"\"" & ((x - (time to GMT)) as «class isot» as string) & ".000Z" & "\""
	else if c is text then
		"\"" & x & "\""
	else if (c is integer or c is real) then
		x as text
	else if c is class then
		"null"
	else
		try
			x as text
		on error
			("«" & c as text) & "»"
		end try
	end if
end showJSON

2 Likes

That sounds brilliant - I'd add to my thread at MIDI Translator. Thank you.

1 Like

Thank you for the additional information. MIDI Translator is a wonderful application—I just wish its scripting semantics and syntax matched KB's maturity…I cannot use JavaScript in MT.

2 Likes

You mentioned arrays in another thread, and AppleScript lists are certainly less tricky than records.

The AppleScript end is much the same (pass a list to showJSON)

and at the KM end, the indexing is 1-based
( index 0 holds the length of the array)

Expand disclosure triangle to view AppleScript for Arrays
use framework "Foundation"
use scripting additions

on run
	set someList to {"alpha", "beta", "gamma", "delta", "epsilon", "zeta"}
	
	set jsonArray to my showJSON(someList)
	
	tell application "Keyboard Maestro Engine"
		setvariable "jsonArray" to jsonArray
	end tell
	
	jsonArray
end run


-- showJSON :: a -> String
on showJSON(x)
	set c to class of x
	if (c is list) or (c is record) then
		set ca to current application
		set {json, e} to ca's NSJSONSerialization's dataWithJSONObject:x options:1 |error|:(reference)
		if json is missing value then
			e's localizedDescription() as text
		else
			(ca's NSString's alloc()'s initWithData:json encoding:(ca's NSUTF8StringEncoding)) as text
		end if
	else if c is date then
		"\"" & ((x - (time to GMT)) as «class isot» as string) & ".000Z" & "\""
	else if c is text then
		"\"" & x & "\""
	else if (c is integer or c is real) then
		x as text
	else if c is class then
		"null"
	else
		try
			x as text
		on error
			("«" & c as text) & "»"
		end try
	end if
end showJSON

2 Likes

Thank you, kindly. Understood on all. My only other question (I think) is about the most efficient way to do it, as this is a realtime system operating over AppleScript. :joy: The best solution might be to allocate 32 global variables, and have the sending application write to them—and then KB read them?

The globals could be allocated in the sending application (MIDI Translator) or in KM. Or, maybe an intermediate data structure (e.g. JSON object or AS list). Whatever provides persistent data storage that (from the apps' perspectives) doesn't move around, therefore avoiding some of the penalties normally associated with interpreted languages.

You will learn by testing, but if latency really is an issue (solidity always saves more time than speed, over the course of a year) then the trick will be to minimize the number of AppleEvents, which are slow, and form a bottle-neck.

Returning a single compound object like a list or record from AS is likely to be a better bet than 32 distinct events.

1 Like

I'd follow @ComplexPoint's advice I think:

Rob has covered using JSON, so here's a quick example using delimited text. (You can change the delimiter to any character.)

set dataIn01 to "one"
set dataIn02 to "two"
set dataIn03 to "three"
set dataIn04 to "four"
set dataIn05 to "five"

set myList to {dataIn01, dataIn02, dataIn03, dataIn04, dataIn05}

set AppleScript's text item delimiters to ","

tell application "Keyboard Maestro Engine"
   setvariable "g_DataTransfer" to myList as text
end tell

Pass Data to Keyboard Maestro via AppleScript v1.00.kmmacros (7.8 KB)
Keyboard Maestro Export

-Chris

2 Likes

You make the most complete point, simple. Thank you. (I'll see myself out.)

1 Like

Thank you so much for writing out and thinking about this code. :pray:

2 Likes

Thank you for the code - nicely done!