Thursday, September 04, 2008

Revamping a Dated Cocoa Application: InCDius 2.5

iPhone development has re-energized my love of programming. My day job requires me to do too much Win32 coding, and that is soul deadening for a Cocoa programmer. The combination of corporate priorities, and the fact I can add features much more quickly and with fewer bugs in Cocoa, leads me to spend a huge fraction of my time in Visual Studio, when I'd rather be in XCode. It's been draining.

I released InCDius 2 back in 2002, and it never got out of beta, although beta 16 was quite stable, and I apparently have a number of loyal (yet put upon) users. It is just an Audio CD database, not a personal media database like the well regarded Delicious Library. It was fast, and pretty darn stable, although a few users have had database corruption issues—please back up to XML people. I knew for years that I should release a new version, but could never quite muster the energy. It embarrassed me having this old, non-Intel application, gradually getting less stable with each OS release application; and I was considering my options for killing it entirely.

But my Remote Remote GH for iPhone application makes heavy use of the SQLite database, so I felt confident I could transition InCDius away from the Berkeley DB database. This transition had been the most daunting of the reasons keeping me from releasing an update.

My first impulse was to refactor the whole application using Core Data, but that would not have added anything but development time; the GUI was wired up quite well; the application was already scriptable. All I needed was a new database backend, and Apple ships OS X 10.5 with SQLite 3 right in /usr/lib. By choosing SQLite, I was no longer responsible for compiling and upgrading my database library; I can rely on it being there and just working. I tried using the QuickLite SQLite wrapper, but it was not capable for the task, as it creates a large number of objects maintaining its internal cache, to the point of locking up my Mac. No, I had to call the SQLite C API directly. As long as I minimize random access into the database, I get good results.

I also wanted this to be a release that could live on its own for a very long time; who knows how long I'll go without finding the time to update it. I wanted code which would last a very long time. This meant not only transitioning to Intel, but compiling for Intel 64-bit, and removing every deprecated API or framework message I could root out. I decided to make the minimum OS X version 10.5 (Leopard), and to transition the codebase to Objective C 2.0, although it isn't quite all there yet. I'll have to work on the garbage collection, but I am using both Objective C 2.0 fast enumeration loops and properties where appropriate.

Forward thinking means removing the past, so out went a few features tied to old APIs. I had been allowing users to import information from Classic Mac OS's CD Player preference file, but that involved using old school Resource Manager calls. Anybody ever going to use this feature again? No. Then out it goes, along with the Resource Manager.

I had 2 separate ways to play Audio CD tracks. One used QuickTime; one used Core Audio. Kept the Core Audio.

NSTableView has a new (to me) way to deal with selections with NSIndexSets, and deprecated the messages I had had been using. Goodbye old messages.

My own Objective C objects were filled with ints, unsigned ints, longs, and even the occasional unsigned. Hello NSInteger, NSUInteger and 64 bit computing.

I had been using a custom version of NSTableView to draw blue stripes. Goodbye custom class, hello check box in Interface Builder and grey stripes.

Obviously, I had been using .nib files for building my GUI. Time to upgrade everything to .xib files.

A user sent me his database (as a .zip archive of XML files) of 15,000 individual disks. Wow, and I had been thinking InCDius was zippy fast and ready for release. Massive performance tuning and the decision to load the database at runtime instead of upon the first search. Much simpler and reliable, and lets the user do something else while everything gets ready.

Take time to remove all the warning messages. You will save yourself a huge amount of hassle in programming, but especially in Cocoa programming if you eliminate all the compile warnings. You want to know right away if the compiler doesn't know if NSView responds to "releese". Do not let warnings build up. Fix them. Fix them all.

A Google Tech Talk by Linus Torvalds inspired me to change the unique identifier key I had been using to lookup disks in the database; he uses SHA1 hashes to avoid corruption in Git, and it seemed to me that a SHA1 hash of the Audio CD's Table of Contents would be as close as I could get to being unique; although there will be rare instances when 2 CDs cannot both be in the database.

So after all this, what do I have? A fast, reliable(?), clean, focused little application, which I can send out into the world; spend the next few months fixing the occasional bug and adding the occasional feature, and which people can use for a good long while.