Iterate 74: Designing for iPhone 6 and Apple Watch, with Kawano, Sinclair, and Wiskus

I was a guest on last week’s episode of the Iterate podcast. There was so much to talk about after Apple’s announcements. My biggest concern: I’m worried that many apps won’t figure out how to feel physically comfortable on iPhone 6 and 6 Plus. My biggest point of excitement: I can’t wait to see what people do with the Apple Watch, especially for customers who can’t use their iPhones while they work.

|  18 September 2014




Good Design is a Process, iPhones 6 Edition

From John Gruber’s review of the iPhones 6 (emphasis added):

My understanding, talking to people at the event last week, is that Apple’s industrial design team mocked up prototypes of every single size between 4.0 and 6.0 inches, in tenths-of-an-inch increments, and from those 20 sizes selected the two that best hit the sweet spots for “regular iPhone” and “ginormous iPhone”.

Two takeaways with regards to my post yesterday about healthy design processes:

|  17 September 2014




Good Design is About Process, not Product

A designer’s process determines the difference between mediocre and great work. Natural talent and training aren’t substitutes for good design habits. The right process can cover many shortcomings of talent and skill — but the opposite is never true. A good process will bring out your best and most unique work. A bad process will leave you with tired, unsurprising clichés.

The easiest way to develop a healthy design process is to copy the good habits of designers whom you admire. In this post I’ll share practical suggestions that I’ve borrowed from my favorite designers — and from a few bright thinkers.

This post ended up much longer than I had anticipated. It could probably be broken up into many smaller posts, but I think it’s important to see all these lessons together, to emphasize the accidental nature of their origin. Great advice seldom arrives when you’re expecting it.

TL;DR

The list below is the short version of what I’ve learned. These aren’t fluffy aphorisms to comfort you on a rainy day. Brace yourself for these as if they were cold water splashed on your face. If you take them seriously, they will reshape the way you work, whether solo or as a team.

Dumpster Diving

You Won’t Believe These Three Simple Steps to Being a Great Designer is not the title of this post. If only being a great designer was that simple. Isn’t everyone tempted to calm the fear of failure with the comfort of a checklist?

When I look at a design that inspires me, I try to imagine myself making it. Many times I have emerged from that exercise of imagination empty-handed. I couldn’t figure out how other designers were making such great work. I felt disheartened, as if they were guardians of a secret knowledge that I wasn’t allowed to have.

It was a long time before I realized that I was solving the problem in the wrong direction. I was beginning with the inspiring result and trying to work my way upstream to the origin of a design. When I looked at the history of a project from a backwards vantage point, every decision seemed obvious. I didn’t see the twists, turns, and dead ends that didn’t make it into the final product. But when I changed perspectives and started studying processes, these paths became clear.

If you want to learn design, look in a good designer’s wastebasket.

When you study another designer’s trash, you will uncover the processes that drive her work. How many iterations of an unused idea were made before that idea was finally thrown away? How much variety can you find in the attempts at solving a particular problem? What common traits kept popping up between revisions? Leonard Cohen wrote, “Poetry is just the evidence of life. If your life is burning well, poetry is just the ash.” The tangible results of all creative acts are just the ash left behind by the way we work.

Healthy Design Processes

What makes a design process healthy? I have some practical answers to this question. What I have to share comes from a variety of sources. These are in no particular order:

Let’s look at each one of these in detail.

1. Process, not Product

I am continually inspired by a little book called Zen in the Martial Arts by Joe Hyams. Hyams was a journalist who developed a late-blooming passion for martial arts. His book has little to do with either Zen or the martial arts, except incidentally. The real joy of the book is the copious practical wisdom it has to offer.

What follows is one of my favorite chapters, reprinted in full (emphasis added), about how focusing on process and not the end product can paradoxically help you succeed at both:

Master Bong Soo Han is a Korean of medium height with a full head of iron-gray hair. There is quiet authority in everything he says and does. No movement or word is superfluous. He is the traditional martial artist who learned hapkido from his master in Korea who, in turn, learned it from a master who had been taught by a long, continuous line of other masters. A session with Master Han is not just a workout, it is also a lesson in life. I always feel enriched after leaving his dojang.

I was fifty years old when I started the study of hapkido with Master Han. From the beginning the learning process was slow and often difficult for me because hapkido requires an extremely limber body. My body had stiffened with age and I had back problems that threw me off balance and made every kick above waist level painful. My learning was further complicated by the presence of much younger men who were able to do easily that which required tremendous effort and concentration on my part. There were many times when I considered quitting, a fact Master Han recognized.

One afternoon following a workout, Master Han invited me to have tea with him. After he had served the tea, he began, “You will never learn to do any endeavor properly unless you are willing to give yourself time. I think you are accustomed to having everything come easily to you, but this is not the way of life or of the martial arts.”

“I am patient,” I said.

“We are not talking now about patience,” he answered. “To be patient is to have the capacity of calm endurance. To give yourself time is to actively work toward a goal without setting a limit on how long you will work.

He had touched the core of my problem. I had given myself a set amount of time to become reasonably proficient in his style, and I was frustrating myself because I didn’t seem to be achieving the goal quickly enough. When I eliminated the deadline from my mind it was like removing a weight from my body. Within a few months I was able to perform with the rest of the class.

Equally important, I used Master Han’s advice to resolve an immediate problem. I was working on a book at that time, and the writing was going slowly. That frustrated me because I had agreed to start another project in short order and it was weighing on my mind. Now I could see that my focus was wrong. I was doing the same thing I had done with hapkido. I should have been concerned with the process of working on the book rather than on its completion. Once I removed the time constraint from my mind and approached the book without an arbitrary limit, I was able to dedicate myself to the writing and work without anxiety.

Hyams was a writer, not a designer, but I think his advice applies equally to all forms of creative work.

2. Open vs. Closed

In 1991, John Cleese delivered a lecture on creativity to an audience of delighted Norwegians. This obscure lecture has been a life-changing discovery for me. It is only 36 minutes long, yet it rewards many re-watchings. For the sake of brevity I’ll summarize his points as succinctly as I can.

Creativity is a way of operating, a habit of the mind — not a talent. When you grasp this fact, it becomes painfully clear that the way we organize our time and our interactions with colleagues often undermines the very creativity we’re supposedly chasing after.

