Forge Code

Making Xcode Work for You When Developing PrefPanes

The first few times I tried developing a Preference Pane in OS X, I had more than a few head-scratching, “what the heck do I do now?” kind of moments. Unlike a typical Mac OS X or iPhone project in Xcode, when you create a Preference Pane project from the built-in Xcode template, doing a ‘Build and Run’ doesn’t result in your project being run. In fact, as of Xcode 3.2.1 the ‘Build and Run’ item in the ‘Build’ menu is disabled ‘out of the box’. This is due to the fact that your project isn’t building an executable program, it’s building a .prefPane bundle/plug-in.

Getting ‘Build and Run’ (⌘+R) to work as you might expect for a PrefPane project (by building your .prefPane, then installing it in System Preferences.app and loading it) isn’t hard, but it’s not at all well documented.

My First Attempt

When I started working on my first PrefPane as a learning around two years ago, I wrote a macro in Keyboard Maestro that would fire when I pressed ⌘+R in Xcode. The macro would open my freshly built .prefPane (which makes System Preferences.app load it), wait 1 second, then hit the ‘Return’ key, which would install the new .prefPane by dismissing the following dialog:

This worked, but it had a lot of downsides. Firstly, I had to turn this macro on and off every time I changed from working on this particular PrefPane project to working on anything else so that ⌘+R would do what it should for other projects. Secondly, it didn’t allow me to debug the PrefPane, only run it. This is a serious impediment to development, especially if you don’t know what you are doing (which, at the time, I didn’t!).

When I returned to looking at PrefPanes for another project about a year ago, it occurred to me that there must be a better way…

A Better Way

To get Xcode to actually ‘Run’ something, you need to have an ‘Executable’. Since a PrefPane doesn’t generate an Executable (something that you can actually execute as a program), you’ll need to add a ‘Custom Executable’ to your Xcode project. To do this, go to the ‘Project’ menu, and select ‘New Custom Executable…’.

Call the Custom Executable ‘System Preferences’, and select the ‘System Preferences.app’ application in /Applications/ by clicking on the ‘Choose…’ button.

You can now Run and Debug System Preferences.app by pressing ⌘+R and ⌘+Y. That’s great, but it doesn’t install your .prefPane as part of the process, it just launches the System Preferences.app application.

System Preferences.app’s Hidden Command Line Argument

I found this out through trial and error, and I haven’t seen it documented anywhere, but the System Preferences.app application actually takes a command line argument, which is the path of a .prefPane to install.

To set this argument, go to the ‘Project’ menu, then select ‘Edit Active Executable “System Preferences”’. Click on the ‘Arguments’ tab at the top of this pane, then add a new command line argument that is the path of your built .prefPane bundle.

/Users/nick/Builds/Xcode/Debug/MyPrefPane.prefPane is the path to the .prefPane built for the MyPrefPane project when it is built in the ‘Debug’ configuration on my machine. Note that on my Xcode setup, I have a customised location for build products (/Users/nick/Builds/Xcode), so even if I move my PrefPane project folder around, this path won’t change. If you have Xcode setup to build in MyProject/build (which is the default), you’ll have a path that looks more like /Users/nick/Code/MyPrefPane/build/Debug/MyPrefPane.prefPane. If you move or rename your Xcode project folder, this path will change, and your project will break, so beware.

Also of note: this technique brings up the dialog shown above asking if you want to replace (or install, if it’s the first time) the .prefPane in question whenever you do a ‘Run’. Hitting ‘Return’ after a build isn’t a huge deal to me, but it would be nice to have this automated. Another command line argument for System Preferences.app could help here, but I’m not aware of the existence of such an option.

Why Doesn’t The Xcode Template Do This For Me?

There’s no good reason that I can think of why the default template doesn’t include System Preferences.app as a Custom Executable. There is, however, a good reason it doesn’t include the .prefPane path as a command line argument. The path is dependent on your Xcode configuration, and the path is fragile (by which I mean that if you renamed your Xcode project folder, “Run” would no longer install your .prefPane).

For Apple (or anyone else, for that matter) to make a template do all this for you, they’d have to allow the Executable ‘Argument’ fields to support Xcode variables. As of Xcode 3.2.1, I have been unable to work out a way to do this. In some fields in Xcode, you can use variables like $BUILT_PRODUCTS_DIR and $PRODUCT_NAME, to represent (not surprisingly), the Build Products Directory, and the Product Name of your current project. If the ‘Argument’ fields supported these variables, it should be possible to use something like $BUILT_PRODUCTS_DIR/$PRODUCT_NAME.prefPane as a command line argument, which would not only be robust against configuration and path changes, but would mean that the setup I have described here could be built in to the Xcode Preference Pane project template itself, meaning that things would ‘just work’ out of the box.

Addendum

As pointed out by Rainer Brockerhoff in the comments, Xcode does support variables in Executable Arguments. The real problem is that for some reason $PRODUCT_NAME doesn’t work when using the default Xcode PrefPane template. Investigations continue…

Comments