The thumbnail is decorative. A blue card with Logical Developments logo in the top left corner on a white background. The Logical Developments logo is a diamond with the letters L and D inside of it. The letters are offset from each other vertically while also overlapping horizontally. The each sit inside of their own rectangle. The thumbnail has the title printed on it - "Apple Automation: Jordan learns AppleScript". Just underneath the title and to it's left is a picture of a document that has it's top slightly rolled forward, and it's bottom is slightly rolled backward. On the document are a pair of less than and greater than signs to signify the opening of a script. After some white space there is another pair of less than and greater than signs with a forward slash inside to signify the end of the script.

Apple Automation: Jordan learns AppleScript

Jordan Bock - Aug 16, 2021

Designed for MacOS, AppleScript is a high level sentence based scripting language. It is designed to write a sentence and convert it to working code.

This makes readability cleaner and the code a little easier to understand how it works. AppleScript is a strict language that requires precisely written sentences (that may not be grammatically correct).

AppleScript is Apple’s automation language for MacOS. It is designed to facilitate automated control over scriptable applications. This means AppleScript allows for us to program operations to work based on elements that are relevant for each unique application. It also allows us to create automated scripts to function between applications we might otherwise be unable to communicate with.

By providing a method to interact with applications in this way, learning (or at least understanding), AppleScript enables further options to utilise applications than I would have been otherwise aware of. I have learned to compress, resize or change the extension on images. Interacting with Microsoft Word (MS Word) documents, I learned how to extract the text. Additionally, I worked out how to simulate a user clicking different options in both the menu and in browsers.

I learned, or at least came to understand, AppleScript through a few different methods. First I looked into the AppleScript documentation, before searching for existing solutions that might already accomplish my desired goal. I took my findings and, through trial and error, I made modifications to produce the desired result.

Understanding the basic concepts of programming is vital for quickly learning a language and knowing how different forms of languages may work. By applying my basic understanding of programming languages I was able to work out potential flows and keywords I might be looking for. From that I could construct my code in a way that would work if I had the right syntax (keywords). Once I had learned the syntax, it was time to test everything – ensuring it all works as intended.

Currently there are three projects I’ve used AppleScript with to achieve different goals. I first used AppleScript to interact with MS Word. Learning how to extract text, find formats, get only the first word on every page, get page references and get footnotes. These were not all as simple as they sound.

To get the first word was not as simple as saying get “word 1 of page X” since MS Word does not understand the concept of pages for AppleScript. To get the first word extraction working I started with a Visual Basic Application (VBA) code to retrieve my desired output and tried to make AppleScript run it. After testing with no success, I turned to Stack Overflow for assistance.

NOTE: that ‘--' is used to comment and [] to note unique file paths.

Here is the code required to find the first word:


use AppleScript version '2.5'
use scripting additions
use framework 'Foundation'

to trimText:inText
    set _str to current application's NSString's stringWithString:inText
    set _whitespace to current application's NSCharacterSet's whitespaceAndNewlineCharacterSet()
    set _str to _str's stringByTrimmingCharactersInSet:_whitespace
    return _str as text
end trimText:

on run
    set wordList to {}
    set lastPage to false
    tell application 'Microsoft Word'
      tell active document
        set firstWord to word 1
        repeat until lastPage
          set pageWord to my trimText:(content of firstWord)
          if pageWord ≠ '' then set end of wordList to pageWord
          set nextPage to go to next firstWord what goto a page item
          if (start of content of nextPage) is not equal to (start of content of firstWord) then
            set firstWord to word 1 of nextPage
          else
            set lastPage to true
          end if
        end repeat
      end tell
    end tell
    if (count of wordList) is greater than 0 then
      set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}
      set the clipboard to (wordList as text)
      set AppleScript's text item delimiters to oldDelims
    end if
end run


Now to break down what this code does. A good practice is to understand code you get from sources like Stack Overflow before you apply it. Otherwise it might have dangerous consequences.


use AppleScript version '2.5'
use scripting additions
use framework 'Foundation'


The first 3 lines are used to specify exactly what environment conditions we want. While this is not always required it does make sure we can confirm the code will work regardless of updates to the language


to trimText:inText
  set _str to current application's NSString's stringWithString:inText
  set _whitespace to current application's NSCharacterSet's whitespaceAndNewlineCharacterSet()
  set _str to _str's stringByTrimmingCharactersInSet:_whitespace
  return _str as text
end trimText:


This segment is used to define a function, trimText, that we can call in the body of the script. The function is used to strip white spaces and newlines from a defined string. After this we have an ‘on run’ line, this tells the script run the main body of code. The function is predefined and needs to be created before the body so AppleScript knows it exists. In the main body we can then call the function.

on run
  tell active document
  set firstWord to word 1
    repeat until lastPage
      set pageWord to my trimText:(content of firstWord)
      if pageWord ≠ '' then set end of wordList to pageWord
      set nextPage to go to next firstWord what goto a page item
      if (start of content of nextPage) is not equal to (start of content of firstWord) then
      set firstWord to word 1 of nextPage
      else
        set lastPage to true
      end if
    end repeat
  end tell
.
.
.
end run