Creative sessions should be kept formally separate from the hurried mundanity of getting things done. When we need to solve a problem that requires creativity, we should deliberately shut out all of that noise and stress. For a clearly-defined interval of time — Cleese suggests an hour and a half — we enter a state of humorous, open-ended play. Within this cocoon of play, we strive to think of as many ways to view a problem as we can muster.

The point is not to solve the problem (though that will eventually happen), but merely to explore it. The urge to find a decision and pass judgement will destroy the fragile creative process. Instead, postpone judgement until the allotted time for creative work has lapsed. Only then should you return to a “closed” mode, in which you are judging and implementing the plans that your creativity has inspired. Repeat the cycle of open and closed modes with regularity.

My summary of Cleese’s lecture is a weak paraphrase at best. You really ought to do yourself the favor of watching the entire video. It’s rife with his characteristic wit. Prepare to laugh and be cheerful.

3. Be Indecisive

Marc Edwards, a knowledgeable designer and a founder of Bjango, recently posted a moving article about his memories of a former boss, a design director at a graphic design firm.

Marc recalls how he at first misjudged his boss as being flaky and indecisive. Marc later learned that there was a career-defining lesson hidden in his boss’ madness:

To me, he didn’t appear to know what he was doing. He’d fumble around, eventually landing on something that may or may not be final. It was ok, but not great. Then he’d ask for more changes. Changes that were obviously bad.

[…]

Then it clicked.

He’d intentionally try different and crazy things, knowing that most wouldn’t work. He didn’t care. He didn’t care and it didn’t matter — we’d end up in places we never would have if we over thought the layout. The question wasn’t “what is the best way?”, but “what are the many ways?”, deferring judgement until the last possible moment. Judgement may feel good, but it has no value. The value is in the outcome.

And the outcome was often solid, stunning designs that were unconventional. Non-obvious solutions. From the outside and to other art directors, it appeared magical. But, from within the process, far less nuanced and intentional.

It was also playful — the exploration was fun. Quickly throwing many alternatives together established a rhythm. It meant no one was invested in any particular direction. It also meant we never had designer’s block, because we allowed ourselves to create bad layouts, knowing it may be a bridge to a better solution.

I especially loved Marc’s last observation about how his boss’ methods prevented designer’s block. In retrospect, I can see now that whenever I have experienced designer’s block it has been out of a premature urge to make a decision and alleviate the discomfort of a not-yet-solved problem.

4. Intervals, not Deadlines

Aza Raskin, an accomplished designer who is now a VP at Jawbone, wrote a blog post in which he recounts the fascinating insight that led Paul MacCready to build the first human-powered vehicle capable of crossing the English Channel.

In 1959, a wealthy British magnate named Henry Kremer offered a cash prize (equivalent to $2.5 million in today’s dollars) to the first team who flew across the English Channel using only human power. Eighteen years passed by before MacReady won the prize. Many had tried and failed.

MacReady’s fundamental insight was that those who had come before him misunderstood the nature of the problem to be solved. All the previous teams spent many months designing and building their planes. Most would crash after a few minutes or even seconds, requiring a lengthy period of recalculation and rebuilding. MacReady saw things differently:

The problem was the problem. Paul realized that what we needed to be solved was not, in fact, human powered flight. That was a red-herring. The problem was the process itself, and along with it the blind pursuit of a goal without a deeper understanding how to tackle deeply difficult challenges. He came up with a new problem that he set out to solve: how can you build a plane that could be rebuilt in hours not months. And he did. He built a plane with Mylar, aluminum tubing, and wire.

The first airplane didn’t work. It was too flimsy. But, because the problem he set out to solve was creating a plane he could fix in hours, he was able to quickly iterate. Sometimes he would fly three or four different planes in a single day. The rebuild, retest, relearn cycle went from months and years to hours and days.

MacReady’s insight is especially applicable to software design. If your team is comprised of multiple designers and developers, you are probably familiar with the tendency for long delays — days or weeks — between a new internal build and some design feedback, or between that feedback and a revised build. Such delays are a serious problem. It makes it hard for engineers and designers to keep finicky implementation details fresh in their minds.

Every speed improvement counts. Companies like Flipboard and Facebook use automated beta deployments and in-app design tweaks to eliminate mundane tasks like manual deployments and updating hard-coded user interface constants. Live preview apps like Skala or Flinto make it possible to preview a design directly on your device with as little overhead as possible. When you focus on shortening the turnaround time between each iteration of a design, you’re freeing your team to collaborate more efficiently and to deliver their best work.

5. Redesign is the Essence of Design

In his book On Writing Well, William Zinsser makes an eloquent case for the importance of rewriting:

Rewriting is the essence of writing well: it’s where the game is won or lost. That idea is hard to accept. We all have an emotional equity in our first draft; we can’t believe that it wasn’t born perfect. But the odds are close to 100 percent that it wasn’t. Most writers don’t initially say what they want to say, or say it as well as they could. The newly hatched sentence almost always has something wrong with it. It’s not clear. It’s not logical. It’s verbose. It’s clunky. It’s pretentious. It’s boring. It’s full of clutter. […] The point is that clear writing is the result of a lot of tinkering.

I would argue that redesign is the essence of design. No one, no matter how talented, lands on the best version of her work in the first attempt. Shipping a first or second draft is wishful thinking at best, if not outright laziness. There is always some room for improvement: something new to add, an excess to remove, things to rearrange. New ideas often spring upon us when we least expect them. If you’re in a state of perpetual readiness, you can respond to new ideas without having to fight the inertia that wants you to accept average work.

6. Opinionated Principles, Liberal Implementations

There’s a common refrain among certain software design circles:

Make opinionated software.

The idea is, as the 37 Signals writer puts it:

The best software has a vision. The best software takes sides. When someone uses software, they’re not just looking for features, they’re looking for an approach. They’re looking for a vision. Decide what your vision is and run with it.

I think this is valuable advice, but only when it’s understood in a helpful way. I have seen it used to justify the brash ego of designers who refuse to compromise on minor matters.1 Good opinionated design has little to do with personal taste. It’s about building a firm foundation for your project out of a handful of guiding principles.

Good design is opinionated in its principles, yet liberal in their implementation. The best example of this is the iMac.

There have been many iMacs over the last decade and a half, each one different from the previous — sometimes dramatically different. Yet each one has been unmistakably an iMac. This is because each version was a interpretation of a few unchanging principles:

Apple’s hardware designers allowed themselves the freedom to express these principles in as many ways as they could imagine, yet they never allowed themselves to question the underlying principles behind every iMac. This is the essence of opinionated design.

