Solution to the problem of using the AppleScript Foundation framework in Smart Rules

An inability to use the AppleScript Foundation framework for scripts in DEVONthink Smart Rules has been discussed several times over the years: scripts with import statements such as this,

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

fail with obscure errors or simply don’t run at all when used in Smart Rules, but not when used outside of Smart Rules. Some examples of past postings in this forum where the topic came up include the following:

The only solution has seemed to be to move the code into a separate script library, and then make the Smart Rule script import it, making sure that the code used in the Smart Rule portion does not itself import the Foundation framework. Some past postings where this was mentioned are:

(My apologies if I missed some other relevant postings.) Recently, I stumbled headlong into this problem after having spent far too long writing some AppleScript code [1] that worked fine both in an external debugger and as a standalone program, and yet failed with a mysterious error -1708 when invoked from a Smart Rule. It was the first time I tried to use AppleScriptObjC with AppleScript, and in my naïveté, assumed it would just work. My balding head had too few hairs left to pull out in frustration, so with some degree of desperation, I spent even more time [2] searching for a way. And found one!

The basis of the approach is explained in a 2018 blog posting [3] by Shane Stanley (of Script Debugger fame) – I want to be clear it is not my invention. The pattern turns out to be simple. If you have a handler named HANDLERNAME where you need to use the Foundation framework, wrap it inside a script object inside another handler, like this:

use AppleScript version "2.5"
use scripting additions

on HANDLERNAME(ARGUMENTS...)
    script wrapperScript
        property ca: a reference to current application
        use framework "Foundation"
        on HANDLERNAME(ARGUMENTS...)
            ... your code that uses AppleScriptObjC ...
            return RESULT
        end trim
    end script
    return wrapperScript's HANDLERNAME(ARGUMENTS...)
end HANDLERNAME

Here is an actual example of something I’m using successfully in a larger script invoked from a Smart Rule in DEVONthink version 3.9.4:

-- Remove leading & trailing whitespace from the text and return the result.
on trim(raw_text)
    script wrapperScript
        property ca: a reference to current application
        use framework "Foundation"
        on trim(raw_text)
            set str to ca's NSString's stringWithString:raw_text
            set whsp to ca's NSCharacterSet's whitespaceAndNewlineCharacterSet()
            return (str's stringByTrimmingCharactersInSet:whsp) as text
        end trim
    end script
    return wrapperScript's trim(raw_text)
end trim

The code above is not something for which you necessarily need the Foundation framework – you could surely implement it in plain AppleScript – but because it’s short, it’s useful as a example here. More important is the possibility offered by this general approach for using classes like NSURLRequest, NSJSONSerialization, etc, in your code. It does not necessarily represent an argument for using AppleScript instead of JavaScript and JXA, but if you need or want to use AppleScript, being able to access those classes can be a huge benefit.

Anyway, I wanted to share this, in the hopes that it will help other people.

[1] All the while, in the back of my mind, I could hear a certain person from this forum saying I should be using JavaScript. And it’s true; I really should have.

[2] This was plainly an example of the sunk cost fallacy at work.

[3] An archived copy is available in the Internet Archive’s Wayback Machine.

4 Likes

Can’t imagine whom you’re talking about :wink:

Joking aside: with all its shortcomings, the integration of JXA with the ObjC frameworks works surprisingly well. Most of the time.

1 Like