Chapter 4. Basic Concepts

This chapter explains how AppleScript works. Its purpose is to provide you with a mental picture of what’s really going on when you use AppleScript. It also acts as a kind of glossary, defining the basic terms and concepts needed for an understanding of AppleScript, but the concepts are presented in an expository order rather than alphabetically.

All subsequent chapters will presuppose some familiarity with the terms and concepts presented in this chapter. You don’t have to grasp everything immediately, and you don’t need to read every section with equal care. But you should at least skim this chapter in order to get your conceptual bearings. You can always come back later and reread a section if you need a refresher on the details.

Apple Event

Apple events lie at the heart of what AppleScript is and why you’re going to use it, and having a sense of what they are will be of tremendous help to you in your AppleScript adventures.

Apple events are the Macintosh’s system-level way of letting one running application communicate with another. Such communication is called interapplication communication . Apple events were introduced in 1991 as part of System 7. I refer to the two parties in an interapplication communication as the sender (the application that sends the message) and the target (the application that receives the message); I find this clearer and more instructive than the more technical terms “client” and “server.”

An Apple event is an astonishingly powerful thing. Hermes-like, it crosses borders. Two completely independent applications are talking to each other. What’s more, Apple events work across a network, including the Internet, so these two applications can be on different computers. Or it can be the opposite; an application can send an Apple event to itself. (Why would it want to do that? You’ll find out, in Section 4.9.2, later in this chapter.)

Moreover, the range of what may be expressed in an Apple event is remarkably broad. Apple events actually have a kind of grammar: there are (so to speak) verbs and nouns and modifiers, and these are so cleverly and flexibly devised that single Apple events can be constructed to say surprisingly complicated things, such as (speaking to a word processing program), “Look at the text of your first window and give me a reference to every line of it whose second word begins with the letter t,” or (speaking to an email program), “Look in the mailbox where the incoming mail is, find the first mail message with a subject that starts with the word `applescript', and move it into the `AppleScript’ mailbox.”

Command, Query, and Reply

An interapplication communication can be thought of as either a command or a query. There is no real technical distinction here; either way it’s the same kind of message. But as human beings we naturally tend to feel that these are broadly the reasons for sending an interapplication communication: either we tell the target to do something or we ask the target a question. In either case, there will be a reply . The reply to an Apple event is itself an Apple event.

You might think that if an interapplication communication is a command, there wouldn’t need to be a reply. But that’s not so. Apple events tend to use the reply to hand back useful information; for example, you saw in Chapter 3 that telling FrameMaker to find, which sounds like a command, also nets us a reference to what was found. What’s more, even if the sender couldn’t care less about the content of the reply, the reply itself is still important. Remember, these two applications are running independently, so they have to be coordinated somehow if they are to interact coherently. The sender, having sent a command to the target, typically doesn’t want to proceed to its own next step until the target has finished obeying that command. The reply informs the sender that the command has been carried out (or not, if an error has occurred).

When two independently running applications communicate with each other, things can go wrong. The sender sends a message to the target, and then what? The target application might try to obey the message, and crash. It might obey the message, but require a great deal of time to do so. It might be busy or otherwise not in a position to receive the message. The sender needs a way to hedge his bets in order to cope with such possibilities. Apple events provide some bet-hedging mechanisms.

  • The sender may attach to the message a timeout value, a statement of how long he is willing to wait for an answer. If a reply doesn’t come back within the specified time, the sender receives a reply anyway—a reply saying that, for one reason or another, no reply came back in time. This can permit the sender to proceed to the next step. (Meanwhile the target is probably still performing his time-consuming task, blissfully unaware that the sender has lost interest.)

  • The sender may specify that he isn’t interested in the reply at all: he doesn’t care about its value (which implies that this is a command, not a query); he doesn’t care to know even whether there is a reply, or whether the command was carried out. In this case the sender does not wait; the message is sent, and the sender immediately proceeds to the next step of his own process. The sender will never find out in any direct way what became of the Apple event. This devil-may-care approach is rather rarely used, but there are times when it comes in very handy.

Scriptability

Not just any old Apple event can be sent to any old application. Well, it can, but the result could easily be an error message instead of the desired result. The target application needs to have been written in the first place in such a way that the particular Apple event you send is one to which it is prepared to respond. Such an application defines internally a repertory of Apple events that it understands. The application is then said to be scriptable.

A given scriptable application’s repertory of acceptable Apple events doesn’t necessarily resemble that of any other scriptable application. This presents something of a problem for the sender, since every possible target application is picky in a different way about what can be said to it. This problem washes over into AppleScript, and is in fact one of the single greatest challenges facing the AppleScript programmer. (You already saw this in Chapter 3.)

