Keyboard Maestro supports Swift?!

So, suppose, for the sake of argument, I wanted to use JXA for KM scripting.

  1. What editor do you use? Please tell me it’s not Script Editor. Can I use BBEdit?
  2. How do I debug?
  3. What’s the standard workflow you use to edit/compile/debug/repeat as necessary?

Again, I only care right now about JXA as it relates to KM.

Thanks

with:


From Atom you can run scripts and use the Safari debugger for JSC running in app contexts like TaskPaper.app

For JSC running in the JavaScript for Automation context, the Safari debugger currently needs a copy of the code in Script Editor, but that's the only time that that I flip into Script Editor.

You could even build your own JSC context, with your own custom namespace :slight_smile:

https://developer.apple.com/library/mac/documentation/JavaScriptCore/Reference/JSContext_Ref/

http://devstreaming.apple.com/videos/wwdc/2013/615xax5xpcdns8jyhaiszkz2p/615/615.pdf

(and use the Safari debugger with it)

Wait, so I could use this JSContext in Swift to get and set variables from KM, etc?

Core JavaScript (Browser JavaScript) can NOT set KM variables -- only get them.

Dan, I know you're having fun, and that's great. Press on if that suits you.

But, being a bit of a devil's advocate, ask yourself: "Are you reinventing the wheel?"

Apple has decided that AppleScript and JXA are the primary tools for Mac automation, for interacting with all Mac apps and the OSX. From what I have seen, other Mac languages can do it, but it is more difficult.

I know where you're coming from. At this point, it's more important to be having fun at what you're doing than to always taking the most efficient route. :sunglasses:

For me, that's learning JXA.

Crap, I really am making this hard on myself, aren’t I? Sigh. I suppose I should make an effort at JXA.

OK, here goes.

Sounds interesting.

For writing/debugging JXA scripts, what is the advantage of Atom over Script Editor, since you have to go to Safari for debugging in any case?

Can you view the Scripting Dictionaries from Atom?

The reason I wish I could use Swift is that it is so close to C#, Microsoft published a PDF that compares the two languages.

I know you can't read that. If you want to see the PDF, Google "microsoft swift c#" and download the PDF. But just the fact that they can publish a side-by-side chart like this ought to show you how similar the 2 languages are.

And I ROCK at C#. It wouldn't be long before I could do the same with Swift.

Truthfully, even if I don't use it for automation, I may start learning iOS/OS/X/ development in Swift. Might be fun. Perhaps I get bored with KM. :stuck_out_tongue:

2 Likes

Cool.

Would you like a challenging project for Swift? :smile:

I would love, and I think many, many other JXA/AppleScript developers would also, to have an app where I could build a moderately complex GUI window/form, but using GUI drop/drag tools. And then script this app/form using JXA/AS.

Your app could build GUI forms using GUI, by the user, who could then use the form in a standalone mode (with some standard outputs), or the form could be scripted.

Shouldn't take you more than a week, right? For an expert C# developer! :smile:

Seriously, if this was even half decent, I think you could sell it. There is no real competition.

I worked with a guy whose standard response was “I can do that in 2 hours!” with a heavy Israeli accept. It was a crackup.

I’m done with writing complex stuff for other people. Too much pressure. Been there, done that, NFW, etc.

The wonderful thing about being a young developer is you don’t know enough to know what you’re starting is going to be WAY harder than you thought, and WAY more of a PITA than you could ever imagine.

I. Am. Not. That. Young. Developer. Any More.
:slight_smile:

Hey Dan, the "1 week" part was just a joke. Thought you'd get that. I have absolutely no idea if this is a viable project, much less how long it would take.

I was just throwing out ideas. If you're not interested, then toss it.

Core JavaScript cannot set or get Keyboard Maestro variables.

In the browser, before Keyboard Maestro runs a JavaScript via Execute a JavaScript in Safari or Google Chrome action, Keyboard Maestro sets the document.kmvar property and you can read variables that way.

