3

I've created an application with a "Run AppleScript" action in Automator.

I've created a Service in Automator which launches the aforementioned app.

The Service is triggered by a keyboard shortcut, assigned in System Preferences

The application presents a series of dialogs to the user. I would like to give the user the option of restarting the entire application mid-application. For example, suppose that the following line was Line 100 of the code:

set buttonChoice to button returned of (display alert "Do you want to replace this entry?" as critical buttons {"Start over", "No", "Yes"})

   if buttonChoice is "Start over" then     
      <this is where I need your help, Ask Different>
   end if

When I say "restart" or "start over," I mean that I want to return the user to the initial dialog of the application, that is, the very first line of code of the application.

Can this be done using AppleScript?

If needed, this is the location of the application file:

/Applications/My Applications/My Log.app

  • 1
    Is there any way you can explain in more detail? Because I posted an answer and it was pointed out to me in a comment that my answer doesn't really tackle your issue. – wch1zpink Jan 23 '17 at 15:46
  • The "Do you want to replace this entry?" alert above is, for example, the tenth dialog presented to the user. I would like the application to restart completely, from the very first line of code, if buttonChoice is "Start over", as @user3439894 suspected. I apologize if this was not clear. – rubik's sphere Jan 23 '17 at 20:38
  • So your "My Log.app" basically just contains a bunch of different consecutive dialogs? I think that's what you're saying so I will edit my original answer. Thank you – wch1zpink Jan 23 '17 at 20:52
  • It completes other functions, such as writing text to file and reading text from file, but, yes, "My Log.app" is basically just a bunch of different consecutive dialogs. – rubik's sphere Jan 23 '17 at 21:00
2

The following code example assume that the Run AppleScript action starts with an on run command without any list, e.g. {input, parameters} and consequently ends the script with end run:

on run

    (*
            This comment represents the e.g. previous 99 lines of code.
        *)

    set buttonChoice to button returned of (display alert "Do you want to replace this entry?" as critical buttons {"Start over", "No", "Yes"})
    try
        if buttonChoice is "Start over" then
            return on run
        end if
    end try

    (*
            This comment represents the rest of the code in the script.
        *)

end run

Note that I tested this in macOS Sierra 10.12 and while it does appear to start over, because if you press the "Start over" button it loops until you select another choice, I'm not sure this is the best way to implement this. I say that because I do not know the structure and coding of the rest of the script and that may make a difference along with the fact that while testing other code on each side of this, I was able to crash the app depending on what was going on if I looped more then once at this point.

So with that said, I'm offering this as something to test throughly before implementing it in the final code.

I'd suggest just allowing the user to terminate the app with a message to manually restart by pressing the key-combo to trigger the service that starts the app over implementing a loop such as this.

1

Update:

I began with user3439894's solution as my base, but I had to make a couple of modifications to perfect the "restart" feature for my application (as user3439894 recommended).

At first, when I directly copied the solution, I was having an issue where the code would run one extra time after the restarted run completed. But, by trial and error, I managed to intuit a solution.

Here's what I did to get it to work:

I put the following line before the very first line of the main body of my code:

try

and at the end of my main body of code, I put:

on error errStr number errorNumber
end try

As instructed by user3439894, I also had to delete the default return input line from this location (and I had no need to return input in this application, anyway).

Then, after every dialog of which I wanted the user to be presented with the option to start over in that dialog, I put:

if buttonChoice is "Start over" then
    return on run
end if

Following these steps, the "restart" function works perfectly! It doesn't repeat an extra number of times at the end, nor does it ever present the user with some error. I can also loop the application multiple times, if I so desire, without error or issue.

It even works perfectly when the dialogs are located in subroutines. Note that my subroutines are not within the try block.


What happens when you need to use input in your AppleScript?

I managed to discover a workaround. Simply place the restart code within a subroutine:

on run {input, parameters}
    restartSubroutine()
    return input
end run

