Launch By Creator or Identifier

Launch By Creator or Identifier

AppleScript is not as intelligent as it could be about running an application with the standard tell application statement. This tip describes a problem you may encounter when attempting to run your scripts on another computer, different from the computer on which you compiled the scripts, and it explores a solution.

In AppleScript, the target application is ordinarily specified by name; for example, tell application “TextEdit” to run. In fact, you don’t need to include the run command. The opening line of a tell block addressed to an application by name automatically runs the target application. But a problem arises when the target application has a different name on another computer.

The Problem

Here’s the problem: Using the application’s name can lead to unexpected difficulties if”as often happens”the script is installed on another computer where the name of the target application has been changed. For example, many users are in the habit of renaming an application like “TextEdit” to something like “TextEdit 1.4” or application “Adobe Photoshop CS2” to “Photoshop”. Typically, in this situation, AppleScript presents the standard Choose Application dialog asking “Where is TextEdit?” the first time the script is run on a computer where the target application’s name is different from the name used in the script. Worse, on Mac OS X, AppleScript spends several seconds gathering a list of all scriptable applications, in order to list them in the dialog. This is, at best, an annoyance, and it may prevent inexperienced users from using your script. This problem has led many scripters to search for a solution.

When you compile a script using the name of the target application as it exists on the compiling computer, AppleScript has no trouble finding the application’s dictionary and compiling the script. Of course, if you misspell the name, the compiler must ask you to locate the target application using the Choose Application dialog. Once you locate the application, however, the compiler automatically substitutes the correctly-spelled name in your script and goes on about its business. When you run the script on the same computer again later, it runs the target application as expected, without asking you where it is, because this script now has its correct name. AppleScript is even smart enough to run the target application if you change its name after compiling the script, so long as you are working on the same computer on which the script was originally compiled.

Unfortunately, if you then install the script onto another computer and the target application there has been given a different name, you’ll be right back where you started: the script will once again ask you to locate the target application. This shouldn’t happen, because AppleScript should know when it is first compiled that the target application has the same unique creator or identifier on both computers. Threads on the AppleScript mailing lists crop up periodically that provide one or another workaround for this problem. This tip and its companion tip are a distillation and expansion of those discussions.

The Solution

You can avoid the problem by using the target application’s creator code or bundle identifier, instead of its name, to run it, with the help of the Finder.

The creator is a four-character code that uniquely identifies an application; for example, “ttxt” for TextEdit. Creator codes have been around since the beginning of the Macintosh.

As time goes by, however, more and more applications will dispense with a creator code and rely instead on a bundle identifier, a new feature introduced with Mac OS X. An identifier looks something like an Internet domain name in reverse, such as “com.apple.textedit”. It works better than a creator code, because it includes the name of the developer (here, “apple”) and the name of the application (here, “textedit”). It won’t be necessary for Apple to maintain a registry of unique bundle identifiers, as it does for creator codes, because each developer’s trademarked business name is part of the identifier, and the developer can easily avoid duplicate application names within the developer’s own domain.

The trick is to tell the scriptable Finder to run the target application by opening its file using its application file id, which can be a creator code or, in Mac OS X, a bundle identifier. The application will always have one or the other, if not both. Both of these commands run TextEdit:


tell application "Finder" to open application file id "ttxt"

or:


tell application "Finder" to open application file id "com.apple.TextEdit"

If you don’t know the creator code or bundle identifier of an application, get it on your own computer before you write the script, like this:


get file creator of (info for (path to application "TextEdit"))

or:


get bundle identifier of (info for (path to application "TextEdit"))

The bundle identifier property of the file information record returned by the info for command became available in Mac OS X 10.3 (Panther).

Opening an appliction file by its id, as shown above, is equivalent to this:


tell application "Finder"
   get application file id "ttxt"
   open result
end tell

where get application file id “ttxt” returns a reference in Finder reference form, such as file “TextEdit.app” of folder “My Folder” of startup disk of application “Finder”. When the Finder opens the target application’s file, the application runs. Running an application in this manner will work on any computer where the target application is installed, no matter what name the target application has been given on that computer, without generating an error or asking the user to locate the application. It works because the script got the full path of the target application, including its actual name, as it exists on the run-time computer.

It is necessary to tell the Finder to open the application’s file by using a full reference to the file, because the Finder’s open command requires a reference in its direct parameter. Trying to open it by using the term application followed by the application’s name alone, as you would do in a tell statement, doesn’t work. The following script fails in line 3 for this reason:


tell application "Finder"
   get name of application file id "ttxt"
   open application result --error
end tell

Telling the Finder to open an application file by its full path obtained using its creator or identifier may, for a particular application, be a complete solution to the problem of running it on another computer where its name may have been changed. If it works for your script, you can stop here.

Problems with the Solution

There remain some problems, however, with respect to many applications. Here they are, together with their solutions.

Launch, Don’t Run

For one thing, when the Finder opens an application file, it runs the application rather than launching it. Some applications, when run, automatically ask the user for information, and this prevents unattended operation by script. For example, FileMaker Pro might present a dialog asking whether you want to open an existing file. Furthermore, most applications”including TextEdit”follow Apple’s human interface guidelines by opening an empty, untitled document, which very likely is not the script writer’s intent. While some applications allow you to override these behaviors with preference settings, many applications do not”and you can’t be sure of your users’ preference settings on other computers, in any event.