When you’re embarking on a new project, or on the redesign of an existing project, before you do anything else you need to find your principles. Don’t open Photoshop or Xcode until you have studied the problem you wish to solve and can describe how you want your solution to feel. Distill those thoughts into a handful of principles. Write them down. Memorize them. Make a poster out of them.

Your principles will give you clarity and objectivity when solving hard implementation problems. Without principles, the only objectivity is the person with the loudest voice in the room. Every decision should be weighed against your guiding principles. This is how the best ideas win.


  1. I know I have certainly been guilty of this. 

|  16 September 2014




Unread is Now a Supertop App

Supertop, the folks behind Castro — my favorite podcast app — are the new home for Unread, the RSS reader for iPhone and iPad that you have probably heard of by now. I am really happy for this news. Since I moved on to my new job at Bloglovin, Unread had become something akin to a beloved but grumpy family dog in a home with a fragile new baby. It’s better for everyone that Unread has moved on to a new home: better for me, for Supertop (obviously), and most of all for Unread’s users.

I’m proud of the work I put into Unread, and can’t wait to see what Supertop does with the foundation I laid down. Unread has the cleanest code I’ve ever written for a personal project, so I’m hopeful that it won’t be a burden for Oisin and Padraig to wander through it. The hardest part has been dealing with maddening App Store policies. You would not believe the hoops they’re having to jump through to try to migrate existing users to the new Supertop version. They’re working really hard to make it a great experience for everyone who is already an Unread customer.

|  11 September 2014




The Best of All Possible Xcode Automated Build Numbering Techniques

At Bloglovin, we use an automated build numbering setup in Xcode that sets the build number to the current number of git commits in the active git branch. It has worked mostly well, though our technique has not been without frustrations.

The Frustrating Old Way

The technique we have been using is based on this post by the inexcusably-named “Cocoa is my Girlfriend”1. It works as follows:

  1. A run script grabs the git commit count and echo's it out to a file called InfoPlistWhatever.h. The name doesn’t matter.

  2. The build settings for the target enable Preprocess Info.plist File and set the value of Info.plist Preprocessor Prefix File to our file from step 1.

  3. The file from #1 sets the git commit count as the value of a #define preprocessor variable called CUSTOM_BUILD_NUMBER or something to that effect.

  4. The Info.plist screen in Xcode is updated to use CUSTOM_BUILD_NUMBER instead of an actual build number.

This technique works as advertised, but it has several really annoying drawbacks:

The Best Possible Way

After much Googling, I came across this post by an anonymous commenter. This technique is far better. It avoids all of the pitfalls of our previous technique, and is even easier to set up.

All you need to do to enable this technique is to add a run script build phase any time after “Copy Bundle Resources”:

#
# Set the build number to the current git commit count.
# If we're using the Dev scheme, then we'll suffix the build
# number with the current branch name, to make collisions
# far less likely across feature branches.
# Based on: http://w3facility.info/question/how-do-i-force-xcode-to-rebuild-the-info-plist-file-in-my-project-every-time-i-build-the-project/
#
git=`sh /etc/profile; which git`
appBuild=`"$git" rev-list --all |wc -l`
if [ $CONFIGURATION = "Debug" ]; then
branchName=`"$git" rev-parse --abbrev-ref HEAD`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild-$branchName" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
else
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
fi
echo "Updated ${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"

This script uses the PlistBuddy command line tool to edit the build number of the Info.plist in the /foo/DerivedData/bar directory, the target’s build directory. This is why git isn’t dirtied when the build number changes. It doesn’t matter what you’ve entered in your Info.plist screen in Xcode. It also updates the build number for every build, not just the first build after a total clean.

The code above includes some modifications to the code posted by that anonymous commenter. It uses the git commit count for the build number, but if it detects that we’re using our Debug build configuration in Xcode, it suffixes the build number with the current git branch name. This avoids potential build number collisions across feature branches under simultaneous development.2

Update Sep. 14, 2014 - Johan Kool, arguably the man with the coolest name on the Internet, has kindly corrected some potential bugs in my new run script. The corrections have already been applied above.


  1. Seriously. It’s 2014. Rename it. 

  2. At Bloglovin, we have fix or six new feature branches in active development at any given time. Build number collisions happen daily. While not a huge problem, I don’t like that feature-specific builds will be hard to identify in crash logs. This new method of suffixing the build number should alleviate this problem. 

|  10 September 2014




Time-Saving TextExpander Snippets for Xcode

I literally cannot get through a work day without using TextExpander. It’s one of the first things I install on a new Mac. Over the years I’ve built up a lot of time-saving Xcode TextExpander snippets. I’ve compiled all the universally applicable ones into this exported backup. Here are some of the highlights from my collection:

slef, vodi

self, void

These fix my two most common typing mistakes in Xcode.

ttableview

This fills out an entire stub .m file for a table view controller.

First copy the name of your new UITableViewController subclass into the clipboard. Delete all the text from the stock .m file that’s created. Then type ttableview. It expands into a bare-bones table view controller. It’s especially handy when you know you won’t need to uncomment the editing methods of the default Xcode template.

ssingleton

+ (instancetype)sharedInstance {
    static dispatch_once_t once;
    static %clipboard * sharedInstance;
    dispatch_once(&once, ^ { sharedInstance = [[self alloc] init]; });
    return sharedInstance;
}

Used prudently, a singleton isn’t a liability. But if you’re going to use it, make sure you’re initializing it correctly: once and in a thread-safe manner. Like the ttableview one above, start by copying your class name into your clipboard.

dmain

dispatch_async(dispatch_get_main_queue(), ^{

});

This is the preferred way to kick off a block of code to be executed on the next turn of the main thread runloop. No, _async is not a typo. I have never had a need to use _sync, and often just the opposite: the synchronous call can cause terrible race conditions.

dbg

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

});

dafter

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        
});

I use this one a lot when debugging new code, for example, when simulating a long-running background operation before that background code is available to build against.

wself

typeof(self) __weak weakSelf = self;

People have all kinds of solutions for this problem, but I prefer to use a native solution over a third-party dependency when it’s convenient to do so. This snippet makes it trivial.

kkvo

static void * JTSSomeContext = "JTSSomeContext";

#pragma mark - KVO

- (void)addObservationsTo:(NSObject *)object {
    [object addObserver:self forKeyPath:@"keyPath" options:NSKeyValueObservingOptionNew context:JTSSomeContext];
}

