Advanced AppleScript Techniques

Advanced AppleScript Techniques

Tutorial Details
  • Topics: Automation, AppleScript
  • Difficulty: Advanced
  • Estimated Completion Time: 45 Minutes

We’ve previously offered a basic introduction to AppleScript, and this article will cover a number of useful AppleScript tips and tricks to help you create some amazing advanced scripts.

This is part of a series of posts that revisits some of our readers’ favorite tutorials from the past that still contain awesome and relevant information that you might find useful. This post was originally published on September 24th, 2009.


First, Do Your Homework

This is the third article in my series on automation. Be sure to check out my Beginner’s Guides to both AppleScript and Automator to get acquainted with scripting and automation in general.

In those articles I cover basic functions such as variables that I will not explain here to reduce redundancy. After you’ve familiarized yourself with basic AppleScript, you’ll be ready to jump into the good stuff. Let’s get started!


If and If, Else

“If” and “if, else” statements are used when you have a certain command or set of commands that you want the script to perform only if the stated requirements are met.

For instance, say you want the script to tell you if the value of two variables, x and y, is equal or different. You would use an “if” statement to tell the script how to react to each situation.

There are only three possibilities: x is greater than y, y is greater than x, or x = y. Let’s start with a basic “if” statement and make it more complex as we go along.

--Declare Variables
set x to 5
set y to 78
--Run if Statement
if x < y then
	return "yes"
end if

Here I first declared two variables and assigned values to each. Then I told Script Editor to return “yes” if x is greater than y. Notice the structure of the “if” statement is much like a tell block. You must end every “if” statement with “end if” or your code will not compile.

You end every “if” statement with “end if” or your code will not compile.

The basic structure of the statement is as follows: If (a given statement) is true, then do the following. Be sure to include the statement to be tested and “then” on the same line as “if.”

Then place your instructions on the next line(s) before ending the block. If you compile and run the statement above, Script Editor should return “yes,” indicating that x is in fact less than y. If x were greater than y, nothing would happen because we haven’t programmed for that contingency. Let’s try something a little more complex.

--Declare Variables
set x to 78
set y to 5
--Run if Statement
if x < y then
	return "yes"
else
	return "no"
end if

Here we’ve included an “else” statement that tells Script Editor what to do if the initial conditional statement is false. The structure is as follows: If (a given statement) is true, perform action a, otherwise, perform action b.

This is enough for many situations, but we still haven’t created a contingency for our third situation: x = y. To accomplish this we’ll use “else if.”

--Declare Variables
set x to 5
set y to 5
--Run if Statement
if x < y then
	return "x is less than y"
else if x > y then
	return "x is greater than y"
else
	return "x is equal to y"
end if

Here we’ve allowed for all three different scenarios. Running this script should return “x is equal to y.” Notice the last scenario only requires an “else” instead of an “else if.” This is because if the first two statements are false, it must be the case that x = y (barring any input errors).


Booleans

Assigning a truth value to a variable is easy. Just set the variable to true or false.

set theBoolean to false
if theBoolean = true then
	return "It's true!"
else
	return "That's just not true"
end if

For now just know that it’s that easy. We’ll see a more practical use in the final example.


Displaying a Dialog Box

AppleScript makes it super simple to display output to a user. Just type the following:

display dialog "Greetings!"

This should produce a simple window displaying your greeting and two buttons: “Cancel” and “OK.” It is also possible to customize those buttons with the following code:

display dialog "How are you?" buttons {"Great", "Horrible", "None of your business!"} default button 3

There are three sections to this code. The first section displays a dialog box with the text “How are you?” The second section determines how many buttons the dialog will have and what they will say. In this case we have three buttons. Notice the set of buttons must be encased in brackets and each button encased in quotes.

Notice the set of buttons must be encased in brackets and each button encased in quotes.

The resulting dialog
The resulting dialog

Finally, the third section tells the dialog what the default button should be. Setting the default button to 3 highlights the third button when the dialog is shown.

Having a dialog with multiple buttons is great but it’s no good unless you know how to use the input received from the user as a result. Let’s take what we just learned about dialogs and combine it with an “if, else” statement to see how we can use dialogs to carry on a… well… dialogue.

