Adding AppleScriptObjC to Existing Scripts

AppleScriptObjC opens up lots of doors for AppleScript, but it’s not without its quirks. One of them is that it can change the way file references are resolved.

Here is a script that shows the problem:

use AppleScript version "2.4" -- Yosemite (10.10) or later
-- use framework "Foundation"
use scripting additions

set filePath to (path to desktop as text) & "Test.txt"
set theText to read file filePath

The script runs fine (assuming you have a file called Test.txt on your Desktop), until you uncomment the use framework line, after which you get the error: “Can’t make current application into type file.” That’s not very helpful.

The solution is to change the code so you build file references using coercions. So, for example:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set filePath to (path to desktop as text) & "Test.txt"
set theText to read (filePath as alias)

or:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set filePath to (path to desktop as text) & "Test.txt"
set theText to read (filePath as «class furl») -- a «class furl» is a type of file

Once you know, it’s simple enough to fix.

But what if you have a long script that is well tested, but you would like to add some particular functionality that AppleScriptObjC can supply? Maybe you’re short of time. How can you avoid having to check the whole script for these issues?

One way is to put your AppleScriptObjC code in a library. This has the bonus of making it easily re-usable, too. But there is another, simpler answer: confine the AppleScriptObjC code to a script object.

Here is a simple handler using AppleScriptObjC to change the case of a string:

on makeUppercase(aString)
    set aString to current application's NSString's stringWithString:aString
    return aString's uppercaseString() as text
end makeUppercase

To add it to a script that doesn’t already use AppleScriptObjC, you will also need to add a use frame "Foundation" statement, and if it has no use scripting additions statement already, chances are you will need to add that too. From that point, AppleScriptObjC will be available everywhere in the script.

So here is our orignal script with this handler added:

use AppleScript version "2.4" -- Yosemite (10.10) or later
-- use framework "Foundation"
use scripting additions

set filePath to (path to desktop as text) & "Test.txt"
set theText to read file filePath

set theResult to makeUppercase("hello world")

on makeUppercase(aString)
    set aString to current application's NSString's stringWithString:aString
    return aString's uppercaseString() as text
end makeUppercase

The file reference works OK, but the handler fails. And if you uncomment the use framework statement to make the handler work, the file reference fails before it gets that far.

But you can confine the use of AppleScriptObjC to the handler by wrapping the handler in a script object, inside another handler. Here’s the modified code:

use AppleScript version "2.4" -- Yosemite (10.10) or later
-- use framework "Foundation"
use scripting additions

set filePath to (path to desktop as text) & "Test.txt"
set theText to read file filePath

set theResult to makeUppercase("hello world")

on makeUppercase(aString)
    script theScript
        property parent : a reference to current application
        use framework "Foundation"
        on makeUppercase(aString)
            set aString to current application's NSString's stringWithString:aString
            return aString's uppercaseString() as text
        end makeUppercase
    end script
    return theScript's makeUppercase(aString)
end makeUppercase

The file reference works, and so does the handler.

You may wonder about the parent property. You can comment that line out, and the script will still work. However, if you decide later to use AppleScriptObjC throughout the script and uncomment the original use framework statement, the handler will stop working without this parent property. Consider it future-proofing.

Using AppleScriptObjC this way does add a tiny amount of overhead, and if you are adding many handlers or they will be called repeatedly, you will be better off adding the use framework statement at the top and dealing with the issues, if any (in most scripts there won’t be). But for small changes, it’s a quick and painless approach.

One thought on “Adding AppleScriptObjC to Existing Scripts”

Comments are closed.