Swift Scripting (Part 3)

In part one of this series, we covered the basics of the Scripting Bridge and how one might leverage this technology with Swift. In part two, we explored the idea of using Swift protocols to describe the scriptable interface exposed by an application. Here we’ll look at how we can automatically generate the necessary protocols and package them up in an OS X Framework for easy reuse.

To translate the sdp-generated collection of Objective-C interfaces to a collection of Swift protocols, we’ll leverage the Python bindings for libclang. The first step is to install the bindings. This can be done as follows in a Terminal window

curl -O https://pypi.python.org/packages/source/c/clang/clang-3.5.tar.gz
tar zxf clang-3.5.tar.gz
cd clang-3.5
sudo python setup.py install

The next step would be to write a Python script using the clang module to perform the transformation. For the present discussion, we’ll skip the details of the Python code and simply make use of sbhc.py (Scripting Bridge Header Converter), which is available as part of the SwiftScripting project on GitHub. The sbhc.py script is invoked as follows (using Acorn as an example)

/path/to/sbhc.py Acorn.h

sbhc.py does its work silently. The output of the above command would a file named Acorn.swift in the current directory. Acorn.swift would contain a set of enums and protocols for the Acorn application. The SwiftScripting project includes a second utility, sbsc.py (Scripting Bridge Scripting Classes). sbsc.py is used to generate a separate Swift file which defines a single enum representing the scripting classes for the target application. sbsc.py is invoked as follows

/path/to/sbsc.py Acorn.sdef

Note that the sbsc.py script does not make use of the clang module. It invokes xmllint on the sdef specified on the command line to extract the class strings and then emits the enum in a straight-forward manner.

Now, we can create an OS X framework to house all of the necessary ingredients. This framework can then be imported by Swift automation scripts when targeting a particular application. First, create a new OS X Framework project in Xcode.

Create a new OS X framework

You’ll probably want follow a consistent scheme when naming the frameworks that you create. The convention that I’ve adopted is to use the name of the target application followed by Scripting. So, for the Acorn application, the name I use for the scripting framework is AcornScripting.

Once the framework project has been created, you’ll need to add the following files (again, using the Acorn as an example):

Acorn.swift
AcornScripting.swift

The only other task you’ll need to perform in Xcode is to ensure that the Swift source files are designated as members of the framework target. With that taken care of, you can now build the framework.

To build the framework, visit the directory containing the framework’s .xcodeproj file and issue the xcodebuild command. This will perform a release build and all of the output will be contained in the build directory below the project directory. If the build succeeds, the framework will be located under build/Release. The framework should be copied or moved to the /Library/Frameworks directory.

In part four of this series, we’ll explore automation of scriptable applications following the approach introduced parts one through three.

6 thoughts on “Swift Scripting (Part 3)

  1. I’m having terrific trouble with the
    #import
    in AcornScripting.h
    I don’t have Acorn so I followed all the steps using the Finder app.
    So, I used
    #import
    in SwiftFinderScripting.h
    The problem is: I get an error
    “include of non-modular header inside framework module ‘SwiftFinderScripting'”
    where the header reference has been added.
    I have made sure both headers are Public and changed
    “Allow non-modular headers” to Yes.

    I’ve been using AppleScript for years and the idea of using the same scriptable methods from Apple applications is very exciting to me.

    Is there any suggestion you can make to clear this error?

  2. I did write the #import lines as written in your guide but the less-than, greater-than signs are not appearing in my post. The first appears to have been interpreted as newline and the word “in”

Leave a reply to mistercat Cancel reply