--Create a dialog with 3 buttons
display dialog "How are you?" buttons {"Great", "Horrible", "None of your business!"} default button 1
--If user is great
if result = {button returned:"Great"} then
	display dialog "Excellent, carry on good sir"
--If user is Horrible
else if result = {button returned:"Horrible"} then
	display dialog "You need to cheer up!"
--If user is a jerk
else
	display dialog "Excuse me pal!"
end if

As you can see, the initial line of code is taken directly from the example above. The next portion uses an “if, else” statement to handle each button press. The syntax is essentially: If the user presses button x, then do action a, else if the user presses button y, then do action b, else do action c.

If the user presses button x, then do action a, else if the user presses button y, then do action b, else do action c.


Receiving Text Input From a User

Sometimes a simple button isn’t enough. I frequently write scripts that require the user to input text. To accomplish this, we use a dialog box like above, with a slightly different syntax.

--Display Dialog and Get Input
display dialog "How are you?" default answer "Tell me how you feel"
--Get Answer & Return Comment
set theAnswer to (text returned of result)
display dialog "I'm glad you feel so " & theAnswer

By putting a “default answer” into our code, we tell AppeScript that we want the user to input text in response. We then set a variable to the text returned by the user. Then we can use that variable anywhere we want to use the input.

Receiving Text Input From a User
Receiving Text Input From a User

Handling Errors: The Try Block

Letting users enter data is dangerous. If you are looking for a very specific answer or type of data you can be certain your users will screw it up and cause an error.

If you are looking for a very specific answer or type of data you can be certain your users will screw it up and cause an error.

For this reason, you need to filter what comes in from the user to make sure it’s exactly the kind of input you want. For this, we’ll use a try block and a couple of “if” statements.

--Display Dialog and Get Input
display dialog "Pick a number 1-10" default answer "Only Enter Numbers Less than 11!"
--Test For Invalid Characters
try
	set theAnswer to (text returned of result) as number
on error
	display dialog "Invalid Input"
	return
end try
--Test for Correct Numbers
if theAnswer < 1 then
	set theTest to 0
else if theAnswer < 11 then
	set theTest to 1
else
	set theTest to 0
end if
--Return Comments to user
if theTest = 0 then
	display dialog "Invalid Input"
else
	display dialog "You chose the number " & theAnswer
end if

The code above is complicated so let’s break it down. The first section simply displays a dialog and asks the user to pick a number. However, we only want to accept numbers one through ten. If anything else is entered, we want to tell the user the input was invalid.

The second section is where we test to see if any non-numbers were entered such as letters or symbols. A try block allows us to tell AppleScript to try to execute the code and see what happens.

A try block allows us to tell AppleScript to try to execute the code and see what happens.

Here we’ve told AppleScript to try to take the text returned by the user and turn it into a number. If there were no try block and we executed this command, it would throw an error and end the program if the user entered a letter or symbol.

However, the try block allows us to give AppleScript a special command if an error is thrown. In this case we display an “Invalid Input” dialog if an error is thrown. We then command the script to end by typing “return.” Notice a try block must end with the “end try” command.

When programming, you must always consider every possible mistake that your users can make and create a contingency for it. Try running the code above and entering a few invalid characters. You can see that no matter what you enter, the script only accepts numbers 1-10.

When programming, you must always consider every possible mistake that your users can make and create a contingency for it.


Handlers

In the same way that variables store a lot of information in a small snippet of code, handlers (often called functions in other programming languages) are a great way to repeatedly execute a large number of commands with very little code.

By storing multiple commands in one snippet, you save yourself lots of programming and greatly simplify your code. Here’s an example:

--Define Handler
on additionHandler(theVariable)
	set x to 5
	set y to 5
	set z to x + y + theVariable
	display dialog "10 + " & theVariable & " = " & z
end additionHandler
--Run Handler
additionHandler(8)
--Run Handler Again
set theNumber to 145
additionHandler(theNumber)

The syntax for defining a handler is: on HandlerName(variable), followed by the commands to execute, followed by “end handlerName.”

The variable in the parentheses (theVariable) is the part that we must define every time we run the handler. The first section of code by itself will do nothing because we haven’t yet run the handler, only defined it.