on restartSubroutine()
    set buttonChoice to button returned of (display alert "Do you want to replace this entry?" as critical buttons {"Start over", "No", "Yes"})

    try
        if buttonChoice is "Start over" then
            return on run restartSubroutine()
            (*
            The following lines work as well:
            return on restartSubroutine()
            return run restartSubroutine()
            *)
        end if
    end try
end restartSubroutine

Of course, the restart function will only take you back to the beginning of that subroutine, not to the first line of the main body. But, if you don't need to reference the input until the end of your script, this may work for you so that you can have a functional restart button right up until the moment that you need input.


Edit:

I just figured out an even better workaround:

on run {input, parameters}
    restartSubroutine(input)
    return input
end run

on restartSubroutine(input)
    set buttonChoice to button returned of (display alert "Do you want to replace this entry?" as critical buttons {"Start over", "No", "Yes"})

    if buttonChoice is "Start over" then
        return restartSubroutine(input)
    end if      
end restartSubroutine

If you send the input to a subroutine, then you can have a restart function all while having access to the input contents. Win-win.

  • @user3439894 Is this solution okay or do you see potential issues with it? – rubik's sphere Feb 5 '17 at 7:12
  • If it works for you as needed/wanted and isn't causing errors, then I don't see any problem with using it. – user3439894 Feb 6 '17 at 20:21
0

I've discovered another method to restart an AppleScript.

This method is not "better" than the other methods that have been provided; it is merely just another way of approaching things. Here's how you can restart an .scpt file in AppleScript:

if buttonChoice is "Start over" then
    run script "/Users/Me/Desktop/My script.scpt"
    error number -128 (* user cancelled *)
end if

Or, alternatively:

if buttonChoice is "Start over" then
    run script (path to me)
    error number -128 (* user cancelled *)
end if

This code simply creates another instance of the currently running AppleScript .scpt file, and then quits the current instance.

I learned about run script from a comment by user Camelot on the following webpage:

Call Another Applescript | Apple Communities


With the same underlying premise, if you want to restart an .app file using AppleScript, then use this:

if buttonChoice is "Start over" then
    do shell script "open -n " & quoted form of "/Users/Me/Desktop/My app.app"
    error number -128 (* user cancelled *)
end if

Or, alternatively:

if buttonChoice is "Start over" then
    do shell script "open -n " & (quoted form of (POSIX path of (path to me)))
    error number -128 (* user cancelled *)
end if

The -n allows a new instance of the application to open, even if an instance of this application is already running. The -n is necessary because, practically speaking, the AppleScript app technically closes after the second instance has been created, even though this may not appear to be the case based on what one sees on their screen.


When should one use this method of restarting?

This is going to get a little confusing, so bear with me...

I have an AppleScript .scpt file that automatically runs when my computer wakes from sleep. (I use SleepWatcher to accomplish this.)

This .scpt file calls a specific subroutine of a different .scpt file; it skips over the entire script, and only runs the code that is found in one, specific subroutine. (To accomplish this, I use the method described here.)

This second .scpt file is rife with "Start over" buttons in nearly every dialog, employing:

return on run

to achieve the restarting effect. (This restart method was provided in a previous answer to this question.)

Now, the problem is, while my first .scpt file does in fact successfully restart the second .scpt file when return on run is used in the second .scpt file, once the script has completed, a dialog box containing the following error is presented to the user:

«script» doesn’t understand the “return”

To be fair, this error makes sense; the first .scpt file cannot fully comprehend the significance of return, because it was never truly running the other script. It was only running one subroutine of the other script.

This error bugged me. So, I messed around with the code some more, and learned about run script. The specific type of scenario that I've just explained is where the run script restart method truly shines.

The simple run script method saves the day: it restarts the second .scpt file, and without having to invoke the return command in the process. Thus, this method is completely error-free, even when being run outside of its larger, surrounding code. This is the only method on this page that can do this, as the other methods are reliant on return.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .