Forge Code

Using RubyCocoa to Alphabetically Sort Property List Files

When you need small sets of static, structured data for your Mac or iPhone projects, the most common approach is to bundle a Property List (or plist) file in your app. Any object-graphs made solely of ‘property-list objects’ - i.e. instances of NSArray, NSDictionary, NSString, NSData, NSDate or NSNumber - can be easily converted to or from serialised data formats, which means that they can be trivially read from or written to files or network sockets. For more information on plists, see Apple’s Property List Programming Guide.

Apple’s Xcode Developer Tools ship with a bunch of utility applications, including Property List Editor, which (as you might have already guessed) allows you to edit plists. One feature it doesn’t have is the ability to sort entries within NSDictionary objects alphabetically. You can re-arrange elements from the GUI, but for any non-trivial plist, sorting by hand becomes tedious. The order in which these entries appear is only of interest to a person editing the plist manually; as soon as you read the plist using the Cocoa APIs, any order is lost, since NSDictionary objects have no concept of sort order. As an aside, if you do want an ordered NSDictionary or NSMutableDictionary, check out Matt Gallagher’s post on Cocoa with Love: OrderedDictionary: Subclassing a Cocoa class cluster.

RubyCocoa = Mac OS X Scripting Goodness

While I love working with Objective-C for application development, it’s not a good scripting language. For scripting, I prefer to use one of the highly dynamic, modern programming languages like Ruby or Python. RubyCocoa may not be the most technically advanced way of bridging the Cocoa APIs and the Ruby programming language (see MacRuby), but it does have the advantage that it ships with Mac OS X (from 10.5 onwards). Until MacRuby is shipped with Mac OS X, RubyCocoa is the best choice for scripting the Cocoa APIs in Mac OS X if you want to use Ruby.

The script I’ve written simply reads a plist from a file, then writes it back out again using the Cocoa APIs. When the plist data is written back to a file, the entries in each NSDictionary are sorted by their keys alphabetically, since this is the way that the Cocoa APIs serialise NSDictionary objects. The actual Ruby code for each step is extremely simple.

This loads RubyCocoa:

require 'osx/cocoa'

This will parse the contents of a plist at path into dict:

dict = OSX::NSDictionary.dictionaryWithContentsOfFile(path)

This will save the contents of dict to a file at path:

dict.writeToFile_atomically(path, true)

That’s all there is to it. The actual script that takes either one or two command line arguments. If you give only one argument, the script over-writes the original plist with the sorted version. If you give two, it reads from the path given in the first argument, and writes to the path given in the second.

The Actual Script


require 'osx/cocoa'

infile = ARGV[0]
outfile = ARGV[1]

# If no output file is specified, over-write the input file
if outfile == nil
  outfile = infile

OSX::NSDictionary.dictionaryWithContentsOfFile(infile).writeToFile_atomically(outfile, true)

But That’s Kinda Boring…

Yeah, it is kind of boring. Sorting a plist file like this isn’t particularly useful for most people (well, it was useful enough for me to cause me to write the above script, but I digress). What might actually be useful for a lot of people would be to use RubyCocoa scripts to deal with plists as part of your project build cycle. If you are editing plists by hand, it may be useful to have a script that verifies that the static plist data files actually conform to the data structure your code is expecting; writing a RubyCocoa script to check this would probably be a lot easier than writing similar Objective-C code. It may also be useful to use simple scripts to populate your plist files with data from other sources, like a database, or a web API.

Whatever the reason, using RubyCocoa scripts instead of Objective-C to deal with plists programatically is a very handy skill in the arsenal of any Mac OS X or iPhone OS developer.