Thursday, July 20, 2006

Command Line Processing in Cocoa

Why would you want to process the command line in Cocoa? I mean, Cocoa's all GUI and other cool stuff, right? Well, yes.. but there's really nothing quite as cool as the command line, is there? Good, so let's get to it.

The typical ways to parse command line arguments on Unix systems are to use either getopt(), getopt_long(), or just parse argv yourself. Well now Cocoa (actually, it's Foundation that provides this) offers an even easier alternative. Enter NSUserDefaults. Let's just jump right to a quick example.

// File: args.m
// Compile with: gcc -o args args.m -framework Foundation
#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSUserDefaults *args = [NSUserDefaults standardUserDefaults];

NSLog(@"boolArg = %d", [args boolForKey:@"boolArg"]);
NSLog(@"intArg = %d", [args integerForKey:@"intArg"]);
NSLog(@"floatArg = %f", [args floatForKey:@"floatArg"]);
NSLog(@"stringArg = %@", [args stringForKey:@"stringArg"]);

[pool release];
return 0;

First we get a access to the standard user defaults object, which will actually do all the parsing of the command line. Then we just access command line arguments like they were stored in the user defaults object. Here's a few sample runs:

$ ./args
2006-07-20 22:24:52.996 args[21633] boolArg = 0
2006-07-20 22:24:52.997 args[21633] intArg = 0
2006-07-20 22:24:52.997 args[21633] floatArg = 0.000000
2006-07-20 22:24:52.997 args[21633] stringArg = (null)

$ ./args -intArg 18
2006-07-20 22:25:41.923 args[21640] boolArg = 0
2006-07-20 22:25:41.923 args[21640] intArg = 18
2006-07-20 22:25:41.923 args[21640] floatArg = 0.000000
2006-07-20 22:25:41.923 args[21640] stringArg = (null)

$ ./args -intArg 18 -stringArg "foo bar" -floatArg 3.14159 -boolArg YES
2006-07-20 22:26:15.129 args[21644] boolArg = 1
2006-07-20 22:26:15.129 args[21644] intArg = 18
2006-07-20 22:26:15.129 args[21644] floatArg = 3.141590
2006-07-20 22:26:15.129 args[21644] stringArg = foo bar

Arguments are case-sensitive, they can be specified in any order, and in general, NSUserDefaults is pretty smart about processing them. For example, the a true bool value can be specified as YES, Y, y, 1, 123, etc, whereas a false bool value can be NO, no, n, 0, etc. Also, note that arguments are specified with a single leading - rather than -- which is typical of most "long" Unix command line options.

I haven't verified this, but I'd imagine that NSUserDefaults access argv via the NSProcessInfo class. It also leaves the argv that's passed to main unharmed in case you want to do any additional processing.


Erik Scrafford said...

Hrm...Didn't know (or expect) the NSUserDefaults class would interact with the command line. In fact, I can't find anything in the documentation that would suggest this work; but I just tried it out to override one of the defaults I check in my app, and it works fine. Weird. Where did you find out about this?

Greg said...

There's a little blurb in Cocoa Programming by Anguish where he mentions overriding some Quartz drawing pref via a command line option. The command line option happened to match a user defaults setting, so I just started poking around from there.

Dave Dribin said...

This is actually documented in User Defaults guide, under NSArgumentDomain. I didn't know you could do that, though, so thanks for the tip, Greg!