The knowledge of what Apple events a scriptable application can respond to, and what it will do in response to them, is an implicit fact built into its workings, not an explicit fact written somehow on its face. How, then, is it possible to know what a scriptable application’s repertory is? Some secondary device is clearly needed to expose this information. In the AppleScript world, this device is the application’s dictionary, which is a kind of built-in public document describing the application’s repertory. There is a section about dictionaries later in this chapter, and an entire chapter devoted to them later in the book (Chapter 19).

The Life of an Apple Event

There’s obviously more to the story of interapplication communications than just the sender application and the target application. For example, earlier it was said that the sender normally receives a reply even if the target isn’t even listening. How is that possible? It’s possible because the System itself functions as the intermediary through which all interapplication communications happen. The sender doesn’t speak directly to the target, but to the System. It is the System that is responsible for passing the message on to the target, and for letting the sender know how things went.

Figure 4-1 shows in more detail the process whereby an Apple event is sent and a reply is returned.

Life of an Apple event

Figure 4-1. Life of an Apple event

  1. The sender application (on the left of the figure) constructs the Apple event. The Apple event is rather like a letter inside an envelope that you post in the mail. It has information about how it is to be directed—who the target application is, and whether the sender intends to wait around for the reply, and if so, what the timeout value is. This information is intended for the System, and is rather like the stuff that goes on the outside of the envelope. Then there is the content—the details as to what kind of Apple event this is and the particular data that it involves. This information is intended for the target application, and is rather like the letter that’s inside the envelope.

  2. The sender application calls the System (in the middle of the figure) and hands it the Apple event. The System, rather like the postal service, examines the Apple event and looks at the information about how it is to be directed. Using this information, the System tries to locate the target application. Let’s presume that it succeeds in doing this.

  3. The target application (on the right of the figure) is portrayed as having a repertory of Apple events to which it is prepared to respond. These Apple events are listed using pairs of four-letter codes. (Apple events really are identified by pairs of four-letter codes, as explained in Chapter 19, and the Apple events listed in the diagram are genuine, common Apple events.)

  4. The System calls the target application, handing it the Apple event supplied by the sender. The System also attaches to this Apple event a reply Apple event. It is rather as if, when the post office delivers a letter to you, it were to provide a stamped addressed envelope for you to put your reply into. The System holds out this reply event to the target application, but doesn’t let go of it.

  5. The target application does whatever the Apple event tells it to do, and puts the result into the reply event. There are two parts to this result. First, the target application must return a value signifying whether or not things succeeded. Second, the target application may put into the reply any other information to be returned. If there was an error, it can put in a text message describing the problem. If things succeeded and a result is expected, it can put in that result.

  6. The target application now signs off, and the System is left holding the reply Apple event (which, as we said, it never let go of ). The System now delivers the reply Apple event to the sender application, and the story ends.

What an Apple Event Looks Like

By now the reader is probably eager to see an Apple event. What does one look like? Actually, an Apple event was never meant for human eyes. It is meant to be machine-constructible and machine-parsable. It doesn’t really even have, in a strict sense, any appearance at all. Nevertheless, as a kind of linguistic shortcut for expressing and understanding Apple events, there is a textual format called AEPrint that shows you what an Apple event looks like. Example 4-1 displays, in AEPrint format, the second of the two Apple events I mentioned earlier, the one addressed to a mail program.

Example 4-1. A raw Apple event

        core\move{ 
                insh:insl{ 
                        kobj:obj { 
                                form:'name', 
                                want:'Mbox', 
                                seld:"appleScript", 
                                from:'null'(  ) 
                        }, 
                        kpos:'end ' 
                }, 
                ----:obj { 
                        form:'indx', 
                        want:'cobj', 
                        seld:1, 
                        from:obj { 
                                form:'test', 
                                want:'msg ', 
                                from:obj { 
                                        form:'prop', 
                                        want:'prop', 
                                        seld:'unBX', 
                                        from:'null'(  ) 
                                }, 
                                seld:cmpd{ 
                                        relo:'bgwt', 
                                        obj1:obj { 
                                                form:'prop', 
                                                want:'prop', 
                                                seld:'subj', 
                                                from:exmn($$) 
                                        }, 
                                        obj2:"applescript" 
                                } 
                        } 
                } 
        }

Go and Catch an Apple Event

We have seen that the System plays the central role of postman whenever an Apple event is sent. Now imagine that Apple events are secret messages, and that we are international spies who would like to get a look at them when they are sent. In effect, we would like to waylay the postman, bonk him over the head, snatch the letter out of his hand, and glance at its contents. It turns out that there is a way to do this.