Notice when we run the handler we type the handler name followed by a number in parentheses. The handler will now take this number and insert it into any place we have typed “theVariable” into our handler definition.

In this case, it will set the chosen variable to z and add it to x(5) and y(5) and display a dialog with the answer. So additionHandler(8) sets z to 8 and then adds 10 (5+5) and gives us the result of 18. When we run the handler again, the same code is executed on a different number.

I did this to show you that you can define a variable elsewhere and insert it into your handler. Here we’ve set theNumber to 145 and then run the handler on theNumber.

Note that any variables defined inside an handler don’t exist outside of the handler. So if you ended this script with “return z” you would get an error that tells you that “z” is not defined.

Be sure to give all your variables descriptive and unique names.

This means that you can technically use identical variable names for variables defined inside and outside of an handler. However, this makes for messy and confusing code and should be avoided. Just be sure to give all your variables descriptive and unique names.


Repeat Blocks

It is often the case that you will find the need to create a step in your script that repeats a given number of times.

In AppleScript we accomplish loops with repeat blocks.

Many programming language call this a loop. In AppleScript we accomplish loops with repeat blocks. Repeat blocks are fairly simple and versatile, here’s a simple example:

set theNumber to 5
repeat theNumber times
	display dialog "Isn't this annoying?"
end repeat

Here we’ve created a dialog box and set it to repeat five times in a row. This example, though useless, shows you how easy it is to create repeating steps.

However, you don’t always know how many times you want a given step to repeat. It is often necessary to repeat a step or series of steps while a certain condition remains true. For these cases we use a “repeat while” block.

set theNumber to 1
repeat while (theNumber < 2)
	display dialog "Play Again?" buttons {"Yes", "No"} default button 1
	if result = {button returned:"Yes"} then
		display dialog "Please insert more quarters!" buttons {"OK"} default button 1
	else
		display dialog "Goodbye!" default button 2
		set theNumber to (theNumber + 1)
	end if
end repeat

Here we’ve setup the dialog that repeats while theNumber is less that 2. If the user says they want to play again, they are given a message to insert more quarters and the dialog repeats.

As long as the user keeps hitting yes, the dialog keeps repeating. When the user selects “No”, we add one to theNumber, setting the value to two. The script sees that the truth requirement has become false and ceases repeating the action.


Putting It All Together

Rather than leave you with several bits of unconnected code, I thought it would be best to give one final example integrating everything we’ve learned above. Let’s take the “pick a number” example above and flesh it out into a fully working script.

--Set Random Number
set randomNumber to random number from 1 to 10
--Boolean to Test if Script Should be Repeated
set repeatTest to true
--If Result Is a Number, Change to True and See if the Number is Between 1 and 10
set testNumbers to false
--If Result Passes All Previous Tests, is it Correct?
set winningTest to false
--Count Number of Guesses
set tryCount to 0
--Generic Dialog Handler
on dialogBox(theMessage)
	display dialog theMessage
end dialogBox
--Repeat if Any Tests Are Failed
repeat while (repeatTest = true)
	--Display Dialog & Get Result
	display dialog "Choose a number 1-10" default answer "Enter Only Numbers 1-10" default button 2
	set theAnswer to (text returned of result)
	--Is Result a Number?
	try
		set theAnswer to theAnswer as number
		set testNumbers to true
	on error
		dialogBox("Invalid Input")
	end try
	--Is Result Between 1 and 10?
	if (testNumbers = true) then
		--Test for Correct Numbers
		if theAnswer < 1 then
			dialogBox("Invalid Input")
		else if theAnswer < 11 then
			set repeatTest to false
			set winningTest to true
		else
			dialogBox("Invalid Input")
		end if
	end if
	--Is Result the Correct Answer?
	if winningTest = true then
		if theAnswer = randomNumber then
			dialogBox("You Guessed it in " & tryCount & " tries!")
		else
			dialogBox("Try Again!")
			set repeatTest to true
			set tryCount to tryCount + 1
		end if
	end if
end repeat

At first glance this script may seem intimidating. However, if you’ve gone through all of the examples above, it should all make perfect sense.

Step 1: Declare Your Variables

First, we declare our variables, mostly boolean at this point. Then we create a handler (dialogBox) to display our various dialogs throughout the script. The user is then asked to pick a number between 1 and 10. The answer is then run through three separate tests.

