AppleScript is Apple's powerful automation language for macOS. Here's how to use it to speed up your workflow when using your Mac.
History
AppleScript was born in the early 1990's at Apple in an attempt to modernize the Mac's OS. It was partly derived from an earlier script language designed by one of the original Mac architects - Bill Atkinson who wrote the original Mac's graphics system, QuickDraw.
On the early Mac, Atkinson designed a card-based development environment called HyperCard, and a scripting language to go with it called HyperTalk. Developers could attach small HyperTalk code snippets to user interface elements such as buttons, text fields, and lists.
Apple, however, wanted a way to script and control the entire Mac OS, not just one application. Some elements of HyperTalk were commandeered, and other scripting elements were added. The result was AppleScript.
First released thirty years ago along with the first color version of the Mac OS, System 7, the Mac also shipped with a lightweight Script Editor application, which lives on to this day in macOS.
In the 1990's a slew of third-party AppleScript developer tools appeared, including Facespan and UserLand Frontier.
Facepsan development environment on Mac OS 9.
Getting Started
AppleScript and macOS's Script Editor app allow you to write and optionally record scripts that have targets in macOS that they can be directed at. The AppleScript language is fairly easy and English-like and is a breeze to learn.
Designed for simplicity, most AppleScripts follow a subject-verb model where you first state which object or objects you are directing your commands at, then you tell those objects what to do. Objects can be apps, windows, UI elements, or data within an application.
The AppleScript Editor window looks like this:
AppleScript Editor
At the top of the window is a pane where you type your script in, and the bottom pane is used for both output and debugging.
You can check the syntax of your script for accuracy by clicking the Compile (hammer) button in the toolbar. AppleScript Editor will compile your script and highlight any errors.
If the script compiles successfully, you will see all its syntax coloring change to that defined in the Settings window. We'll get to the Settings window below in a moment.
Once compiled, click the Play button in the toolbar to run your script, and click the Stop button to stop it.
You can also record scripts from other apps by pressing the Record button and then going to another app and performing some action. The app being recorded must be written to support AppleScript recording.
Once you're done recording, come back to AppleScript Editor and press the Stop button.
Architecture
AppleScript is based on an underlying technology called the Open Scripting Architecture (OSA). Apple designed OSA purposely to allow other scripting languages to be plugged in and used to automate the Mac. AppleScript is but one OSA language.
Later Apple added JavaScript support to OSA, which now allows JavaScript to be used to control the Mac as well.
At an even lower level is another Apple technology called AppleEvents (AE). AppleEvents are an Inter-Application Communication (IAC) facility which allows apps and processes to communicate with each other.
AppleEvents usually have a four-character code for the class, or suite of events, then another four-character code for the event type itself. By sending and receiving AppleEvents, macOS apps can trigger each other to perform certain actions.
AppleScript is based on AppleEvents and uses the IAC mechanism to transport messages and data around the system.
Dictionaries, terminology, and suites
In order for AppleScript to work, apps have to define and publish which events they support. By doing so they advertise to other apps how they can be controlled.
In most cases in macOS, AppleScript-enabled apps define AppleScript dictionaries which define which events and script elements they support and how those events are triggered externally. By publishing dictionaries, other apps can see how to control apps using AppleScript.
Each dictionary defines scripting terminology and one or more AppleEvent suites - and within each suite, events. There are several publicly available utilities for viewing AppleScript dictionaries contained inside apps.
These dictionaries are especially critical when recording an AppleScript from an app since the recording system relies on reading and receiving events and data defined in the dictionaries. The dictionaries are also used by Script Editor to validate and check AppleScript code before running it.
Apple itself defines several suites such as the Core Suite, the Finder Suite, and others. Third-party apps can define their own suites as well.
Viewing dictionaries and suites
AppleScript Editor has a built-in AE dictionary viewer. To access it, run Script Editor, then select File->Open Dictionary. macOS already knows which apps on your Mac publish script dictionaries so it only displays those apps in the list:
The Open Dictionary window.
To view any app's scripting dictionary, select it from the list and click the Choose button. The browser disappears and the dictionary window opens:
Scripting Dictionary viewer.
In the dictionary window's upper left pane is a list of AE suites the app supports. Single-click any suite to view all its supported events in the pane to the right of it.
In this case, in Apple's Bluetooth File Exchange app, it defines one AE suite, Bluetooth File Exchange Suite, which contains two events: browse and send.
In the pane at the bottom of the dictionary window, each command or event is listed, along with its syntax - namely an object to send the message to. In this case, there are two object types, text and file.
Note the command hierarchy - in the case of send there is the command, then the file to send, then the device to send it to. This nested pattern is everywhere in AppleScript.
One of the initial concepts you'll have to get used to when writing AppleScript is that of containers. Everything is contained inside something else, except for top-level objects.
So when writing scripts you typically "tell" an application to do something or fetch or set something inside of something else, inside of yet something else modified by something else. Additional info is provided in AppleScript using modifiers - the equivalent of English elements like "as" or "with".
"of" is often used in AppleScript to drill down through the container hierarchy.
So for example you might tell a tab group of a certain window to select one tab, then perform some action. AppleScript syntax is highly nested.
In AppleScript, nearly everything is considered an object, which is an encapsulation of both code and data. Objects can send and receive messages (called methods), and data which those methods act on.
Also on the right side of the dictionary window's toolbar is a popup menu that allows you to select which language you want to view the suites' descriptions in. Currently, AppleScript, JavaScript, and Objective-C are supported.
Objective-C is Apple's C-based object programming language which much of macOS is written in. It was inherited from macOS's predecessor, NeXTStep, which Apple acquired in 1997 when it bought Steve Jobs' other company, NeXT, Inc.
The hidden AppleScript Utility
Originally macOS shipped with an app called AppleScript Utility which is hidden away in /System/Library/Core Services. You used to be able to run this app to set up how AppleScript behaves but most of its options are now accessible from within the main Script Editor app which lives in /Applications/Utilities.
To get started, launch Script Editor, then select Script Editor->Settings to open the Settings window:
The Settings window.
There are four tabs in the Settings window:
- General
- Editing
- Formatting
- History
and Formatting allow you to set text details in the Script Editor window such as fonts, colors, indenting, suggestions, and line wrapping.
History provides features such as logging, and the number of log entries.
In the General tab you'll find many of the features previously controlled by Script Utility. This includes whether to use AppleScript or JavaScript, whether to show or hide the "tell" application menu, settings for the dictionary viewer, and whether to show or hide the AppleScript menu bar icon (which we'll get to in a moment).
You can also choose an app for editing scripts. In fact, you don't need to use Script Editor to edit scripts. Any text editor will do - you can later import or copy and paste scripts into Script Editor to run.
The one big advantage Script Editor has is syntax highlighting and debugging since you can both edit and run your scripts in the same window.
If you select the Show Script menu in menu bar checkbox in Settings, macOS adds a script menu to the menu bar. From this menu, you can open Script Editor, open the built-in scripts folder that lives in /Library/Scripts on your Startup Disk, or run one of macOS's built-in scripts directly:
The Scripts menu bar item
AppleScript syntax
There are several syntax constructs (keywords) you'll see time and again in AppleScript. Some of the common ones are:
- on
- if
- then
- else
- exists
- end
In addition, there are several verbs in AppleScript such as tell, open, choose, is, set, repeat, copy, try, and others.
'end' can be combined with tell, if, repeat and other commands to form blocks of code. Most nested verb blocks must be ended with an 'end' in order to maintain flow control throughout the program.
The special verb 'return' is used to break out of a block of code and return to a previous execution point in the program.
There are also adverbs that usually follow or modify verbs - such as with
and without
.
Handlers and properties
AppleScript provides handlers - special functions which can be called from another script or elsewhere in the system. There are several standard AppleScript handlers such as open
and run
.
When an AppleScript is first launched, the open
handler is run. This handler allows you to set up initial conditions for the script. Another common handler is run
, which gets executed after the open
handler.
A property is a named variable which can be set to some value to be used or modified later. For example:
property ChooseScriptPrompt : "Select compiled script file(s) containing folder actions"
This defines a property named ChooseScriptPrompt
whose initial value is set to the string of text after the ":". The ":" is required.
Properties are usually defined all at once at the top of AppleScripts.
Named functions
You can also define your own named functions of AppleScript, then call them from elsewhere in the script. use the to
keyword followed by a function name and empty parenthesis to define a function. For example:
to ChooseFileFromFAScriptFolder()
(some code)
end ChooseFileFromFAScriptFolder
Each named function must be ended with the "end" keyword followed by the same function name. The names must match exactly or you'll get a syntax error.
To call custom functions from elsewhere in an AppleScript, use the keyword "my" followed by the function name and an empty parenthesis. For example:
on run
my ChooseFileFromFAScriptFolder()
open the result
end run
The special variable result
is reserved for the value returned by a function. After running, result
will contain whatever that function returns.
Inside a function, you use the return
keyword to force a function exit - an optional return value is sent back to the calling function. For example:
return SelectedScripts
Interrogating objects
Objects in AppleScript have a class - which describes the type of object. You can find out the class of an object using the class
keyword. For example:
if class of ChosenScripts is boolean then
error number -128
end if
In this example class' asks ChosenScripts
what kind of object it is and if the result is boolean
(true or false), then it sets an error number, otherwise, it does nothing.
Note there are actually two classes being used here - boolean
and number
. AppleScript Editor provides handy syntax highlighting so you can easily identify built-in class names.
Using the filesystem
Often in AppleScript, you'll want to tell an app to access an object on disk. There are several built-in objects for doing this, among them folder
, file
, and path
.
There are also several built-in modifiers for standard locations in the macOS filesystem - for example the user folder or special folders such as Folder Action scripts:
set UserScripts to list folder (path to Folder Action scripts folder from user domain) without invisibles
The path to
construct in parenthesis shown above tells AppleScript to evaluate and resolve that expression first as a path to a folder, then process the rest of the line. Usually path to
expressions get reduced to a single filesystem path, then passed to some other command for processing.
In the above example we're getting the path to the Folder Action scripts folder in the user folder, then storing its contents as a list in the variable UserScripts
, excluding any invisible files.
Once set, the variable UserScripts
can then be used anywhere else in the script to indicate the list of items in the folder at that path on disk.
Comments
In AppleScript, you can add single-line or multi-line comments, which are ignored by the compiler.
Single-line comments are indicated by a double-dash (--
) at the start.
Multi-line, or block comments are bracketed by (*
and *)
. For example:
.(*
Get User Name
This script uses UI element scripting to get the name of the current user.
Copyright 2013 Apple Inc.
*)
Single-line comments can go just about anywhere in a script - on lines by themselves, or at the end of lines.
Saving AppleScripts
Currently, Script Editor supports saving scripts in four formats:
- Script
- Script bundle
- Application
- Text
'Script' saves an AppleScript in Script Editor's native uncompiled format.
'Script bundle' saves an AppleScript in Script Editor's native format but can include additional resources such as external files.
'Application' saves an AppleScript into a precompiled executable format which can be double-clicked in Finder or launched by another process without Script Editor.
'Text' saves a script as plain text which can be opened with any text editor.
To save an AppleScript, press Command-S on the keyboard, enter a name, select a File Format from the popup menu, and click the "Save" button.
The Save panel.
If you selected Application from the File Format popup when saving, two additional checkboxes are enabled: Show startup screen, and Stay open after run handler.
Selecting the first checkbox causes the script to show a run confirmation window on launch, and selecting the second checkbox forces the script to stay open after the run handler completes.
Application options.
If you hold down the Option key on the keyboard and select File->Save from the menu bar, the Save command changes to Save As which allows you to save a new copy of the script under another name.
Putting it all together
Now let's take a look at a sample script and analyze it line by line. We'll use the built-in Probe Menu Bar script found in the /Library/Scripts/UI Element Scripts folder on the Startup Disk in macOS.
This script starts with a try and end try
block. try
is a keyword that allows you to run a block of code and trap for an error if one occurs. try
blocks must end with end try
.
You can nest try
blocks to control program flow.
To handle an error in a try block, use the on error
syntax as used in this script. on error
always goes after the try
line but before the end try
line:
on error errMsg
display dialog "Error: " & errMsg
In this example, if an error occurs, the script displays a simple dialog with the message "Error: " followed by the contents of the variable errMsg
.
Next in the script, we see three nested tell
blocks which each have an end tell
line at the end of each block. You use tell
to direct a block of code to some object - in this case, the system handler, but it can also be another app, a filesystem object, some UI element in an app, or even data.
In this example the first tell
block tells the system to get all available properties, then to get a list of all running processes:
tell application "System Events"
get properties
get every process
In the next tell
block the script tells Finder to get all the menu bars of all running apps, then to iterate each menu bar and get every item contained in every menu in all the menu bars. This gets quite redundant:
tell process "Finder"
get every menu bar
tell menu bar 1
get every menu bar item
get every menu of every menu bar item
get every menu item of every menu of every menu bar item
get every menu of every menu item of every menu of every menu bar item
get every menu item of every menu of every menu item of every menu of every menu bar item
end tell
end tell
From this code, you'll soon realize one of the strangest aspects about AppleScript programming: namely that when you want to access an object you must first consider the most distant or outermost object, then work your way back from there.
This is the reverse of the way most object-oriented programming languages work in which you start with the top object and dive down from there to access its members.
This syntax can often make AppleScript annoying since it's usually the reverse logic of the thing you're trying to get at.
Finally, the script displays a simple dialog box with a message:
display dialog "The 'Probe Menu Bar' script was executed, it uses UI element scripting to return a list of every menuitem
of every menu for every application currently running, to see results run it within AppleScript Editor."
For reference, here's the full complete script listing:
(*
Probe Menu Bar
This script uses UI element scripting to return a list of every menuitem
of every menu for every application currently running.
Copyright 2013 Apple Inc.
You may incorporate this Apple sample code into your program(s) without
restriction. This Apple sample code has been provided "AS IS" and the
responsibility for its operation is yours. You are not permitted to
redistribute this Apple sample code as "Apple sample code" after having
made changes. If you're going to redistribute the code, we require
that you make it clear that the code was descended from Apple sample
code, but that you've made changes.
*)
try
tell application "System Events"
get properties
get every process
tell process "Finder"
get every menu bar
tell menu bar 1
get every menu bar item
get every menu of every menu bar item
get every menu item of every menu of every menu bar item
get every menu of every menu item of every menu of every menu bar item
get every menu item of every menu of every menu item of every menu of every menu bar item
end tell
end tell
display dialog "The 'Probe Menu Bar' script was executed, it uses UI element scripting to return a list of every menuitem
of every menu for every application currently running, to see results run it within AppleScript Editor."
end tell
on error errMsg
display dialog "Error: " & errMsg
end try
Logs
After you run your script, if you click the tiny list-like icon in the lower-left corner of the window, two additional icons appear on the pane separator on the right side of the window: a trash can, and a clock. If you click the clock icon, a log window appears which contains any log messages while the script was running - unless you've turned logging off in the Settings window:
Log History window.
You can click on each item in the log window on the left to see details about each message or event which got logged.
We've barely scratched the surface of AppleScript here. In future articles, we'll dive more in-depth into syntax, special keywords, terminology, recording, scripting addition plug-ins, third-party tools, and Apple's other automation app, Automator.