Progress bar issues

Hi, I am developing a MacOS app for the Mac App Store using AppleScript. It was rejected because the Stop button of one of the progress bars did not produce any action when clicked (or, at least it didn't for over 5 minutes). I have a theory for why this happened: The app was in the middle of executing a shell command while the progress bar was showing, so the application was unresponsive and thus not responsive to user clicks. But this doesn't help me solve the issue. If the app is not responsive at that time, how do I make the app close when the "Stop" button is clicked? I want the whole app to close quickly when Stop is pressed.

Another thing that further complicates this issue is that everything is wrapped in try-catch blocks. When a user cancels something in AppleScript (such as by pressing a Stop button in a progress bar), an error is thrown to signal that the script must stop. If the user clicks "Stop" in the middle of a try-catch block, the script will not cancel-it will execute whatever I tell it to do on an error. There are too many try blocks in the app to put in a check for what error occurred in each one.

So, how do I make the script respond quickly and produce the desired action (closing the app) in under 5 minutes?
Answered by Dev500 in 674628022
I've fixed it!
The first thing would probably be to not use exceptions for flow control.

You don’t mention how you are calling the shell command or how you are handling the progress bar stop button, but if you are using NSTask, you can keep track of it (property, etc). Then the task can be terminated immediately in the stop button handler before performing the normal progress bar dismissal.

Hi,
I am not using any API's such as NSTask or anything from Swift-I made the app in Script Editor, using purely AppleScript. I preform the shell commands using AppleScript's "do shell script" command (see https://developer.apple.com/library/archive/technotes/tn2065/_index.html for how that command works).

The problem with how I implement the progress bar is that there is no Stop button handler-that functionality is built-in to AppleScript.

How do I make the program register the Stop button's click even while the script is on another task and the UI is unresponsive?

How do I make the program register the Stop button's click even while the script is on another task and the UI is unresponsive?

You refactor to make the UI responsive. do shell script will block until it is completed, and the built-in progress indicator is mostly designed to work with regular single threaded AppleScripts, only generating a "user cancelled" error for the stop button.

The task should really be run in the background, and you would need to get a process ID so that you can kill it as needed. Using your current script you might be able to do something like:

Code Block
# putting an `&` at the end of the command runs it in the background
# `$!` returns the pid of the most recently started background process
# AppleScript needs an output redirection clause to free stdout / stderr
set pid to (do shell script "sleep 30 &> /dev/null & echo $!") -- example task that takes a while
delay 5 -- kill it early
try -- handle error if already dead
do shell script "kill " & pid
end try

But the best way for all that is to use some AppleScriptObjC and create a real, event driven Cocoa-AppleScript application (instead of a script in an app wrapper), where you can take advantage of the various Cocoa APIs:
  • NSTask will let you launch a shell script in the background, has notifications to let you know when data is ready or it is finished, and has methods to terminate the task.

  • An NSButton can have an action handler that will run whatever code when the button is pressed, without using error statements.

  • Xcode will let you edit the user interface, so you are not stuck with the default Script Editor template.


Hi,
That would be a good solution, but I need the stderr of the command to handle it. My app is a network troubleshooting app, and it uses commands to attempt to access websites. If a (network) error is thrown by a command, my app needs that error immediately to handle it. If I use background commands, I will not be able to tell if a command failed or succeeded. I also can't access /dev/null, as my app is sandboxed. My app has it's own "trash bin" called "Tests.log" that is never referenced. Could I use that? (But the main question is about the stderr).
Another reason to use something like NSTask, where pipes or file handles can be set up for stdout and stderr with various notifications. Since you keep adding details, it might be helpful to post a Minimal, Complete, and Verifiable Example that demonstrates the issue.
Accepted Answer
I've fixed it!
Progress bar issues
 
 
Q