- (void)removeObservationsFrom:(NSObject *)object {
    [object removeObserver:self forKeyPath:@"keyPath" context:JTSSomeContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == JTSSomeContext) {
        if (object == self.myObject) {
            if ([keyPath isEqualToString:@"keyPath"]) {
                // Stuff
            }
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

I don’t ever use the result of this snippet as-is — it needs a specific target class and key path — but it sure saves a helluva lot of time and potential errors, like forgetting to call super.

fformat

[NSString stringWithFormat:@""]

This one even drops your cursor between the double quotes in the format string.

llocal

NSLocalizedString(@"", @"DESCRIPTION_HERE")

This one, like fformat above, drops your cursor between the first double quotes. You’re less likely to forget to localize a string if you make this snippet a muscle memory.

lltab + suffix variants

NSLocalizedStringFromTable(@"

If my project is using NSLocalizedStringFromTable() instead of NSLocalizedString(), I use the lltab snippet in combination with a project-specific suffix snippet. For example, in Unread I use lltun which turns into:

", @"Unread", nil)

The workflow is then lltab + text + lltun, the latter of which becomes a muscle memory pretty quickly as I get deep into a project.

ccoding

#pragma mark - NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [NSCoder encodeObjectIfNotNil:_item forKey:@"key" withCoder:aCoder];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        _item = [aDecoder decodeObjectForKey:@"key"];
    }
    return self;
}

bgtask

 UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        // handle expiration
   }];
    
    // Do some long running operation then:

   [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];

|  31 August 2014




Finding Objectivity When You Feel Lost as a Designer

Francesco Di Lorenzo, an iOS developer, wrote to me1 a while back:

I am stuck with personal projects because I feel like I have reached the limit of what I can do with my current design skills. Could you give me some hints on where to go to learn the basics of design? I can’t get my head around even really basic stuff like color theory and simple typography because I miss the fundamental concepts. As of today I design following my instinct and by imitation.

The bad news for Francesco is that he wrote to the wrong guy. I feel the same way, all the time! I especially identify with that last line:

I design following my instinct and by imitation.

The good news for Francesco is that this is, from what I have observed, a universal experience among people in creative professions. Creative work is open ended. There are no objectively wrong solutions to creative challenges, yet there is still a sense that you’re obligated to demonstrate expertise. Folks in creative professions end up caught in the tension between the freedom to do anything and the duty to become an expert. I am not referring to what is sometimes called "Imposter Syndrome," though that is a related concept. Instead, I’m talking about how we judge what is good and what is not good.

There’s a scene in the film Basquiat in which Andy Warhol, played by David Bowie, is collaborating with Basquiat on a painting. Warhol is taken aback by the changes Basquiat makes:

Basquiat scribbles lines and text across pristine areas of Warhol’s silk-screened logos.

WARHOL

What are you doing? You’re painting out everything I do!

Warhol goes silent while Basquiat keeps going. Warhol looks like he’s on the verge of changing his mind.

WARHOL

[Genuinely] Wow, that’s great.

Basquiat steps back from the canvas.

BASQUIAT

There. That’s better.

WARHOL

You really think so?

Warhol sighs.

WARHOL

I can’t even see what’s good anymore.

I have no idea if that bit of dialogue is true-to-life, but I love it just the same. It’s both reassuring and terrifying to watch an artist like Warhol — either a revolutionary new master or an iconoclastic fool, depending on whom you ask — find himself just as lost in front of his work as you or I might feel in front of ours.

There are no Absolutes

Whatever your religious or moral convictions might be in the rest of your life, in art there are no absolutes. Nothing is truly good or bad. There are only two forms of certainty:

  1. Does the work successfully embody the artist’s intention?

  2. Is the artist’s intention admirable?

Let’s use television shows as an example. The original Law & Order may not have the same gravitas and high-art merit as a golden-age-of-TV series like The Sopranos, but that doesn’t mean it isn’t a great show. The shallow characters and cheeseball zingers are part of the game. Law & Order is a near-perfect expression of a certain kind of entertainment. The intention behind it is different from shows like The Sopranos. It’s unfair to judge them by the same criteria.

The same is true of any creative work, from iOS app design to fusion cuisine. You choose your rules out of thin air. But choose them carefully. There’s no objectivity outside of your intention, except the question of whether your intention is admirable in itself. A pornographic film, for example, might be of good or bad quality, but the intention behind it is less admirable than the intention behind a given art house film with the same visual content. It takes wisdom to see past the particulars and into the intention.

Objectivity and Taste

I think the way out of a dilemma like Francesco’s is to shove aside one’s fears and focus first on choosing an appropriate set of rules. There’s no pure objectivity, so we need to find something to do the job of objectivity in its place. We just pick something. Anything. It’s messy, but it isn’t as hard as it sounds.

It starts with taste. Everyone has her own taste. Your taste is a composite of the tastes you’ve borrowed from people you admire and the taste you discover for yourself. Wading through this mixture, you will usually find yourself drawn to certain kinds of artistic intention. The important part is choosing an intention that you can cling to when the work gets hard.

I’m reminded of my friend Kris Donegan, a Nashville session guitarist and one of the best musicians I have ever known. Kris doesn’t play every style of music. He could, if pressured to do so, play highly technical, meedly-meedly-meedly-mow, Malmsteemish math rock — but it would require an enormous amount of effort and practice to get there. Instead, Kris plays a distinct style of gutsy Americana rock guitar. He works diligently on crafting a tone that is uniquely his own: a certain touch on the strings, a curated collection of effects and boutique amplifiers. Kris is an ingredient which, when added to a song, makes it richer and more flavorful. To a math rock fan, Kris’ playing is all wrong. But to the people who continue to seek out Kris for their albums, his playing is just right.

Always Bust Ass Like a Beginner

In his email to me above, Francesco asked:

Could you give me some hints on where to go to learn the basics of design? I can’t get my head around even really basic stuff like color theory and simple typography because I miss the fundamental concepts.

Like Francesco, I have little or no formal training in these subjects, but I don’t think that’s a problem — not for creative work. In creative work, formal training is ninety percent bullshit. The only thing that really matters is your willingness to always be learning, to always bust some ass like an eager beginner. I read with awe this post by the developer of Capo, in which Chris Liscio describes how he took on the task of learning how to write machine learning software in order to improve a chord detection feature by leaps and bounds:

