Automatic Subversion Revision Stamping for iPhone Projects

Early during development of a new iPhone OS application, I discovered that App Store submissions must use a strict format for the CFBundleVersion key in the Info.plist file. It can only be numbers and dots. This left me a little frustrated with how to version pre-1.0 releases to testers. I didn’t want to use an open-source style “0.9” and yet it wasn’t 1.0. What I really wanted was “1.0 b1” or similar.

I stumbled on Daniel Jalkut’s post about automatically stamping the Subversion revision into Info.plist and thought that might be a good way to go. I also created a new key in Info.plist, LYSApplicationDisplayVersion, that I use as a human-friendly version string, which is where I get my preferred “1.0 b1” form. CFBundleVersion now takes the form of major.minor.revision, where major and minor are the important parts of the human-friendly version (“1.0”) and revision is the Subversion revision number that produced the binary.

I like this method because it solves one problem raised by commenters on Daniel’s post. Because the Subversion revision is the third component, it doesn’t matter that r3999 is a mature 1.2 point release and r4000 is a risky pre-2.0 alpha for testers. Those versions end up 1.2.3999 and 2.0.4000 and it’s clear that they are from two different branches of development. For iPhone Ad Hoc distribution, iTunes also parses the version properly and knows that 1.0.100 is newer than 1.0.95.

Here is the script to paste into your custom script build phase:

# Xcode auto-versioning script for Subversion
# by Axel Andersson, modified by Daniel Jalkut to add
# "--revision HEAD" to the svn info line, which allows
# the latest revision to always be used.
 
use strict;
 
die "$0: Must be run from Xcode" unless $ENV{"BUILT_PRODUCTS_DIR"};
 
# Get the current subversion revision number and use it to set the CFBundleVersion value
my $REV = `/usr/bin/svnversion -n ./`;
my $INFO = "$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Info.plist";
 
my $version = $REV;
 
# (Match the last group of digits and optional letter M/S/P):
 
# ugly yet functional (barely) regex by Daniel Jalkut:
#$version =~ s/([\d]*:)(\d+[M|S]*).*/$2/;
 
# better yet still functional regex via Kevin "Regex Nerd" Ballard
($version =~ m/(\d+)([MSP]*)$/) && ($version = $1);
 
die "$0: No Subversion revision found" unless $version;
die "$0: Modified, switched or partial working copy, you sure about that?" if $2 && ($ENV{"BUILD_STYLE"} eq "Ad Hoc" || $ENV{"BUILD_STYLE"} eq "App Store");
 
open(FH, "plutil -convert xml1 \"$INFO\" -o - |") or die "$0: $INFO: $!";
my $info = join("", <FH>);
close(FH);
 
