Tuesday, July 24, 2012

Sparrow's not a Canary

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

All the cool kids are talking about Sparrow's acquisition by Google. People far smarter than I have chimed in about every aspect of the situation. But this caught my attention today:

David Barnard, AppCubby:

...Sparrow is the proverbial canary in the coal mine. The age of selling software to users at a fixed, one-time price is coming to an end. It’s just not sustainable at the absurdly low prices users have come to expect. Sure, independent developers may scrap it out one app at a time, and some may even do quite well and be the exception to the rule, but I don’t think Sparrow would have sold-out if the team — and their investors — believed they could build a substantially profitable company on their own. The gold rush is well and truly over.

It's true that the App Store has reset people's expectations with regard to the value of software, and I do believe that the race to the bottom is ultimately detrimental to indie developers. It is pretty apparent, based on what we know, that the Sparrow team didn't believe they could sustain a profitable business. But, how did we make the jump from "Sparrow could not sustain a profitable business building software" to "It's not possible to sustain a profitable business building software"?

I don't see how Sparrow's failure to build a sustainable business means that it's impossible to do so. Remember how 60% of developers don't break even?

Sparrow had a 5 person development team, and was selling a $3 app. Read that again, because it's kind of important. If you have a 5 person dev team, and you're selling a $3 app, you probably plan on selling a whole lot of apps. Between Macs and iOS devices, there are over 200 million potential devices that can run Sparrow. And every single one of them ships with a very good email client. Are you seeing the problem yet?

I believe Sparrow's failure to build a sustainable business can be attributed to:

  1. Overestimating the market for email client software.
  2. Underestimating the value of their software to it's actual target market.

I consider myself a nerd. I have a full-time job as a software developer, and I've got my own indie dev business going on the side. I have six (count 'em) active email accounts, and regularly send and receive many dozens of emails per day. But I don't own Sparrow. Why? Apple's Mail is good enough for me. Sparrow solves a problem I don't have.

I don't think I'm alone. Mail is actually a pretty good app. I don't think it even occurs to most people to go looking for a better email app, just as it doesn't occur to most people to go looking for a better web browser.

The Twittersphere exploded in a shitstorm of angry sentiment from butt-hurt Sparrow users lamenting the ultimate demise of their software. Clearly, this was a very important piece of software for those that did buy it. I'm willing to bet that most of the people that paid $3 (or $10) for Sparrow would gladly have paid much more.

Brent Simmons knew this, years ago:

I’ve been joking for years that I’m going to write an email client and charge $500 for it — an email client that actually meets the needs of developers and professionals who rely on email, folks who type for a living.

I read somewhere that this may have been the genesis of the idea for Sparrow. I hope not, because if it was, they clearly didn't read the next paragraph:

But I’m not going to, and I don’t know anybody who is. The economics of it make it kind of tough, given that Apple ships a good email client with OS X.

Sparrow was a premium product, sold at a commodity price. If BMW started selling their cars for $15,000, I don't think it would surprise anyone when they lost buckets of money, and I don't think anyone would declare that it's not possible to sustain a profitable business selling cars.

Marco Arment nailed it:

Don’t blame Sparrow. Blame the terrible market for email clients.

Just because most apps are $0.99, doesn't mean yours has to be. What is your software worth to your customers? Seth Godin said, "The problem with the race to the bottom is that you might win…"

Saturday, July 21, 2012

High resolution timing in Cocoa

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

For profiling app performance, it's necessary to accurately time your code. The best way to do this is to use mach_absolute_time. In conjunction with mach_timebase_info, you can get extremely high resolution timing to ease performance benchmarking.

I've wrapped this functionality up in a little class to make it super easy to use.

// MachTimer.h
#include <mach/mach_time.h>

@interface MachTimer : NSObject
    uint64_t timeZero;

+ (id) timer;

- (void) start;
- (uint64_t) elapsed;
- (float) elapsedSeconds;
// MachTimer.m
#import "MachTimer.h"

static mach_timebase_info_data_t timeBase;

@implementation MachTimer

+ (void) initialize
    (void) mach_timebase_info( &timeBase );

+ (id) timer
#if( __has_feature( objc_arc ) )
    return [[[self class] alloc] init];
    return [[[[self class] alloc] init] autorelease];

- (id) init
    if( (self = [super init]) ) {
        timeZero = mach_absolute_time();
    return self;

- (void) start
    timeZero = mach_absolute_time();

- (uint64_t) elapsed
    return mach_absolute_time() - timeZero;

- (float) elapsedSeconds
    return ((float)(mach_absolute_time() - timeZero)) 
        * ((float)timeBase.numer) / ((float)timeBase.denom) / 1000000000.0f;

You'd use it like this:
MachTimer* aTimer = [MachTimer timer];
[self performSomeOperation];
NSLog( @"performSomeOperation took %f seconds", [aTimer elapsedSeconds] );
I've used this code for iOS and Mac OS apps, and it works great.

Thursday, July 19, 2012

Retina Graphics in Mac OS X

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

I recently added Retina graphics to the Mac version of PuzzleTiles. I had Retina-ized TaskLog prior, and found that to be pretty trivial, as it's not a very graphics-heavy app. PuzzleTiles has many hundreds of images, so it was a bit more of a challenge.

The whole process is pretty straightforward, especially if you've done Retina graphics for an iOS app. The TL;DR is:

  • Make double-size images and suffix them with @2x
  • Use NSImage imageNamed: to load them
  • Use Quartz Debug to test your new graphics (if you don't have a Retina MacBook Pro)
The code

The main thing is to use

NSImage imageNamed:
It does all the @2x image-loading magic. One thing not immediately obvious to me is that you don't specify the extension when doing so.

NSImage* myImage = [NSImage imageNamed:@"foo.png"];
// NO, this makes the magic not work.

NSImage* myImage = [NSImage imageNamed:@"foo"];
// YES, magic is a-comin'.
The art

Producing 2x artwork was, by far, the biggest task. Luckily we'd had the foresight to make all our source art with vector graphics (text, shapes, and effects layers) which are basically arbitrarily resizable.

The first step was batch upsizing everything to 2x. I ended up making a separate set of source art for our @2x images, so that we could tweak them by hand. Some of the artwork we upsized with no other changes, but for some of them we wanted to tweak some things: line or shadow thickness, for example. Some others (the Wood tile set, for example) had bitmaps which we had to completely redo.

My advice is this: if your source art isn't vector-based, it might be time to bite the bullet and do that.

If you're like me and don't have a graphic artist on staff to do all this stuff for you, I have a few pieces of advice to ease the Retina-izing process:

  • Get familiar with PhotoShop's automation tools (File->Automate->Batch and File->Scripts->Image Processor). This saved me tons of time when converting, resizing, and exporting hundreds of images at once.
  • Get a tool for batch file renaming. At one point I decided to change my file naming convention (to make things more amenable to using imageNamed:), and hand renaming nearly a thousand files would have been a time-consuming, error-prone task. I ended up using Core-Renamer for this, and it worked perfectly.

If you don't have a Retina MacBook Pro, you can use any old Mac to test out your Retina graphics. Download and install Graphics Tools for Xcode (from within Xcode, choose Xcode->Open Developer Tool->More Developer Tools). Run Quartz Debug, choose Window->UI Resolution, and check "Enable HiDPI display modes". Now, when you go to Displays in System Preferences, you'll see a bunch of HiDPI modes. Pick one, run your app, and see how it looks.


The WWDC 2012 session "Introduction to High Resolution on OS X" is fantastic; I highly recommend checking that out.