When I returned from NAMM and came down from all the excitement of the show, I decided that enough was enough and I needed to tackle all the research I was uncomfortable and afraid of. I had never taken an Artificial Intelligence or higher level Statistics courses at school, but all the research papers I was reading over the years made frequent references to concepts that I was completely unfamiliar with.

I re-read all the papers I’ve used over the years for reference, and read them again. I got in touch with Taemin Cho to get some clarification on some of his work, and he led me to newer papers which required additional learning on my part. For a solid 6 weeks I was doing nothing but reading papers and exploring in MATLAB.

I resolved to not just build a new chord detection engine for Capo, but to build an entire chord detection engine factory. Armed with my copies of MATLAB and Xcode, and an unwillingness to fail, I set forth on my quest.

So, Francesco, my answer would be to look in places so difficult to understand that they scare you. You can handle it. Let me know how it goes, you’re probably braver than me.


  1. Thanks, Francesco, for permitting me to repost this here. 

|  31 August 2014




Quotebook: a Commonplace Book for Your iPhone or iPad

My friends at LickabilityMatt Bischoff, Brian Capps and Andrew Harrison — have launched Quotebook on the App Store today.

Quotebook is a clean take on a very old tradition. Long before the typewriter, but sometime after the printing press, avid readers would collect their favorite quotes in a single place. This collection was typically gathered into a hand-written journal called a “commonplace book,” but “Quotebook” is objectively a better name.

I like apps with attention to detail, and Quotebook has it, right down to making sure every button and label is set in a custom font and color — even the action sheets. Get it today.

|  27 August 2014




My New Job

I’m happy to say that, starting Monday, I will be joining Bloglovin as Lead iOS Engineer. They’re a smart and fun group of men and women. It will be an honor to be a part of them. I can’t speak publicly about what we’ll be doing together, but I’m excited about it.

Obviously this is a full-time staff position, so I will have to make changes to my priorities for the existing apps I have on the App Store. All of them will remain for sale indefinitely and receive regular bug fixes and compatibility releases.

Due to potential conflicts of interest between Unread and Bloglovin, I won’t be adding new features to either the iPhone or iPad version of Unread. This is by choice on my part. I like to pour myself into my work. I wouldn’t feel right doing it any other way.

I can still ship new unrelated projects on the side. My Time Zones app, for example, has been submitted for App Review and will hopefully be coming out soon.

Thanks again to everybody who encouraged me with their time and advice over the past year. I’m looking forward to what’s next.

|  8 August 2014




What’s the Most Common App Icon Color?

Except for the apps on my first home screen, all the other apps on my phone are sorted into folders by color. Some colors require more than one folder because I don’t put more than nine apps in any folder. Until recently blue has been the most common color on my phone. But over the last year the number of white app icons on my phone has steadily increased. It now takes four folders to hold all the white apps, taking the lead from blue for the first time since I organized apps in this way.

I was curious to know if my anecdotal experience could be backed up by some data. Using the same criteria that I use to sort apps on my phone, I examined the top 100 apps from all the relevant App Store categories, sorting them into groups by their predominant background color:

There is an eighth category I called “Other” for app icons that can’t be sorted into one of the above categories. These icons either had intense gradients (like Fling or Haze), very dark or neutral backgrounds (like Reeder), or had lots of clashing details or colors (like most game icons). I skipped a handful of App Store categories, like Games, since almost every icon in those categories would not be categorizable.

General Comments

The overall distribution of app icon colors, averaged across all the categories I sampled, was unsurprising except for the difference between paid and free apps:

Overall-Paid (Left) versus Overall-Free (Right)

A significant proportion of paid apps fell into the “Other” category. Most of these apps were multi-colored and excessively detailed to the point that they were aesthetically unpleasant, to my eyes. Free apps were far more likely to have a more homogenous application of a predominant background color. White and blue were very common colors across both paid and free apps, dwarfing all other solid colors.

It seems clear that, at least on the US App Store, white is objectively a very common app icon color. In some categories (Finance-Paid, Food&Drink-Paid, Lifestyle-Free) white is even the most common solid color background.

Business

Entertainment

Finance

Food & Drink

Health & Fitness

Lifestyle

Medical

Music

Navigation

News

Photo & Video

Productivity

Reference

Social Networking

Travel

Weather

|  8 August 2014




For Subclass Eyes Only: Swift, UIGestureRecognizer, and the Protected Extension Pattern

One of my favorite features that Jamin Guy and I shipped in Riposte, an App.net client we made for iPhone, was our Screen Brightness Gesture. Part of the Riposte Pro set of upgrade-only features, the Brightness Gesture enabled you to quickly fade your screen’s brightness up or down by dragging with three fingers anywhere on the screen. It worked just like a dimmer switch for a dining room chandelier.

You have to understand how awesome this was during the iOS 6 days, before the Control Center pane from iOS 7. As far as I know, we are still the only app to ever provide this feature. It was convenient and fun.

An Unfixable Bug

One drawback with the Brightness Gesture was that if you performed the drag gesture over a table view, the content underneath your finger tips would jiggle a bit before our brightness gesture recognizer took over control of touch event handling. At the beginning of the gesture, the table view would scroll about 25 to 50 points before we could disable scrolling. We mitigated this as much as we could, but the first shipping version of it had a dissatisfying amount of jiggle.

Michael "Jury" Jurewitz challenged us to find a way around this behavior. I spent the better part of a week trying everything I could think of: using a UIView subclass to contain the entire view hierarchy, scroll view delegate methods, etc. The irreducible problem was caused by the way touch events get routed through competing gesture recognizers. There was no way to prevent a touch event from being initially handled by UITableView’s pan gesture recognizer. I could cancel scrolling once it was underway, but I couldn’t prevent it from starting in the first place.

But I didn’t want to give up. There was one thing I hadn’t tried yet: method swizzling.

Swizzling Saves the Day

Finally I tried swizzling various methods on UIScrollView and UIPanGestureRecognizer. I found that swizzling two methods on UIPanGestureRecognizer provided the best solution with the least amount of intrusion into the touch event sequence. I swizzled:

- (CGPoint)translationInView:(UIView *)view;

and

- (CGPoint)velocityInView:(UIView *)view;

These methods determine the vector of a pan gesture at any given point in time. In my swizzled implementation, I returned the original implementation’s values in every case except where (in pseudocode):

(self.numberOfTouches > 2) && (self.view.class == UIScrollView)

