Automator “Quit Application” Action stops

applescriptautomatorcatalina

I'm trying to set up an Automator script to do a bunch of routine things before I pull my laptop off it's thunderbolt dock – turn WiFi back on, eject disks, and quit certain applications. It's this last one that's currently vexing me – using either AppleScript or the Automator "Quit Application" action, the script stops after executing the quit order. The app I'm currently trying to quit is MakeMKV. I've also tried it with Zoom (another app I'd like to close out).

It only stops if it actually has to quit the app – and the apps do quit. If the app isn't open, the step is skipped and moved past. The error I get is:

"Quit Application was stopped" if using the Quit action or
"Run AppleScript was stopped" if using the AppleScript action.

I've tried it with Chrome, Messages, and Notes, all of which quit gracefully. These other apps are executing the quit order – is there any way to figure out why the script is stopping, and suppress whatever is doing it?

edit: Code block looks like the following:

enter image description here

Best Answer

You can try these three potential solutions to see if either fare any better than the built-in Automator action.

The first is a plain AppleScript handler that takes a list of exceptions (apps that are exempt from being quit), and then proceeds to send a quit command to all the others (they being any application process not belonging to a file in the /System directory--therefore, Finder and System Events will be spared, but Automator will not unless it is specified in the list of exceptions, X):

to quitAll(X as list)
    local X

    tell application id "com.apple.SystemEvents" to script Apps
        property list : the bundle identifier of processes ¬
            where the POSIX path of its application file ¬
            does not start with "/System"
        property locked : X
    end script

    tell Apps to repeat with id in its list
        ignoring application responses
            set A to the application id id
            if A's name is not in locked ¬
                then try
                quit A
            end try
        end ignoring
    end repeat
end quitAll

The second uses the same method as above to retrieve a list of running applications but it grabs their pid numbers. These are passed en masse to a do shell script call to kill the applications (some people get nervous about this, possibly because the name of the command sounds like it's a bad action to perform, but it's perfectly safe, and very standard. Applications typically also save their window and document states, so the kill command can allow them to retrieve the .savedState data when re-opened later.

to kill(X as list)
    local X

    tell application id "com.apple.SystemEvents" to script Apps
        property parent : a reference to every process where the ¬
            POSIX path of its application file doesn't start with ¬
            "/System"
        property id : my unix id
        property name : my short name
        property locked : X
    end script

    tell Apps to repeat with i from 1 to length of id
        if name's item i is in locked ¬
            then set id's item i to []
    end repeat

    set my text item delimiters to space
    "kill " & integers in the id of Apps
    do shell script result
end kill

The last solution is an AppleScript-ObjectiveC handler that, once again, obtains a list of the running applications that aren't located anywhere in the /System folder, and don't appear (by name) in the list that's passed to the handler:

to terminate(X as list)
    set pred to "!bundleURL.path LIKE '/System/*'" & ¬
        "&&!localizedName IN[c] %@"

    script
        use framework "Foundation"

        set [A, P] to a reference to my [¬
            NSWorkspace's sharedWorkspace's runningApplications, ¬
            NSPredicate's predicateWithFormat_(pred, X)]
        (A's filteredArrayUsingPredicate:P)'s terminate
    end script

    run result
end terminate

I haven't tested this handler to see what permissions need granting, as I didn't want all of my programs to terminate. But let me know if there are any hiccups and I'll direct you accordingly. The word terminate inside the handler can be replaced by forceTerminate (case sensitive) to do a force quit on each application instead. This, if it were me, would actually be my preference, as I have set my system to save the state of applications on forced terminations, and it avoids the "Do you want to save the current document?" banality.