How to retain globals in AppleScript between invocations under Mojave

applescript

Problem

Apparently with the security changes made in Mojave and now going forward, I lost the ability to store globals between invocations of my scripts.

All that I want to do is to toggle states of connections based on my location (e.g. work versus home toggles ethernet versus WiFi and other stuff). I want to store the state for the next time so that I can know what the setting had been.

I don't do XCode development (I do however program in other languages such as python). Everything for AppleScript is in ScriptEditor (perhaps soon Script Debugger instead). Finally, I am conversant enough to know how to write the scripts and save them as applications. Anything else that I need to use would hopefully be at a "plug-and-play" level of effort.

Example Script

Here is an example of a script that I use to toggle the status between presentations and no presentations. In this, pCurrentMode is no longer being maintained between invocations. Under High Sierra, it ran with no problems. Under Mojave, it does not remember the previous invocation state. I have a few other equivalent scripts that do the same. They are saved as .app files in the Scripts folder (for menu access whenever I need them).

property pAppsToToggleOFF : {"Mail", "Safari"}
property pAppsToToggleinMSB : {"EasyMP Network Projection"}
property pAppsToToggleinTeach : {"AirServer", "Panopto"}

property blueutil : "/usr/local/bin/blueutil"

property pCurrentMode : "Off"

on run {}

-- ask how to set
set whichMode to choose from list {"Teach", "MSB Show", "Off"} with prompt "How?" default items pCurrentMode
if result is false then return

if (whichMode is equal to pCurrentMode) then return

set pCurrentMode to whichMode as text

set inTeachMode to (whichMode as text is equal to "Teach")
set inMSBMode to (whichMode as text is equal to "MSB Show")
set inONMode to (inTeachMode or inMSBMode)
set inOFFMode to (whichMode as text is equal to "Off")

if (inONMode) then

    -- start presentation mode

    if (inTeachMode) then
        repeat with apptoToggle in pAppsToToggleinTeach
            try
                tell application apptoToggle to activate
            end try
        end repeat
    else if (inMSBMode) then
        repeat with apptoToggle in pAppsToToggleinMSB
            try
                tell application apptoToggle to activate
            end try
        end repeat
    end if

    repeat with apptoToggle in pAppsToToggleOFF
        try
            tell application apptoToggle to quit
        end try
    end repeat

    -- switch to WiFi and turn off bluetooth

    do shell script "scselect WiFi"
    do shell script blueutil & " off"

else if (inOFFMode) then

    -- end presentation modes

    repeat with apptoToggle in pAppsToToggleinTeach
        try
            tell application apptoToggle to quit
        end try
    end repeat

    repeat with apptoToggle in pAppsToToggleinMSB
        try
            tell application apptoToggle to quit
        end try
    end repeat

    -- switch to ethernet and turn on bluetooth

    do shell script "scselect Ethernet"
    do shell script blueutil & " on"

end if

-- show or hide the dock and desktop icons

hidetheDock(inONMode)
showDesktopIcons(not inONMode)

end run

on showDesktopIcons(state)

    set theCmd to "defaults write com.apple.finder CreateDesktop " & state & "&& killall Finder"

    do shell script theCmd

end showDesktopIcons

on hidetheDock(state)

    tell application "System Events" to set the autohide of dock preferences to state

end hidetheDock

A Concluding General Question

By way of reference, when I started looking for insights on how to solve this problem, as a beginner/intermediate user of AppleScript, I was lost about where I should go for the most recent information to figure out why this was now happening. This prompts me to ask a follow up based on a question from two years ago posted at this link.

AppleScript alternatives

In a nutshell, where do programmers go to find resources about the newest practices to write robust scripts with AppleScript?

I stumbled across the site for Script Debugger while subsequently looking for "applescript globals". The site offered some insights. I would be hard pressed to read all of the forum posts to cull the information that I need laid out in a tutorial + example manner. But if this is the current practice, so be it.

Best Answer

Saving Application Preferences

Since a regular AppleScript saves properties and globals in the script file, code signing or making script(s) read-only prevents problems with privacy accessibility settings due to applications being modified, but this prevents the historical use of persistent properties.

A solution would be to use NSUserDefaults (which is the way regular applications handle preferences), either directly using AppleScriptObjC or via something like the PrefsStorageLib library. Either one will save items in ~/Library/Preferences/ under your script application’s bundle identifier. In general, you do something like:

  • define properties for your preference items with initial starting values
  • register your preference items with the user defaults (this sets up a default item store)
  • read your preference items (this will update items from the application’s preferences.plist, if any, or use the default)
  • do your application stuff
  • save your preference items before quitting

In your project, add the following to the beginning of the script to declare the frameworks used:

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

Add a couple of handlers for the defaults stuff (I'm using your pCurrentMode property with a preference key of "CurrentMode"):

on getDefaults() -- register and read any previously saved preferences
  tell standardUserDefaults() of current application's NSUserDefaults
    registerDefaults_({CurrentMode:pCurrentMode})
    set pCurrentMode to objectForKey_("CurrentMode") as text
  end tell
end getDefaults

on saveDefaults() -- update the preferences
  tell standardUserDefaults() of current application's NSUserDefaults
    setObject_forKey_(pCurrentMode, "CurrentMode")
  end tell
end saveDefaults

Then call getDefaults() at the beginning of your run handler, and saveDefaults() at the end (so that the pCurrentMode preference is only updated when the script runs to completion). Save the script as an application (without the "Stay open" option) and give it a unique bundle identifier (there should be a Bundle Contents toolbar item after saving that will give you easy access).

Finding Current AppleScript Resources

Other than a few blog posts about the new security measures, MacScripter / Script Debugger forums and Stack Overflow are pretty much the only places you are going to find anything current.

There are a few classic AppleScript books such as AppleScript: The Definitive Guide and Apple Training Series: AppleScript 1-2-3, but they aren’t current. There just doesn’t seem to be enough of a market - even the current Apple documentation has been archived. For AppleScriptObjC, there never was a whole lot other than Shane Stanley's contributions such as Everyday AppleScriptObjC and the earlier AppleScriptObjC Explored. Apple’s intent would seem to be for you to use their current Cocoa documentation and convert it if you happen to be using something else.

AppleScript documentation has almost never been kept up to date (even the AppleScriptObjC Release Notes is essentially a single page from 2009), but the current AppleScript Language Guide (2016), even though it has been archived, is still relevant, since the language itself hasn’t changed all that much.