When these conditions were met, I returned CGPointZero. This worked flawlessly. Table views remained motionless while dragging up and down with three fingers. It continued to work flawlessly after lots of testing, even after iOS 7 shipped. The jiggle was corrected, and the brightness gesture felt better than ever.

Dirty, Dirty Swizzling

I know, I know. Swizzling is dirty and wrong. No seasoned and serious developer would ever use method swizzling or private APIs1 in a production application, right? Maybe. In most cases, a satisfying user experience trumps all other concerns. But I wanted to have someone a lot wiser than me take a look at my solution and offer advice.

At WWDC in 2013, I had that opportunity. I was fortunate enough to meet one of the engineers who had actually worked on UIGestureRecognizer. I demoed the Brightness Gesture for this person and the jiggle problem it caused. In less than a minute, they rattled off a series of suggestions, listing everything that I had spent a week trying to figure out. Before I could respond to each suggestion with, “No, I’ve already tried that,” this engineer immediately came to the same conclusions. Finally they said to me:

I’ve got it! All you need to do is override this one method…

The method name was not one I recognized. The engineer sighed.

Oh yeah. You’re right. It was one of the last methods I wrote before moving on to other stuff and I forgot to publish it.

The engineer hinted that the UIKit team would look into publishing that method in the future. It and another related method eventually found its way into the UIGestureRecognizerSubclass.h header in iOS 7:

@interface UIGestureRecognizer (UIGestureRecognizerProtected)

// same behavior as the equivalent delegate methods, but can be used by subclasses to define class-wide failure requirements
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);

By simply overriding shouldRequireFailureOfGestureRecognizer: in our custom pan gesture recognizer subclass, we could guarantee that a three-finger pan would always cause any other pan recognizer to fail — and without swizzling any methods.

The “Protected Extension” Design Pattern

An interesting thing to note about the UIGestureRecognizerSubclass.h header is that it defines what could be called a protected extension for UIGestureRecognizer subclasses. The following comment can be found at the top of the header file:

// The extensions in this header are to be used only by subclasses of UIGestureRecognizer. 
// Code that uses UIGestureRecognizers must never call these.

According to this design pattern, the parent class (in this case UIGestureRecognizer2) has a private implementation of some complex logic that depends upon a handful of extension methods overridden by its subclasses. Other classes that are simply using a gesture recognizer should never call these methods since that could trigger unwanted effects. But the methods still need to be exposed in a semi-public fashion to allow subclasses to provide their override implementations.

This design pattern has lots of potential uses. One example (similar in spirit, although it doesn’t use a protected extension per se) is NSURLProtocol, which can only be used via a concrete subclass of the semi-abstract parent class. My RSS app Unread uses a subclass called UNRImageURLProtocol to route all image download requests through my own image caching system. A subclass of NSURLProtocol has to override several essential methods in order to be used. Mattt Thompson at NSHipster wrote a very informative article on this topic.

One Client, Many RSS Services

One of the architectural challenges I faced early on when developing Unread was the fact that there are so many different RSS services to integrate with. While broadly similar in user-facing features, there are numerous, non-trivial API differences that affect common tasks like refreshing all your feeds or marking an article as read. When a user pulls to refresh, a view controller needs to know how trigger a refresh call but it shouldn’t need to know what kind of RSS service is being used behind the scenes.

My final design followed the protected extension pattern. Every signed-in account owns an instance of a class called UNRClient. This class exposes some simple public methods like:

- (void)refresh:(UNRClientCompletionBlock)completion;

- (void)markArticleAsRead:(UNRArticle *)article;

- (void)markArticleAsUnread:(UNRArticle *)article;

UNRClient is mostly a concrete class. It handles things like keeping track of refresh state, access to API credentials, and — most importantly — queued and active API tasks. But every RSS service has a different set of steps to follow when refreshing. So in practice, UNRClient is only used when subclassed. A FeedWrangler account has UNRFeedWranglerClient, a NewsBlur account has UNRNewsBlurClient, and so forth.

UNRClient has a protected extension in a separate header file with required semi-private methods like:

- (UNRClientTaskGroup *)taskGroupForRefreshingAllFeeds:(void(^)(UNRClientTaskGroupCompletionItem *item))completion;

- (UNROutboundArticleStatusChangeItem *)changeItemForMarkingArticleAsRead:(NSString *)articleID;

I won’t go into the nitty-gritty details of UNRClientTaskGroup or UNROutboundArticleStatusChangeItem, but suffice it to say that these objects encapsulate units of work in an abstracted form. UNRClient knows how to enqueue these units of work, but it doesn’t ever need to know what happens inside them. Internally, the parent class has logic like this (simplified):

- (void)refresh:(UNRClientCompletionBlock) completion {
    UNRClientTaskGroup *group;
    group = [self taskGroupForRefreshingAllFeeds: completion];
    [self enqueueTaskGroup:group];
}

The magic happens in that call to taskGroupForRefreshingAllFeeds:. The super implementation does nothing. Subclasses override that method to return a task group (itself a concrete subclass of a semi-abstract parent class) configured to refresh all the feeds for its RSS service of choice.

To save myself from future mistakes, the super implementation looks like this:

- (UNRClientTaskGroup *)taskGroupForRefreshingAllFeeds:(UNRClientCompletionBlock)completion {
    NSAssert(NO, @"UNRClient subclasses must override taskGroupForRefreshingAllFeeds: without calling super. ");
    return nil;
}

This way, if another developer comes behind me and adds a new RSS service, she’ll quickly encounter an exception whenever a required protected method hasn’t been implemented by the new UNRClient subclass.

The beauty of this overall design is that the parent class is the only place where the complex, reusable logic is implemented. Any given subclass’s .m file is very short — just a handful of overrides of the protected extension methods.

Swift and Access Control Keywords

Swift is language in flux. It changes every few weeks. Apple recently added three access control keywords:

public
internal
private

These keywords are used to limit the visibility of any class, function, method, or property. Here’s how they work (as of August 6th, 2014):

Public access enables entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use public access when specifying the public interface to a framework.

Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.

Private access restricts the use of an entity to its own defining source file. Use private access to hide the implementation details of a specific piece of functionality.

So, for example, a Swift class might mix public and private methods as follows:

public class Vehicle {

    public func accelerate() {
        self.pushGasPedal()
    }

    private func pushGasPedal() {
        // do stuff
    }
}

Any class or module could instantiate a Vehicle and call:

var aVehicle = Vehicle()
aVehicle.accelerate()

But the following code would not compile if used outside of the Vehicle source file:

var aVehicle = Vehicle()
aVehicle.pushGasPedal()

Swift and the Protected Extension Pattern

Swift is still in beta, but there is no clean way — yet — to use the protected extension pattern in pure Swift code — not even with the new access control keywords. To illustrate why this is the case, let’s extend the Vehicle example above.

First, let’s define an enum for transmission types:

enum Transmission {
    case Automatic
    case Manual
}

Next, let’s implement the acceleration logic in the parent class:

public class Vehicle {
    private func transmissionType() -> Transmission {
        assert(false, "Subclasses must override.")
        return Transmission.Automatic
    }
    private func pushGasPedal() {
        // push the pedal
    }
    private func shiftUpOneGear() {
        // shift the gear up
    }
    public func accelerate() {
        var type = self.transmissionType()
        switch type {
        case .Automatic:
            self.pushGasPedal()
        case .Manual:
            self.pushGasPedal()
            self.shiftUpOneGear()
        }
    }
}

Finally, let’s implement two subclasses of Vehicle, overriding the one required method for subclasses:

public class FamilyCar: Vehicle {
    override private func transmissionType() -> Transmission {
        return .Automatic
    }
}

public class SportsCar: Vehicle {
    override private func transmissionType() -> Transmission {
        return .Manual
    }
}

This approximates the protected extension pattern as close as Swift will allow. Here are the reasons why the alternatives won’t work:

Declarations from extensions cannot be overridden **yet**

It’s that dangling “yet” that encourages me to believe that Apple’s engineers are aware of the design patterns like the protected extension pattern and will update Swift to support them.3 Personally, I’d like to see three more keywords: required, protected, and testable.

Usage of these would look like this:

public class Vehicle {
    required func transmissionType() -> Transmission {
        // subclasses must override without calling super
    }
    protected func didBeginDriving()  {
        // Any subclass source file can see this
    }
   testable func moveForwardThirtyFeet() {
        // do something inherently testable that 
        // should not be made public.
    }
}

Back to UIGestureRecognizer

A funny side-effect of Swift’s current access control options (or so I imagine) is that the UIGestureRecognizerSubclass.h extension header is not included in the UIKit Swift framework. In order to compile code that uses those methods, you have to #import that header file inside an Objective-C Bridging Header for your Xcode project.4


  1. Cough-cough—Vesper 1.0’s application background—cough-cough. 

  2. Or some other responsible controller class privately used by UIKit. 

  3. Another casualty of the current access control options is that private methods aren’t unit testable. This is a big problem in my opinion. Testable methods are often unsafe to expose publicly. 

  4. Thanks to Ash Furrow for the clarification on this. 

|  6 August 2014




Five Hard Lessons Learned from Unread

My article on the financials behind Unread’s first year prompted a lot of helpful conversation on the Internet last week. It will take me a long time to digest it all, but here are the TL;DR highlights:

|  4 August 2014




On Promotion and Marketing - A Response to Critics of Yesterday’s Article about Unread

Since posting yesterday’s article about the numbers behind Unread’s first year on the App Store, several people have asked me why I didn’t do any promotion for Unread. The answer is that, in fact, I scrounged up as much promotion as I could afford. I did not just naively dump my app on the App Store and expect the bucks to start spilling through the mail slot.

Here’s a list of the promotional activities supporting Unread (both iPhone and iPad), either directly by me or through good fortune:

Did Unread already have a significant amount of competition on the App Store? Yes. But I defy you to name a category on the store that doesn’t already have lots of competition. Could I have done a better job of marketing my app, use different screen shots, etc? Perhaps.

Arguments that I naively built and marketed an RSS reader in 2014 aren’t relevant to the heart of my article. Any polished app — in any category, with any amount of marketing or promotion — is a lottery. Increasing the marketing budget is just as likely to increase the potential losses as it is to increase potential sales. Each niche is an apple or an orange. It’s all a gamble.

The lesson to be learned from Unread is that even if you keep your costs low and your quality high, the immense scale of the App Store — 100 million credit cards — is deceptive. From the outside one might assume that an indie dev with a quality product could “fail” her way to a sustainable paid-up-front app business. The reality is that App Store sales patterns rarely support such a developer. True fans will buy her quality app within the first few days, then never give her any money again. The rest of her time will be spent trying to convince a few more users to become true fans, repeating the same short-lived, one-time purchase until she goes out of business.

|  29 July 2014




Tyler Hall’s Candid Look at the Financial Side of Building Mac Apps

Tyler Hall responds to my post from earlier today about the financials behind Unread:

Well, it’s my experience that you CAN build a sustainable software business selling to consumers as an independent developer. You just can’t do it in the App Store any longer – if you ever could.

You need to start building for the Mac, where you can charge a fair price, sell directly to your customers, and charge for upgrades. Even then, success won’t happen overnight. But it is doable.

Also, Mac sales are up 18 percent year-over-year, while iPad sales are down. It looks like the Mac isn’t just sticking around. It’s thriving.

|  28 July 2014




A Candid Look at Unread’s First Year

To my knowledge, indie developers don’t publish the kind of numbers that you’ll find in this post. My sales records are none of your business, as the saying quite literally goes. The question in your mind will no doubt be why am I writing this at all? It’s a fair question. Before I answer it, allow me to first dismiss all the unsavory reasons some readers might unfairly concoct. This is not a public whine session. I am neither a victim nor a failure. As these things go, I have been very fortunate with Unread. I have nothing but gratitude for everyone who supported me with their blog posts, tweets, pats on the back, and — above all — with their hard-earned cash.

My intention is to share the dirty details of Unread’s story with my colleagues. I especially hope that this post is useful to anyone who is considering striking out on her own, as I did last summer. Unread will no doubt be a cautionary tale for some. For others, it might be an inspiration to one-up what has been my personal best effort, proving what opportunity may still lie in the indie app market.

At the very least, I think you’ll come to agree with these takeaways:

Blood, Sweat, and Tears

I began work on Unread at the beginning of July 2013. I spent about six weeks on the overall design of the app, then plunged headfirst into Xcode, not coming up again for air until the following spring. I estimate that I worked sixty to eighty hours a week every week from July 2013 up until the launch of Unread for iPhone Version 1.0 in February 2014. Along the way were many challenges: building custom user interface navigation and controls, a vast sharing library, syncing and persistence architectures, performance tuning, etc.

