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 Aug 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 Aug 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 Aug 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 Aug 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 Aug 2014