Anybody who’s spent time with Apple’s Interface Builder (IB) application knows that it’s an amazingly intuitive, powerful tool for designing user interfaces. It’s also a major pain in the ass.
While Xcode has improved by leaps and bounds over the past few years, IB has struggled to keep up. For the most part, it seems that changes to IB have been the bare minimum that would placate the masses (and management). IB had to support Carbon, so they tacked on Carbon support. IB had to support Cocoa Bindings, so they added a new inspector pane. There have been some really cool improvements such as the “Compatibility Checking,” but the number of frustrating shortcomings leave me always hoping that a major renovation will soon come.
My problems with IB wouldn’t be nearly so bad if there were viable workarounds. Many of my criticisms stem from its inability to effectively edit multiple items at once. Many times I find myself repeatedly setting the same properties on an item, or typing highly similarly but mildly deviating bindings into a key value field. The problems I face in IB all the time are the kinds of problems I would have long ago automated, if only there was an easy way.
Nibtool is the command-line counterpart to IB, and new programmers on the Mac often assume that it offers exactly the kind of flexibility I have been wishing for. Here we have a tool that dumps a text version of the objects, classes, hierarchy, and connections in a nib file. Hallelujah! Once I dump it to a text file, I can run some shell script or regular expression replacement on the file and pop it back into the nib, right? Nope, sorry! The man page for nibtool lists only one bug, and it’s a big one:
The fact that this is listed as a bug has always given me hope that it would one day be fixed. Unfortunately, it appears to be getting fixed at the same pace as all the other bugs and quirks of IB. But when Michael McCracken posted this entry describing a happy discovery in the latest Xcode release notes, I shared his cautious optimism. Nibtool has been updated, and it includes a new pair of options for “importing” and “exporting” object properties from and to an existing nib file:
Is this it? The holy grail? I have played around with the new nibtool a little bit, and all I can say is we’re closer, but this is by no means an easy or complete mechanism for manipulating the contents of a nib file. The release notes and man page are so vague that it’s difficult to even figure out how to use the new options. I have mostly deciphered the new behavior, and hopefully can clarify things for you by paraphrasing what the new options do.
In each case, the effects of the tool are only to manipulate the instance objects in the particular nib file. The functionality is therefore useless for adjusting things like connections, hierarchy, etc. Think of the nibtool import and export functions as shorthand for “iterate the objects in my nib and do keyed value reading or writing on them.” I think one of the confusions is that the PLISTFILE argument in each case is an input file to nibtool. Output is always to the standard output.
nibtool -e – Takes as input a special-format plist file that specifies a list of Objective-C class names and associated keypaths for values that should be fetched. For all objects in the nib that are of the specified class, this option produces as output an plist file containing the current keyed values for whatever keys you specify in the PLISTFILE.
nibtool -i – Takes as input a plist file of the format produced by nibtool -e. For every object specified in the input plist, this option essentially looks up the object in the nib and sets its value as specified in the plist.
So in a nutshell, you use nibtool -e to fetch a bunch of values as a text file. Tweak the text file as you see fit, and feed it back in with nibtool -i. A major drawback to nibtool -e is that if any object you specify doesn’t respond to the particular key, then the whole deal is called off. So you can’t for instance ask for “bounds” of all “NSObject” and hope to get just the objects in your nib that actually have a bounds property. You have to make sure that for whatever class you specify, every instance is key-value compliant for the given keypath.
So what is the magic format of the first input plist? It’s not too special, and is documented in the nibtool man page, but it is still clunky enough to limit casual use. You basically cannot use nibtool -e for anything useful unless you’ve already taken the time to put together a suitable “input plist” describing what it is you’re looking for. I knew that at the very least I was going to need something to make the input plist easy, so I started with a small python script that produces a plist for the simplest possible fetch: “given one class name and one keypath, fetch the values for all suitable objects in the nib.” This is ugly but it’s my very first python script. Cut me some slack. (Thanks to Gus Mueller for telling me about the os.popen() function, and to Mark Rowe for teaching me about Python’s magic triple-quoting).
Paste this code into a python text file and run it from the command line. I called my NibValFetch.py. Now when I cruise over to one of my nib files, I can do something like this:
That is: get all the NSTextFields in Preferences.nib, and ask for their frame. The file “hacking.plist” now contains a list of all the NSTextField objects in my nib, along with a text representation of the frame for said field. Now if I wanted to make every text field 2 pixels wider or something, I could write a script to modify the hacking.plist file, and then feed that into nibtool -i.
I don’t really have time to delve into this much more right now. I’m not even sure the new functionality will prove very useful, but I figured that somebody out there will find a good use for it if nudged in the right direction. I hope this introduction to the new functionality clears up what it can and can’t do, as well as giving you a clue as to how you might go about leveraging it. If you think up any really clever hacks with it, please be sure to share in the comments!