Here’s what the Github punchcard looks like for Unread’s main repository:

My marriage and mental health suffered a lot because of that punchcard. I worked on Unread seven days a week, at almost any hour of the day. I think the quality and polish of Version 1.0 is due to all that extra effort, but it was physically and emotionally taxing. It’s not a sustainable way to live, and I don’t recommend it. However, if I had adhered to a saner work-week, Unread would have taken a year to finish and might not have launched at all.

Launch Week and Marketing

Thanks to the generous support of many independent writers — Federico Viticci, Shawn Blanc, Stephen Hackett, and others — Unread had a great launch week. If you use RSS or Twitter to read about what’s new in the iOS world, chances are good that you saw it mentioned once or twice.

After a huge spike in sales the first day, sales dropped in what is (to my knowledge) a typical launch week curve for iPhone apps. The week after launch, Unread was featured on the main page of the App Store. This feature did not lead to a spike in sales. Rather, it kept sales from dropping any further for that week, then tapered off. I conclude from this that an App Store feature may not be as helpful as positive, prominent reviews from influential writers.

Cumulative iPhone Sales

Here is a graph of cumulative iPhone sales, from launch to the present. The orange dotted line is the averaged trend line:

Here are the highlights from this graph. Note that these numbers are after Apple’s 30 percent cut and before I’ve set aside a portion for self-employment taxes and healthcare premiums:

Half of the lifetime sales of Unread were generated in the first five days1. It would take another 170 days (24 weeks) to generate that same amount again.

Unread for iPhone Version 1.2

Unread’s biggest mid-run spike accompanied the launch of Version 1.2 for iPhone. This was a huge update with lots of new features: two new RSS services, an image viewer, a quick way to jump between articles, etc. Version 1.2 was released on April 21st, nine weeks after the previous update.

It’s difficult to assess how much money Version 1.2 earned by itself. A close approximation might be to determine the cumulative sales between the launch of Version 1.2 and when the sales trend line returned to the previous average trend. By my estimates, this happened around May 4th, about two weeks later. That would mean Version 1.2 earned about $4,000 extra in sales, plus or minus $1K.

Since it took two months to develop, Version 1.2 earned me around $2,000/month in sales. Subtracting 40 percent for self-employment taxes2 and a $350 health care premium3, my take-home pay for that work was about $850/month.

Cumulative iPad Sales

In the interest of brevity I won’t go into detail about Unread for iPad. Here is the cumulative sales graph to date:

Suffice it to say, so far it looks like a scaled-down version of the Unread sales history:

The Bottom Line

Unread for iPhone has earned a total of $32K in App Store sales. Unread for iPad has earned $10K. After subtracting 40 percent in self-employment taxes and $350/month for health care premiums (times 12 months), the actual take-home pay from the combined sales of both apps is:

$21,000, or $1,750/month

Considering the enormous amount of effort I have put into these apps over the past year, that’s a depressing figure. I try not to think about the salary I could earn if I worked for another company, with my skills and qualifications. It’s also a solid piece of evidence that shows that paid-up-front app sales are not a sustainable way to make money on the App Store.

Conclusions

There are lots of things that I haven’t tried with Unread. I haven’t done any major promotions I tried several forms of promotion, for example $400 worth of promoted tweets on Twitter, coinciding with the launch of Version 1.2.4 I could have tried more. I haven’t added any in-app purchases or subscriptions, either. But Unread is a good test case nonetheless, for the following reasons:

Despite all of these circumstances, Unread still only earned $42K in sales ($21K after taxes and expenses) and is on a course that doesn’t promise much growth. I conclude from all this that anyone who wants to make a satisfying living as an independent app developer should seriously consider only building apps based on sustainable revenue models. I suspect this means through consumable in-app purchases, like those in Candy Crush Saga or Clash of Clans, or through recurring subscription charges, like those in WhatsApp. Furthermore, I have grave doubts that any solo developer would have the capacity to ship and maintain either kind of business working alone. She would probably have to consolidate her business with other indie developers in the same position. The marketing budgets of the major competitors makes me doubt that even a consolidation strategy is tenable.

Update – On Promotion & Marketing

Since posting the above, several people have asked me why I didn’t do any promotion for Unread. The answer is that, in fact, I scrounged up as much promotion as I could afford. I did not just naively dump my app on the App Store and expect the bucks to start spilling through the mail slot.

Here’s a list of the promotional activities supporting Unread (both iPhone and iPad), either directly by me or through good fortune:

Did Unread already have a significant amount of competition on the App Store? Yes. But I defy you to name a category on the store that doesn’t already have lots of competition. Could I have done a better job of marketing my app, use different screen shots, etc? Perhaps.

Arguments that I naively built and marketed an RSS reader in 2014 aren’t relevant to the heart of my article. Any polished app — in any category, with any amount of marketing or promotion — is a lottery. Increasing the marketing budget is just as likely to increase the potential losses as it is to increase potential sales. Each niche is an apple or an orange. It’s all a gamble.

The lesson to be learned from Unread is that even if you keep your costs low and your quality high, the immense scale of the App Store — 100 million credit cards — is deceptive. From the outside one might assume that an indie dev with a quality product could “fail” her way to a sustainable paid-up-front app business. The reality is that App Store sales patterns rarely support such a developer. True fans will buy her quality app within the first few days, then never give her any money again. The rest of her time will be spent trying to convince a few more users to become true fans, repeating the same short-lived, one-time purchase.


  1. Unread launched at an introductory price of $2.99 USD. I rose the price to $4.99 two weeks later. In retrospect, I think I left a lot of money on the table. Demand was never as high as that first week. I should have priced Unread accordingly. If I had launched at $4.99 and got the same amount of downloads, I probably missed out on an additional $16K in sales. Eep. 

  2. This seems ridiculous to anyone who hasn’t been self-employed, but it is absolutely the reality. In addition to regular federal and state income taxes, self-employed people have to pay the full amount of FICA taxes — 15 percent of income. If you work for a company, your employer pays half of that 15 percent over and above what you actually earn. Since I live in Indiana, my income taxes total around 40 percent of my income. 

  3. For me and my son, both of whom have no major medical conditions. I have mild asthma and seasonal allergies. 

  4. Which means my take-home pay for those two months working on Version 1.2 is, in effect, $650/month. 

|  28 July 2014





Links