In JXA, you can basically do the same things you can from AppleScript, and thus you can interact with the Keyboard Maestro Engine to read & write Keyboard Maestro variables.

In a Custom HTML Prompt, Keyboard Maestro adds functions to the environment that let you get and set Keyboard Maestro variables.

Only JXA has access to all the things you can do from AppleScript. In the browser, you have access only to what Keyboard Maestro Engine injects, and to what Safari/Chrome allow. In Custom HTML Prompt, you have access only to what Keyboard Maestro Engine injects (which in this case allows direct communication with the Keyboard Maestro Engine).

Thanks for clarifying this. I was referring to the KM Actions Execute JavaScript in <browser>, but your statement provides the clarity we need.

Same point I was making earlier, perhaps not as clearly.

Bottom line: JXA is one of the best tools to automate Mac apps, and easily get/set KM variables. Actually, as I have shown, you can even easily execute KM macros from JXA (like the HTML Prompt).

No. As Peter explained, a JSContext is just a vanilla JSC evaluation context – its not ‘browser JavaScript’, (more properly, it’s not a browser context – no Window interface object is provided in the global evaluation context), and its not an Automation context (no Automation interface object) – neither is it a command line context – its just a vanilla JSC JavaScript interpreter, and it’s up to you to extend its global context.

You could possibly arrange to shell out from it to osascript, but you can do that from Swift anyway, and osascript still requires you to talk AppleScript or JSC JavaScript to its automation interface …

So for communication with KM, and for application automation generally, all roads Swiftly lead back out of Swift …

Sorry, yes, I did get that. I thought it was funny. That's why I mentioned the buy who always said "2 hours". Ah, the trouble with written communication. You can't see the other person's face.

Then I went on just to say it wasn't going to happen, no matter how long it took. At least, that's what I was trying to say.

So no worries. I got you, you didn't get me, which is actually SOP from my end. :slight_smile:

Actually, turns out I can get and set variables from Swift.

Sort of. I guess we all forgot about our venerable old friend AppleScript. In this case, executed via the NSAppleScript class. Works just fine!

Note: I’m not having any trouble parsing NSAppleEventDescriptor. I’ll report back when I’m done.

One limitation of NSAppleScript is that it's limited to AppleScript (can't, I think, work with JS for Automation).

If you want that flexibility, you could use a shell call from Swift to osascript -l JavaScript ...

(or, I think, use OSAKit, but that looks messier)

https://developer.apple.com/library/mac/releasenotes/General/APIDiffsMacOSX10_11/Swift/OSAKit.html

Hence the name "NSAppleScript". Yeah, I figured that one out. :smile:

And yes, I looked into running JXA from Swift, and it's not up to snuff yet. Besides, all I'm doing is plugging a hole in Swift, namely, talk with other apps (most often this will be KM). Which, now that I think about it, might not be a hole at all. I'll get back on that.

And yes, I looked into running JXA from Swift, and it's not up to snuff yet

Well, OSAKit looks more tractable than I thought. Here it is (from JXA, but should be readily translatable, evaluating the same piece of code, first as AS and then as JS):

(function () {
    'use strict';

    ObjC.import('OSAKit');

    function evalOSA(strLang, strCode) {

        var strIdiom = strLang.toLowerCase()
            .indexOf('j') !== -1 ? (
                'JavaScript'
            ) : 'AppleScript',
            error = $(),
            oScript = (
                $.OSALanguage.setDefaultLanguage(
                    $.OSALanguage.languageForName(strIdiom)
                ),
                $.OSAScript.alloc.initWithSource(strCode)
            ),
            blnCompiled = oScript.compileAndReturnError(error),

            oDesc = blnCompiled ? (
                oScript.executeAndReturnError(error)
            ) : undefined;

        return oDesc ? (

            [oDesc.stringValue.js]

        ) : error.js.NSLocalizedDescription.js;

    }

    return ['JS', 'AS']
        .map(function (strLang) {
            return evalOSA(strLang, '2 + 2');
        });
})();