Here’s how. First, open the Console; that’s where any Apple events are going to be reported to us. Next, go into the Terminal and enter the following:

$ setenv AEDebug 1
$ setenv AEDebugSends 1
$ setenv AEDebugReceives 1

Or, in bash (the default shell in Panther), you’d say:

$ export AEDebug=1
$ export AEDebugSends=1
$ export AEDebugReceives=1

This turns on the environment settings that cause Apple events to be intercepted and reported. These settings will apply only within this shell session, and only with respect to applications that are launched from within this process. So, let’s launch one:

$ open /Applications/Safari.app

Now any Apple events sent to Safari will be logged. Let’s send one:

$ open http://www.apple.com

(I’m assuming here that Safari is your default browser.) This causes two things to happen. First, within the Terminal, the process started by the open command sends an Apple event, and this fact is reported within the Terminal. Second, Safari receives this Apple event, and this fact is reported within the Console. The two Apple events are exactly the same event, so there’s no point examining both of them—here it is as it’s reported in the Console, when Safari receives it:

AE2000 (556): Received an event:
------oo start of event oo------
{ 1 } 'aevt':  GURL/GURL {
          return id: 38666240 (0x24e0000)
     transaction id: 0 (0x0)
  interaction level: 112 (0x70)
     reply required: 0 (0x0)
  target:
    { 1 } 'psn ':  8 bytes {
      { 0x0, 0x3e0001 } (open)
    }
  optional attributes:
    < empty record >
  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' - 
        { 1 } 'TEXT':  20 bytes {
          "http://www.apple.com"
        }
    }
}

------oo  end of event  oo------

This is quite a bit more verbose than simple AEPrint format, and more informative. I won’t analyze it for you, but you can see immediately that the first half is information about the Apple event (its identifier, its target, whether a reply is expected, that sort of thing) and the second half is the content of the Apple event (the actual URL that Safari was asked to open).

Let’s do another. In the Terminal, say this:

$ osascript -e 'tell app "Finder" to get disks'

(The osascript command was mentioned under Section 2.7 and is formally discussed in Chapter 23.) This causes the Terminal to spew out large amounts of information. The bulk of this information has to do with the fact that we’ve just asked the Terminal to compile and run a line of AppleScript, and may be ignored here. The important thing for our purposes is the Apple event that is ultimately sent to the Finder. You’ll see it, along with the Finder’s reply, at the very end of the output. Here it is:

AE2000 (811): Sending an event:
------oo start of event oo------
{ 1 } 'aevt':  core/getd {
          return id: 53149700 (0x32b0004)
     transaction id: 0 (0x0)
  interaction level: 64 (0x40)
     reply required: 1 (0x1)
  target:
    { 2 } 'psn ':  8 bytes {
      { 0x0, 0xc0001 } (Finder)
    }
  optional attributes:
    { 1 } 'reco':  - 1 items {
      key 'csig' - 
        { 1 } 'magn':  4 bytes {
          65536l (0x10000)
        }
    }

  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' - 
        { 1 } 'obj ':  - 4 items {
          key 'form' - 
            { 1 } 'enum':  4 bytes {
              'indx'
            }
          key 'want' - 
            { 1 } 'type':  4 bytes {
              'cdis'
            }
          key 'seld' - 
            { 1 } 'abso':  4 bytes {
              'all '
            }
          key 'from' - 
            { 4 } 'null':  null descriptor
        }
    }
}

------oo  end of event  oo------

Without going into the details of this Apple event, you can once again recognize its two main constituent parts.

What All This Has to Do with AppleScript

A raw Apple event, as portrayed in the preceding sections, is not by any means completely incomprehensible to a human being. Nevertheless, you’ll surely admit that if raw format were the only available way of expressing Apple events, your attitude would probably be: “In that case, forget it.” And rightly so. Raw Apple events are meant primarily for computers to construct and to read, not for humans.

But Apple wants ordinary users to be able to take direct advantage of Apple events. They want Apple events to be human-readable and human-writable. And that’s why there’s AppleScript. The very same Apple event seen in Example 4-1 can be constructed and presented in a different textual form, one which looks rather more familiar, intuitive, and accessible to a human being:

move item 1 of (every message of incoming mail ¬
        whose subject begins with "applescript") ¬
        to end of mailbox "appleScript"

That’s AppleScript. One of the chief purposes of AppleScript—perhaps the chief purpose—is to provide an English-like way of expressing Apple events.

Get AppleScript: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.