The first step toward a universal solution is to go back to the realization that you can tell AppleScript to run a target application, as opposed to telling the Finder to open it. If AppleScript can run an application, it can also launch it, and the launch command has the advantage of not opening blank documents or asking for information. It’s just what we want. AppleScript’s run and launch commands take an application reference, not a path reference as the Finder’s open command requires. An application reference requires only the application’s name, not its path, so you’ll obtain its name on the run-time computer from the Finder.

We’ll start by using the run command, like this:


tell application "Finder" to get name of application file id "ttxt"
run application result

or use its equivalent with an explicit tell statement:


tell application "Finder" to get name of application file id "ttxt"
tell application result to run

This technique works on current versions of Mac OS X, but it might have problems on older versions of the Mac OS. If either version of this script is saved as a script application and run on older versions of the Mac OS, it may still ask the user to locate the target application the first time it is run”even on the computer on which it was compiled”because the script wasn’t given the application’s name at compile time. This is even worse than the problem we set out to avoid in the first place. On more recent versions of the Mac OS, however, this script runs the target application without asking, because it always uses the name of the target application as it exists on the run-time computer.

In older versions of the Mac OS, you can avoid this side issue by telling the Finder to obtain the application’s full path from its creator, not just its name, then to tell AppleScript to run that path. There will be no difficulty locating the application on another computer, because the path will have been generated at run time on that machine. There are a couple of special requirements to make this work, however. First, since AppleScript does not understand the Finder reference form, you must tell the Finder to return the path as a string by using an as string coercion. Second, you should bracket the run command in a try block to trap and ignore error number -1708; otherwise, you may get an error because some applications don’t respond to the run command when given a full path.


tell application "Finder" to set appPath to application file id "ttxt" as string
try
   run application appPath
on error number -1708
  -- ignore error
end try

Better yet”this is almost the ultimate solution”you can tell AppleScript to launch the application instead of running it. Using the launch command avoids the problem of some applications opening an untitled document, asking what file to open, or presenting other dialogs. (Unfortunately, there are some applications that do not follow Apple’s recommendation to suppress dialogs when launching; we have no solution for those, other than to include commands in your script to close whatever windows or dialogs are automatically generated.)


tell application "Finder" to set appName to name of application file id "ttxt" as string
launch application appName

In summary, telling the Finder to get an application file’s full path from its creator or identifier, as a string, then telling AppleScript to launch that path, is a simple way to ensure that a user will not receive fatal errors or annoying questions when launching an application by script. (You might want to consider trapping for error number -1728, which is returned by the Finder if an application with the specified creator is not present.)

Recognizing the Target Application’s Dictionary

But we are not yet finished. Since you are writing a script, you no doubt want to tell the target application to do something more interesting than just sit there. To do more with it, you normally need to use its name in a tell application statement. As noted above, the scriptable Finder can provide the name to you at run time and, once you have the application running, you can use its name in a variable to tell the application to jump through a number of hoops. For a simple example, you can tell it to quit:


tell application "Finder" to set appName to name of application file id "com.apple.textedit"
launch application appName
tell application appName to quit

Unfortunately, although this technique works, it is severely limited. It will only work with commands, like quit, that are part of the general AppleScript language, or global commands implemented in scripting additions that are always available to the compiler and to any script. Even then, a script using this technique will execute only the general form of the command, rather than an application-specific form that was designed to do something slightly different. More importantly, it will not let you compile, let alone run, a script that uses commands that are unique to the target application.

These limitations are encountered because the compiler does not know where to find the target application’s dictionary when the tell block is used with a variable instead of a string constant representing the application’s name.

For example, this script causes the Finder to open a window on the startup disk, using the Finder’s reveal command. It works when the tell block uses the Finder’s name, like this:


tell application "Finder" to reveal startup disk

However, the same script written to tell a variable holding the name of the Finder does not work. In fact, it may not even compile. The following example doesn’t launch the Finder because we assume it is always running. The script will not compile, however, because it can’t find the Finder’ s dictionary in order to know what a startup disk is.


tell application "Finder" to set appName to name of application file id "com.apple.finder"
tell application appName to reveal startup disk

It was partly in order to solve this last remaining problem that Apple introduced the using terms from statement in Mac OS 9.0. By enclosing all application-specific commands in a using terms from block addressed to the target application by name, the compiler is able to access the application’s dictionary on the compiling computer to build the executable script. When the script is installed on another computer, it already has this information and can run properly without restriction even though the tell block uses a variable to supply the application’s name. Note that the using terms from statement must use the target application’s actual name on the compiling computer, as a string constant, for this to work. That isn’t a problem, because you, the scripter, know exactly what names you’ve given to the applications on your own computer. The using terms from block is ignored on the run time computer. Here’s a script that works anywhere no matter what name was given to the target application on another computer:


tell application "Finder" to set appName to name of application file id "com.apple.finder"
tell application appName
   using terms from application "Finder"
      reveal startup disk
   end using terms from
end tell

For more details about using variables in tell blocks, turn to the Tell By Variable tip.