“I would prefer not to”: Preferences Files Done Badly

I wrote a post I didn’t upload two days ago about how CodeWarrior 6 was working natively in Panther (Mac OS X 10.3) when it hadn’t worked in Jaguar (10.2) and possibly earlier. The appropriate comp.sys.programmer.codewarrior thread can be found here.

CW 6 is probably most useful to developers who still need to do 68K builds. Yes, there are still professional developers who are getting paid to do this in certain cases, whose identities (the cases, not the developers) I will leave as an exercise for the reader.

So I wrote a whole post about the tempestuous history of CodeWarrior 6 on OS X, waxed nostalgic about WWDC 2000, and marveled how something Apple had done in Panther managed to make it work again.

But I was wrong.

A little later I opened CodeWarrior 8, which is a much more respectable version of CodeWarrior to be using in this day and age, and when I tried 6 again for the express reason of verifying the post I’d written, CodeWarrior 6 crashed again just like it had in 10.2.

If I remove the /Metrowerks/ folder that CodeWarrior 8 puts in my OS X ~/Library/Preferences/ folder, then CodeWarrior 6 can be opened again as a native OS X application without crashing.

The problem isn’t something in the OS. The problem is CodeWarrior 6 choking on something in a preferences file it didn’t expect.

So this post is going to be a little different.

I don’t know for a fact exactly why CW 6 is crashing, but it might be related to code like this, which gets a preferences value from a preferences file:

  SInt32 valueInt; // Where we're saving value

  CFTypeRef valueRef = CFPreferencesCopyValue(ValueKey, AppID, (CFStringRef)kCFPreferencesAnyUser, (CFStringRef)kCFPreferencesAnyHost);
  if(value != nil)
  {
    if(CFGetTypeID(valueRef) == CFNumberGetTypeID() && CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &valueInt))
    {
      // Success
    }

    CFRelease(valueRef);
  }

Now, this code does the right thing. (Sorry about the formatting.) It doesn’t assume that the value referred to by ValueKey exists. And (what developers get wrong) it doesn’t assume that if a value is found, that it’s of the assumed type. This is important because the OS X preferences file format is text, XML-based, easy for end users to get into and muck around with. If the type check wasn’t there, and the value were instead, say, a string? The application would most likely crash.

And this is just Carbon. The Cocoa framework takes care of all of this for you, reading preferences XML data into objects. Guess what! Cocoa apps also crash when you muck with the preferences file, just like badly written Carbon apps. There’s nothing you can do about it.

Now, this is not a hugely important detail. Users who muck with preferences files willy-nilly deserve what they get. But I would prefer that my apps get this sort of thing right, and I’d prefer that the apps I use do, too.