$info =~ s/([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>\d+\.\d+).*?(<\/string>)/$1.$version$2/;
 
open(FH, "| plutil -convert binary1 - -o \"$INFO\"") or die "$0: $INFO: $!";
print FH $info;
close(FH);

There are a couple of other changes to this script for Subversion 1.5 and later, and for iPhone OS targets.

The first is that the regular expression allows for a trailing P in the Subversion revision. This signals a working copy from a sparse checkout, which I never use and therefore may be a problem. I have the script fail if any letter is appended to the revision when the BUILD_STYLE is “Ad Hoc” or “App Store”, which are two new configurations, cloned from Release, that I use for Ad Hoc and App Store distribution, respectively. Especially for modified working copies: I never want to accidentally hand out a build made from a modified working copy. Should the day come that I really do, I can comment this line out, make the build, and uncomment it again.

The second is that iPhone projects convert Info.plist to the binary plist format in the application bundle. In order to extract the existing CFBundleVersion key, it must be converted back to XML. When writing the plist back out, it is again converted to binary. Both of these steps use plutil, which is part of the Developer Tools.

Ruby, Threads and RubyCocoa

If you’re a Rails developer working on Mac OS X 10.6, you may have seen this message:

Ruby threads cannot be used in RubyCocoa without patches to the Ruby interpreter

It is caused by a plug-in or gem requiring osx/cocoa, frequently attachment_fu. AttachmentFu can use CoreImage as an image processor instead of calling out to an image manipulation library such as ImageMagick or GD2.

The warning is harmless, but it can be very noisy. To disable it, you can simply remove CoreImage from AttachmentFu’s list of image processors by creating a new file in config/initializers. Its contents should be this one line:

Technoweenie::AttachmentFu.default_processors.delete('CoreImage')

Image resize operations will now use one of the other image processors, which you can install from MacPorts. I never deploy to a Mac OS X server for production, and I prefer to run as much of the same code as possible in development, so this isn’t a problem for me.

Remember HTTP Password in Mobile Safari

In iPhone OS 3.0, Apple allowed Mobile Safari to save usernames and passwords in web forms. Unfortunately, Safari does not offer to do the same thing for HTTP Basic and Digest authentication. I’ve become fond of using HTTP authentication because it is very easy to set up, either in your Apache virtual host configuration or within a Rails application. There are many times that a full-fledged user database is unnecessary for a simple administration back-end.

There is a work-around, though it does mean storing your user and password in plaintext in your device’s bookmarks. HTTP allows you to supply authentication credentials as part of the URL, in the form http://username:password@example.com/.

Use content_for to Put Markup In Its Place

Here is a useful trick for ensuring that you keep your partial templates well organized without sacrificing page-load times or duplicating your layouts unnecessarily.

You can use content_for to capture some markup, but have it emitted into the page from somewhere else.

Two places this is immediately useful: adding additional tags inside <head> and placing in-line Javascript near </body>, while keeping the code itself right next to the DOM elements it works upon.

content_for works by capturing whatever appears inside the block and storing it for later use. You emit whatever is stored using yield. What’s more, content_for doesn’t clobber the previous captured text if you use it more than once with the same key.

Say you have a fancy Javascript control that replaces a standard <select> element in forms. You can do this in your partials:

<select ...>
  <option>...</option>
  <option>...</option>
  ...
</select>
<% content_for :body_close do %>
  <script type="text/javascript">
    // code that does something with that <select>
  </script>
<% end >

And then in your layout, just before the </body>:

<%= yield :body_close %>

Because most browsers will run in-line Javascript upon encountering it, this would delay the execution of the code until all of the page content was loaded, making your page appear to load faster.

What’s more, if that fancy Javascript control also requires a CSS file, but you don’t want to require the browser to fetch it on all of the other pages that don’t need it, you can conditionally add it to <head> by defining another content_for key that accumulates additional markup to go there:

<% content_for :head do %>
  <%= stylesheet_link_tag 'fancy_control' %>
<% end %>

And in your layout:

<head>
  ...
  <%= yield :head %>
</head>

This is also useful for keeping per-page markup like <title> and meta tags in the template and out of your controller.

IE + Rails Javascript Caching Gotcha

Rails supports a simple method of asset bundling with javascript_include_tag by way of the :cache option:

<%= javascript_include_tag 'first', 'second', 'third', :cache => true %>

There’s a gotcha hiding here that you may not find until you deploy to production and visit your site in IE.

Admittedly, this is a bug, but if you happen to have a second javascript_include_tag with the :cache argument in the same page, IE will choke on lots of the script, telling you “object doesn’t support this property or method,” among other things.

The problem is that Rails can’t resolve multiple tags using the same value for :cache, and it emits two <script> tags referencing the same Javascript file. IE gets confused by the second one (perhaps loading it twice?). Every other browser I’ve used handles it fine.

The solution: either don’t use :cache more than once or make sure you use explicit, unique bundle names instead of just true.

Xcode User Defaults

Xcode as a development environment matches my working style fairly well, but there are two things about it that have bothered me. The first is a warning when I try to undo edits across a file save event. Since I have Xcode set up to save all files before a build, this happens often when I try an experiment and don’t like it.

The second is code completion for language constructs such as if statements. Xcode wants to do this:

if (condition) {
}

While I used this style a long time ago, today I want the opening brace on its own line:

if (condition)
{
}

No amount of digging in the preferences will provide a way to change these behaviors, however there is a lot of customizability hidden in the Xcode User Defaults. My two issues are solved quite easily:

$ defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO
$ defaults write com.apple.Xcode XCCodeSenseFormattingOptions -dict BlockSeparator "\n"
Voicemail Problems with Skype

For small businesses in need of only a few phone numbers, Skype is a great value. I pay about $60/year for a phone number that rings wherever I’m logged into Skype, plus voicemail. When it works, which is almost all of the time, it’s fantastic.

The trouble is “almost all of the time.” So for Googlers benefit, here are three problems I’ve had with Skype voicemail over the last couple of years that aren’t answered by Skype’s FAQ and unlikely to get a straight answer from tech support in the first response.

These solutions are for the Mac client, but can probably be adapted to Windows. I doubt the files are different, but the paths to them will be.

Can’t hear any audio when you listen to a voicemail?

Calls go to voicemail immediately, without ringing?

For both of these, quit Skype, then delete the file ~/Library/Application Support/Skype/shared.xml.

Unanswered calls not going to voicemail?

Quit Skype. Open ~/Library/Application Support/Skype/your-user-name/config.xml in a text editor. Look for:

<Call>
  ...
  <NoAnswerTimeout>10</NoAnswerTimeout>
  ...
</Call>

The number is the delay, in seconds, before Skype will do something with the call. If this is missing, Skype won’t send the call to voicemail.

What Happened to Programming

Last week, Mike Taylor posed the question “whatever happened to programming?” (also see his followup). I think part of the answer is simply “it’s grown up.”

Programming, either as a hobby or a career, has only been around for a little less than 70 years. (The first was the Colossus, built and used by the British during World War II to read encrypted German messages.) This is very young when compared to other scientific fields: agriculture, construction, various engineering disciplines, etc.

I won’t disagree that building the very fundamental parts of a new application from scratch can be very enjoyable. Perhaps not printf(3), but certainly other parts. The problem is bug rates, though: any seasoned software developer knows that writing less code means less bugs. If high-quality libraries are available that provide 80% of the functionality of your new application, you will likely end up with a better product, in less time, than if you chose to write the entire thing yourself.

Consider the sorry state we’d be in if:

All farmers experimented with their own methods of weed and pest control (organic or not), instead of using techniques proven to work in the past. Our agriculture industry probably couldn’t meet the population’s demand for food.

Architects and builders “winged it” when building new homes and office buildings. Bugs in that industry would likely mean structural failure. Who would want to live in those houses?

How about automakers? It’s one thing to build a kit car as a hobby, it’s quite another to tinker with your designs when you’re producing cars on a massive scale for general use (just ask Toyota).

It may not be as glamorous, but if our collective bug count goes down and the software industry ships better products that don’t require constant patching, I can’t help but consider this a very good thing.

Small Business Accounting Software

I made a New Year’s resolution for 2010 back on October 1st: stop using the awful shovelware produced by Intuit (QuickBooks) and move to something else. I first wrote “something by a company that cares more about the Mac,” but let’s be realistic. Large companies seem to lose a sense for quality products and good customer service, so really this is all about “anything but QuickBooks”.

I’m moving away from QuickBooks because I paid for it in 2007 and it doesn’t work (seriously: it corrupts transactions) on Snow Leopard. Even the QB 2009 installer failed to work on Snow Leopard until they patched it, as they were still using the Java Bridge, deprecated by Apple way back in 2006 and removed in Snow Leopard. If I have to spend money on accounting software to run Snow Leopard, I’m going to look at all my options.

Light Year Software is, for now, primarily a consulting company. I sell my time, so an accounting package needs to support that. I do not carry inventory or resell goods. I don’t have to collect sales tax. While the business owns some equipment (computers), it’s very minimal.

The list of contenders is disappointingly short, though on the upside, it means I don’t have to try out as many of them: Gnucash (free), AccountEdge 2009 (USD$299) from Acclivity (formerly MYOB) and MoneyWorks Express 5.2 (USD$279) from Cognito.

Gnucash

This one was easy. While I’m a fan of open source, I only had to spend a few minutes with Gnucash to know it wasn’t right for me. It’s written using Gtk+, which isn’t surprising since it’s a cross-platform application, but I didn’t want to spend already unpleasant time using a thoroughly non-Mac UI. There are also some known issues that struck me as likely to be highly irritating. Finally, while I hope to never have to, I do want a safety net of a real company behind the software: phone support, a well-written manual, and an expectation of support for older, but not ancient, releases (after all, I’m in this boat because I didn’t want to update QuickBooks 2007).

AccountEdge and MoneyWorks Express

These two are similar enough that it’s probably easier to write about them together.

AccountEdge wins (barely) on first impressions. The main dashboard interface is more colorful and buttons look like buttons. Both applications use a flowchart style, visually presenting how you should move through the application. AccountEdge does a better job of it, though, because buttons are obvious, whereas with MoneyWorks, you don’t know what’s clickable until you hover over it.

MoneyWorks is a bit nicer when you get down into the forms, but only by a hair. The tab order is often unintuitive; for example: after entering a date for a transaction, tab takes you to the period drop-down immediately above it, instead of the next field down and to the left.

AccountEdge did a better job of hand-holding me through setup. There is a separate setup dashboard, each major section walking me through a wizard that prompts to transfer data from your old system into AccountEdge. MoneyWorks doesn’t have this, though it does have a nicer manual (PDF). AccountEdge’s manual is just a set of HTML pages, with a Javascript table of contents on the left that doesn’t always work quite as I expect. AccountEdge actually has a nice set of manuals, too, under Help → Manuals. I don’t know how I missed this earlier.

AccountEdge allows me to set up a billing rate per customer (or even per employee), which is very nice. MoneyWorks does not do this, instead requiring me, as with QuickBooks, to set up separate products for different billing rates. For me, differing rates are quite common. It can be hard to raise the rate for long-term customers, or I may wish to charge different rates for different types of work. MoneyWorks Gold claims better support for time-based billing, but it’s also USD$599, more than double the price. Express doesn’t let me enter dates for each line item on an invoice, which I think is important. This is a major strike against.

AccountEdge’s forms editor is awful. It’s impossible to see how objects are layered, and I found that line shapes didn’t show up at all, even when previewed. The first time I tried to customize an invoice, the result that was emailed to a customer was horribly misaligned, though thankfully still legible. A second try produced better results, but it took a lot longer than I expected. Thankfully, this is the kind of thing I hope to do very rarely. MoneyWorks’s forms designer looks quite powerful, offering what looks like a macro language to create the forms. I’ll be honest, though: I didn’t go into this in depth because by this point, after realizing the limitations on time-billing, I’d made up my mind to go with AccountEdge.

AccountEdge can email invoices and statements directly from the application, which is really nice, except that it uses Mail.app and the first defined mail account. For me, this means that business correspondence goes out using my personal account. That’s unacceptable, so I’m back to doing what I did with QuickBooks: print to a PDF, then compose an email by hand and attach.

One last “weak sauce” moment is with the MoneyWorks user support forums. Trying to be funny, they convert any instance of “MYOB” to “*Four-letter-word accounting software*”. It’s juvenile and unnecessary. To their credit, searches for “MYOB” do the right thing, so if that’s an important part of a search query, at least the right posts will come back.

So, that’s it. It’s early January, 2010 and AccountEdge 2010 was due to come out today. It is not yet available, however. The trial version I have will work for 30 days, and while Acclivity will give me a free upgrade to 2010 if I buy 2009 now, I’m going to hold off and make sure 2010 doesn’t do anything idiotic before spending the money.

Update Jan. 13: One thing I neglected to point out when I published this is that an often-cited reason for using or sticking with QuickBooks is that “my accountant uses it.” I was pleased to discover that both AccountEdge and MoneyWorks make free copies of their software available to accountants in order to read my company file. The same is technically true for Gnucash, but if I’m not thrilled to use it, what are the chances my accountant will be?

Update Jan. 27: First, I received an email from Tom Nash, a managing partner at Acclivity, who told me that they are aware of the mail account issue I noted above. According to Tom, the way it works is somewhat due to how Mail.app works. If you have multiple accounts, AccountEdge will use the same account that Mail.app will use if you press ⌘N to start a new message.

Second, I received my AccountEdge 2010 CD and, sadly, the form editor looks exactly the same as 2009. I still don’t see line shapes in the default forms, which means customization will likely still be a difficult trial-and-error process.