This section is the main body of the code. To begin with, tell MS Word we are interacting with the active document window. The script tells the document to loop until the variable lastPage is set to true. First, grab the starting word of the page and trim it. Check if the word is empty. If it is not then we add it to the end of a list of first words. We then go to the next first word in nextPage using goto a page item to tell word to go to the next page. We then compare the previous first word with the new one. If they are the same, we then assume there is no next page and hence we set lastPage to true and end the loop. Otherwise we set firstWord to the new first word and continue the loop.

if (count of wordList) is greater than 0 then
  set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}
  set the clipboard to (wordList as text)
  set AppleScript's text item delimiters to oldDelims
end if


The last section takes the wordList and changes the delimiters for easier use. Essentially a delimiter is what is used to break up text. In this code we record the current delimiters, change the delimiters to a line feed and then copy it (put it on the clipboard). Finally we restore the delimiters.

The line:

set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}


is actually running two lines in one and can be understood easier if we write it as follows:

set oldDelims to AppleScript's text item delimiters -- save their current state
set AppleScript's text item delimiters to linefeed -- declare new delimiters


By writing it this way we can see that we first copy the current delimiter and then change it to a new one. This results in a list where each word is put on a new line where it may otherwise be separated by any character.

I have also used AppleScript to manipulate images. There was a task to compress images to make the files smaller. In the process of doing this I learned a few useful commands. To modify images, instead of communicating with the photo viewer, AppleScript provides access to Image Events. Image Events are a set of commands that modify images without requiring to boot up a new application.

The following AppleScript is used to scale the image and convert it to PNG:

tell application 'Image Events'
  -- start the Image Events application
  launch
  -- open the image file
  set this_image to open file [current file location]
  -- perform action
  scale this_image by factor 0.25
  -- save the changes
  save this_image AS PNG in [save destination]
  -- purge the open image data
  close this_image
end tell


I found this script at macosxautomation.com, but the scale factor was 50% and saved an icon. I modified the code to use a different factor and change the save to enforce an image type and new destination.

Looking at the comments, we first start the image events – opening a file using a defined path. Next scale the image to 25% of it’s original size without changing the resolution. We then change it to PNG and define a new location using a path, similar to running “Save As”. Finish by closing the image.

The final way I’ve used AppleScript is to interact with a website without having a scripting interface. For this example I had to make it possible to end a Facebook Messenger call using AppleScript. This required learning how to interact with HTML buttons by their ID. Essentially I worked out how to reference the buttons using their number and then found the correct number to end the call. From this I was also able to map out other functions as well.

to clicktagName(thetagName, elementnum) -- define function
  tell application 'Google Chrome' -- interact with application
    tell window 'Messenger Call' -- reference the window
      tell active tab -- reference the tab
        execute javascript 'document.getElementsByTagName('` & thetagName & `')[` & elementnum & `].click();' -- Define the javascript to run
      end tell -- end tab tell
    end tell -- end window tell
  end tell -- end application tell
end clicktagName -- end function and return
clicktagName('button', 8) -- End call when chat is closed
-- clicktagName('button', 0) - Close chat

Let’s see how this works, the comments describe most of the actions, but let’s break this down further.

to clicktagName(thetagName, elementnum) -- define function

First we create a function with 2 parameters. These being thetagName and elementnum.

tell application 'Google Chrome' -- interact with application
  tell window 'Messenger Call' -- reference the window
    tell active tab -- reference the tab


We then inform Google Chrome to send instructions to a window labelled Messenger Call and then the active tab. To actually perform functions we then execute JavaScript. JavaScript is a web language understood by the browser and can interact with both HTML and CSS.

execute javascript 'document.getElementsByTagName('` & thetagName & `')[' & elementnum & '].click();' -- Define the javascript to run


Use getElementsByTagName to fetch the defined tag with the defined number or instance and to click the matching element.

      end tell -- end tab tell
    end tell -- end window tell
  end tell -- end application tell
end clicktagName -- end function and return
clicktagName(`button`, 8) -- End call when chat is closed
-- clicktagName('button', 0) - Close chat


After ending the tell and function we then call the function with the parameters of “button” and “8”. This then runs the function and tells the browser to simulate a click on the 8th button. In this case if chat is closed it will be end call. We can use this to simulate different button presses and interact with different elements if we know the TagName.

An important aspect of this example is the use of JavaScript. Using JavaScript allows for updating and changing HTML and CSS. JavaScript can also calculate, manipulate and validate data. This means JavaScript is perfect for creating and/or controlling website content. We can use it to create web automation.

From my studies it seems that Apple is slowly losing interest in AppleScript, particularly since the inclusion of iOS’s Shortcuts in MacOS, and its uses are dwindling. While all Mac devices are still packaged with AppleScript not every new application is creating scripting interfaces to interact with AppleScript.

Unless I’m working with an application I know can use AppleScript it is likely I will learn other ways to interact between different applications. While providing useful functionality, AppleScript only works for Apple computers (hence the name) and so when it comes to any other type of device, AppleScript cannot be used and again I’ll need to find other methods to achieve the same results.

Until it officially goes away, AppleScript is an interesting and useful way to implement additional functionality through scripting across MacOS devices.