Step 2: Check the Input

Next, we test to see if the input contains letters or symbols. Then we test to see if the number returned by the user is in the acceptable range (1-10).

Step 3: Does the Input Match the Random Number?

Finally, we test to see if the user’s guess matches the random number generated by the script.

Using booleans and if statements the script is structured so that each consecutive test is only run if the input passes the previous test. If any test returns false, the repeat block begins again and the following actions are not run.

Nesting

Notice that it’s actually possible to nest if statements within if statements. This is true of repeat blocks and tell blocks as well. Be careful though of using nested statements excessively. One or two layers deep isn’t bad but anything beyond that just gets messy and hard to follow.

Running this script should let you choose a number and keep guessing until you get it right. Play with it a few times and see how many guesses it takes you to get it right!


Conclusion

This article has walked you through using if statements, booleans, dialog boxes, user input, handlers, error handlers and repeat blocks.

You should now be equipped to create some seriously fancy scripts that will automate the toughest of workflows. Just bookmark this page and use it as a cheat sheet for all your future scripts.

As mentioned above, this is the third installment in my series on automation. Your feedback is very important in deciding what I write about so let me know if you’d like me to keep them coming. If the demand is great enough, I’ll gladly write more!

Josh Johnson is secondfret on Graphicriver
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.cuby.co.nr Henry Bennett

    Really great tutorial! I’ve learnt heaps from this article, and I’ve always wanted to expand my knowledge in AppleScript. I feel like I’m starting to get the hang of it a little better.

    Thanks for this, and I’d love to see more!

  • http://www.w2point.com Web 2.0

    Geek stuff here.. Great tutorial :)

  • fellowweb

    Joshua, thank you for this great tutorial! It’s awesome to get such a great series of introductions to AppleScript/Automator!

    Keep it coming. ;)

    • http://www.yahoo.com/ Etta

      You’ve got to be kidding me-it’s so transrapnetly clear now!

  • http://johnathan-barrett.me.uk Johnathan Barrett

    Does anyone know if it’s possible using AppleScript to replicate GrabUp’s functionality? When I take a screenshot it takes it, uploads via FTP puts URL on clipboard then deletes? GrabUp’s not working on Snow Leopard and TinyGrab isn’t working for me either. I got Snapplr and it does it but doesn’t upload to custom FTP which is sorta important.

    • http://www.twitter.com/secondfret Joshua Johnson
      Author

      Excellent idea! It might be great for a future tutorial. In the mean time, here’s an Automator action that will upload a file to an FTP server. http://editkid.com/upload_to_ftp/

      Also in Automator is an action to take a screenshot and save it in a specific location. You could setup a folder action on that location to automatically run any files that pop up in the folder with the upload action.

      Now the real trick is getting the actual link to the pic to appear in your clipboard. This is where AppleScript comes in. You would have your path (all but the filename) set to a variable, then you would append the variable with the file name each time the script is run and copy the variable to the clipboard.

      Voila! Your very own GrabUp utility… I will definitely have to try this!

      • http://davidappleyard.net David Appleyard

        Great Thinking Josh :-)

  • Ray Robinson

    Josh, thanks for all this, Quote ‘Your feedback is very important in deciding what I write about so let me know if you’d like me to keep them coming. If the demand is great enough, I’ll gladly write more!’

    So a question,
    Can applescript solve my problem for me or am I asking too much of it?

    At the end of the day I will be halfway through a project. Have a couple of word processor docs open, my diary program, a spreadsheet doc, and safari with a few tabs relating to what I’m doing.
    ->Can I write an applescript that will look at what’s open and record it so that the next time I want to work on that project I can activate the script and it will put everything back where it was?

    ( I know I could take a screen shot but that’s not what I’m asking, I want applescript to put it all there for me)
    I know it’s a tall order and maybe it needs a dedicated app to do it, but it would save me a lot of digging.
    Thanks in advance.

    • Someone

      I’m not completely sure, but you can have applescript see what files are open, save the part to those files as a text document, and when you log in next, when you run the script, it will open the files…
      The problem is that not all applications work with applescript. (But the main ones do.) To find out if the application works, go to the dictionary in Script Editor.

  • Dmitry

    Thank you for the tutorial.
    How can I filter files by extension selected in the following code:

    tell application “Finder”
    set these_items to the selection
    end tell

  • Mike

    thanks for the great tutorial. i’ve just noticed a little inconsistency with the final version of the number game. when run as it is shown here on the site, it actually tells you that you guessed it in one try less than it actually took. (ie if you get the number on your first try, it tells you you got it in 0 tries; if you get it on the second, it tells you you got it in 1 try; etc.) i’ve altered the last section slightly to reflect this. it also requires setting tryCount to 1 at the beginning instead of 0. this will now tell you the correct number of tries it takes you to guess the number:

    –Count Number of Guesses
    set tryCount to 1



    –Is Result the Correct Answer?
    if winningTest = true then
    if theAnswer = randNum then
    if tryCount = 1 then
    dialogBox(“You guessed it in ” & tryCount & ” try!”)
    else
    dialogBox(“You guessed it in ” & tryCount & ” tries!”)
    end if
    else
    dialogBox(“Try again.”)
    set repeatTest to true
    set tryCount to tryCount + 1
    end if
    end if

  • http://www.martinvaresio.com.ar/anillos.html Anillos de plata

    I write an applescript that will look at what’s open and record it so that the next time I want to work on that project I can activate the script

  • Adam

    I loved your tutorial! I knew nothing about Applescript before I read it and now I know heaps! Can you please write some more?

  • Blorsten

    Nice tutorials, make more please…

  • John

    Wicked tutorial!

    i’m new to applescript and i am getting the hang of it, however how to i create a script that will connect me to some share points on a server? i.e, i would normally go: “Go” > “Connect to server” > i’ll type in a server address > then connect to a few shared volumes. that is what i want to automate.

  • http://www.mamaiacazare.ro Cazare Mamaia

    This is so new to me, but with help from you site i think i’ll finish my project.

  • John M

    Thank you so much for these excellent tutorials. I’ve been interested in the promise of AppleScript for… what? 15 years, since I was a schoolchild, but despite Apple’s claims of simplicity I always felt overwhelmed. Finally I see what they meant! Thank goodness for the internet, and moreso, to you, for finally helping me use my Mac the way I’ve always wanted.

  • James Burton

    this is soo awesome

  • http://www.cazarecostinesti2012.ro/ cazare costinesti 2012

    Thank you so much for these excellent tutorials.

  • Bryan

    Very interesting, but I do I have one gripe. You have a section on what you call ‘operators’. The correct term for such functions/procedures in AppleScript is handlers. The term operator has an entirely different meaning from that which you give it – an operator is a symbol, word or phrase that derives a value from another value or pair of values.

    In mathematics and general computer science this means symbols such as the arithmetic operators + – * / and in AppleScript it also includes such phrases as ‘contains’ or ‘does not contain’.

    The reason I point this out is not just to be pedantic, but because using a word that already has a well defined and specific meaning meaning to mean something entirely different is going to be confusing to anyone who consults other documentation that uses it correctly.

    See the Apple documentation:
    AppleScript operators:
    https://developer.apple.com/library/mac/ipad/#documentation/applescript/conceptual/applescriptlangguide/reference/ASLR_operators.html

    AppleScript handlers:
    https://developer.apple.com/library/mac/ipad/#documentation/applescript/conceptual/applescriptlangguide/conceptual/ASLR_about_handlers.html

    • Glen

      @Bryan
      I couldn’t agree more, except that I would describe as a mistake that’s needs to be corrected. Saying it’s just a gripe is being too kind. It’s just plain wrong.

    • http://mac.tutsplus.com Josh Johnson
      Author

      Thanks for pointing that out! I’ve updated this section with the correct name.

      Cheers

      • Bryan

        Very good, and you’re most welcome.

  • Linish

    Great Tutorial! Very Useful :)

  • Shashank

    Hey nice tutorial.

    Would you know how to access html elements in a OS dialog box?
    Am new to applescripts. I have a window with html elements in it. How do I access these html elements using applescripts?
    This is an installer whose elements i have to identify. i could only access close, maximize n minimize n the title of the window.
    not the elements inside it.
    Can u help with this?