Code by Kevin
   


About
Code by Kevin: Programming, code, business, and other pursuits

Your Host
Kevin Walzer, software developer.

Home

Subscribe to RSS Feed
Get a syndicated feed of my weblog.

Archives
2012
2011
2010
2009
2008
2007
2006

Categories
Business
Software
General

        home
Wed, 24 Nov 2010

Adding AppleScript support to my apps

One ongoing project for me in improving the integration of my programs with OS X is by adding or enhancing their AppleScript support.

AppleScript is a programming language that allows extensive inter-application communication on the Mac, and also allows communication between applications and the operating system. Apps that support AppleScript make specific functions available to a scripting interface, and the scripting language can both drive those applications and connect them into larger workflows. For instance, AppleScript is widely used in the printing and publishing industry to automate publishing workflows. A publishing-industry AppleScript might extract data from a FileMaker database, format the same data in Microsoft Word, and then push the data into Adobe InDesign for page layout. Other major applications on the Mac with extensive AppleScript support include Safari, Mail and iTunes.

Adding AppleScript support to my own programs is not just about improving their integration into the OS, however--it's about making them more useful, and allowing them to provide data and services in ways that perhaps I don't foresee. It's about making the programs better.

There's a reason not every program supports AppleScript, however--adding it is not a simple process. It involves several steps:

  • Deciding what data and functionality to make available.

  • Adding the appropriate system-level code to define the scripting commands that will make that data and functionality available.

  • Coding the application's internal functions to expose that data to scripting commands.

  • Testing, testing, and more testing.

  • Let's look at these in more detail. I'm going to base this discussion on the AppleScript support I'm currently adding to NameFind, my file search tool.

    Deciding what data and functionality to make available. What data is most important to the application, and what functions exist to access and manipulate this data? This question will vary, depending on the application. NameFind's main functionality is to search for files in specific directories by file name. I decided to create scripting functions that allow users to set or obtain the name of the search query, set or obtain the name of the search directory, run the search, retrieve the search data, and retrieve the name of a currently-selected file in NameFind's main window. NameFind does a few other things, such as displaying detailed information about selected files and displaying QuickLook previews of selected files, but these are secondary functions that would not be very useful from a scripting interface.

    Adding the appropriate system-level code to define the scripting commands that will make that data and functionality available. There are several steps to this process. Adding AppleScript support actually means adding support for Apple Events, which is the system-level mechanism by which applications on OS X communicate with each other. To support Apple Events, a programmer has to create a sequence of four-letter codes that represent an event class (basically, the application supporting that Apple Event) and event ID (the specific event, or command, defined by that application) and register those with the operating system. Developers register Apple Event support in several different places: in the application's property list file, which the operating system parses to display the name, version, and other aspects of the program, including AppleScript support; in the application's scripting definition (SDEF) file, which maps specific AppleScript commands to specific Apple Events; and internally, in the application's code, where the application registers its internal functions that respond to Apple Events. Cocoa programmers have an entire framework called Cocoa scripting that not only allow allows them to register AppleScript commands, but specific data objects and properties from their application that can be accessed and manipulated via AppleScript. I am using different methods from the lower-level Apple Event Manager framework, accessed via scripting libraries (tclAE for the Tcl programming language and appscript for the Python programming language); these libraries easily implement AppleScript commands but have more limited support for AppleScript data objects and properties. In the case of NameFind, I added an AppleScript identifier to its property list file; created a scripting definition file that contained Apple Events and AppleScript commands to set and obtain search terms, search directories, search data, and a selected file; and added code to register the internal functions that would respond to those Apple Events, via the tclAE library.

    Coding the application's internal functions to expose that data to scripting commands. Usually functions within the application already exist for accessing and manipulating the data in question, but it is often necessary to add additional code that provides the integration between Apple Events and the application. This is true of NameFind: in part because of the way the tclAE library integrates with Apple Events, I added several procedures to access and manipulate data from NameFind.

    Testing, testing, testing. This is the most time-consuming part of the process. Once you understand the basics of Apple Events--their format, how to refer to them in your code and in other files like the scripting definition file, and how to integrate them with your own application's code--they are fairly straightforward to implement. Or so you might think. AppleScript is a quirky, sometimes opaque language. The biggest drawback to AppleScript and Apple Events is that they sometimes behave in unpredictable ways, and often do not provide useful feedback to the programmer if errors occur--which can be very frustrating. For instance, when I was testing one of the AppleScript commands for NameFind's AppleScript support, I kept getting "missing value" instead of the data that I was requesting, with no specific mention of where the error was occurring. Also, after I encountered this "missing value" error, commands that previously worked would also mysteriously fail with "missing value" errors. It took many hours of digging to untangle these problems, which were twofold: first, there was a typo in the scripting definition file that prevented the Apple Event from being recognized; and second, AppleScript caches the output of its commands for increased speed, and apparently "missing value" errors can corrupt other data that has been cached. In encountering these problems, I ran into an issue that many, many developers complain about in regards to AppleScript: more than most scripting languages, it is especially prone to subtle or even silent bugs that are difficult to reproduce and/or track down. Many issues can cause such bugs, such as the typo in the scripting definition file; conflicts between core AppleScript and one of the numerous AppleScript extensions that can be installed to expand its capabilities, or conflicts between an application command and an AppleScript core command; and other issues. I think I've gotten the bugs worked out of my implementation of AppleScript in NameFind; once the program runs all of its AppleScript commands correctly, I'll consider AppleScript support complete.

    It would actually be possible for me to implement some AppleScript support in NameFind without going through all of this extra work: out of the box, Tcl/Tk (the framework that NameFind is developed with) supports a "do script" AppleScript command, which allows the program to run arbitrary Tcl code: the AppleScript would say "do script" and then pass along some Tcl code to execute. This is actually how most Mac programs that use Tcl support AppleScript: for instance, the AlphaX text editor and the aMSN messaging program. This option has a lot of appeal. First, it would require much less work on my part, and would open up a lot more power, as Tcl is both a more powerful programming language than AppleScript and is much more transparent in reporting errors. However, this method of supporting AppleScript is also a security risk, as it would allow the execution of any Tcl code. This method also requires knowledge of Tcl on the user's part; while many Mac users might be able to do basic things with AppleScript, that is not true with Tcl. Additionally, I would not be able to use this approach with my Python programs, as Python does not have comparable support for arbitrary execution of Python code via AppleScript (for security purposes).

    Finally, it is worth it for me to implement a complete AppleScript interface for NameFind and my other programs because AppleScript and Apple Events are the standard mechanism for inter-application communication on the Mac. More Mac users are familiar with this approach than any alternative, and the AppleScript/Apple Event mechanism can also be accessed from other programming languages, including Python, Ruby, Objective-C, and the command-line shell. By going this extra mile, I'm able to make my programs much more useful and better citizens on OS X. That's an excellent reason to do this work, and so I'm doing it.

    [/software] permanent link

    Still mulling App Store strategy

    I'm still reviewing the best way to integrate the Mac App Store into my software business, and as part of that process, I've sent my customers an e-mail soliciting their input. (A copy of this message inadvertantly reached the MacNN news site.)

    I have since received dozens of responses from my customers, which is both overwhelming and gratifying, and here's what they are telling me:

  • They are strongly against making the app store the only distribution channel for Mac software. They want multiple channels.

  • They are strongly against doing away with "try before you buy." They are not likely to spend more than a few bucks on an app without trying it--the risk is just too high.

  • They are unhappy with the lack of an obvious upgrade path from existing apps (i.e. using a serial number to buying through the App Store).

  • This feedback is helpful to me for three reasons.

    First, it suggests that the lack of an easy migration path to the Mac App Store presents significant issues for my existing customer base. My customers have bought my software using traditional shareware model: download a demo, try it out, and purchase a serial number to use the software permanently. Moving all of my existing products to the Mac App Store would require my existing customers to pay full price for future versions of my products--something that, naturally, many of them are reluctant to do.

    Second, it suggests that continuing to offer a sale channel outside the Mac App Store is not only a viable option but a necessary one. Many of my customers, for philosophical reasons, do not want to purchase software through the App Store, or at least do not want it to be the only option. Also, as I've noted before, not every product--including several of mine--is going to be suitable for the App Store because it runs afoul of one guideline or another, and changing the app to meet the guideline in question is not a viable option.

    Third, it helps to provide me a road map for the development of future products. My existing customers tend to be power users or even software developers themselves, who are comfortable with searching for, downloading, and testing software from a variety of sources. The Mac App Store is aimed at a broader audience, one that does not customariliy purchase much software. Currently all of my programs, even the ones that qualify for the App Store, are geared for power users. Going forward, it might make some sense for me to develop a different kind of software for the App Store audience.

    The App Store clearly represents a huge opportunity for Mac software developers, but that opportunity needs to be balanced with meeting the needs of my existing customers--the ones who have been with me this long. While I have not yet finalized my App Store strategy, clearly that strategy must consider both existing and potential new customers through the App Store.

    [/business] permanent link