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




JTSActionSheet – Simple, Customizable Replacement for UIActionSheets on iPhone

If you’ve spent any time working with UIActionSheet, then you’ve probably bumped up against some of its frustrating API limitations. My new open-source replacement, JTSActionSheet, will help you if your project has any of these requirements:

Check it out on Github. Feedback is welcome. The above screenshots are both made with JTSActionSheet. It will be a significant part of my next iOS app, code named “Time Zones.”

|  25 July 2014




Shawn Blanc’s Delight is in the Details – New Version has an Audio Interview with Yours Truly

It’s 2014. Books you already own can be updated with new stuff, which is awesome. If you own Shawn Blanc’s Delight is in the Details, there’s a new version out today with new chapters, videos, and audio interviews.

Shawn invited me to record an audio interview for this new update. I’m really satisfied with how our conversation turned out. Give it a listen if you’d like to hear me speak candidly about my creative upbringing, healthy shame, and how the sausage gets made when I’m working on an app.

If you haven’t read Shawn’s book already, there hasn’t been a better time to buy it. As a person who does creative work, I found it simultaneously challenging and reassuring.

|  23 July 2014




UIFontWeightTrait Ignored When Creating a New Font – TextKit Bug or a Jared Goof?

Given the following code and a device running iOS 7.1 or later:

 NSDictionary *fontTraitsDictionary = @{UIFontWeightTrait : @(-1.0)};
 NSDictionary *attributesDictionary = @{
                                       UIFontDescriptorFamilyAttribute : @"Helvetica Neue", 
                                       UIFontDescriptorTraitsAttribute : fontTraitsDictionary
                                       };
 UIFontDescriptor *ultraLightDescriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:attributesDictionary];
 UIFont *shouldBeAnUltraLightFont = [UIFont fontWithDescriptor:ultraLightDescriptor size:24];

 NSLog(@"%@", shouldBeAnUltraLightFont);

I would expect the value of shouldBeAnUltraLightFont to be an instance of HelveticaNeue-UltraLight, but instead it is:

<UICTFont: 0x908d160> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 24.00pt

I am following the Apple documentation as far as I understand it. Why is the font family and font weight information completely ignored?

Things I’ve Tried

Here are some of the things I’ve tried:

Regardless of these changes, the font returned is always a vanilla instance of Helvetica at normal weight. The only exception is when I remove the UIFontDescriptorTraitsAttribute key/value pair from the attributes dictionary. This results in a 24-point, normal-weight instance of whatever font family I specified.

Things I Can’t Use

Why This Matters

A design calls for an ultra-light system font, which in practice should be an instance of Helvetica Neue UltraLight. I could manually specify something like this:

font = [UIFont fontWithName:@"HelveticaNeue-UltraLight"];

The problem with this approach is that it’s prone to errors. When iOS 7.0.3 was released, Apple quietly renamed all the Helvetica Neue italic font filenames, which broke a lot of apps that were manually specifying the old font names. So I need a solution that is resistant to future errors of this kind, but still allows me to get a specific weight of a system font.

I’ve cross-posted this to Stack Overflow if you have an answer.

|  23 July 2014




Why are Inflation and Deficits Bad?

My old college friend, writer and philosopher Adam Kotsko, on the misleading common rhetoric about defecits and inflation:

A disproportionate amount of political debate centers around vague abstractions: government spending, deficits, and inflation. The latter two are supposed to be particularly horrible, leading to hyperinflation (and therefore Hitler) or else mountains of debt that are impossible to pay off (and therefore Hitler).

A moment’s reflection will reveal that these three technocratic abstractions are actually code words for “stuff that makes rich assholes powerful.”

|  11 July 2014




Birthday Cake M&M’s – The Dawn of a New Era

I tried Birthday Cake M&M’s for the first time tonight, and they are far stranger than I had expected. I had assumed that, like their Peanut Butter or – crucially – their Pretzel forbears, each candy piece would contain a morsel of actual cake. The truth is that Birthday Cake M&M’s are concocted to taste like fake processed chocolate cake – an artificial artificial flavor.

I’m tempted to cite the Jelly Belly’s Dr. Pepper jelly bean as a precursor, but that’s merely intended to amaze the customer with a feat of flavor science. In contrast, Birthday Cake M&M’s take food engineering as a given, presenting the (synthetic) synthetic cake flavor as an end in itself. Birthday Cake M&M’s assume that the customer already craves artificial chocolate cake for its own sake, not because it tastes like a slice of Real™ chocolate cake (which it doesn’t).

In the ontology of Birthday Cake M&M’s, it isn’t:

artificial chocolate cake flavor

but instead:

artificial chocolate cake flavor

It’s essential to point out that artificial chocolate cake flavor cannot exist independently of Birthday Cake M&M’s. On the contrary, it is Birthday Cake M&M’s that bring artificial chocolate cake flavor into being, as a pond ripple is born from a stone.

Birthday Cake M&M’s shift the boundary between the real and the artificial forward by many millenia. It’s as if everything that came before artificial chocolate cake has now been blessed. Agricultural advancements, technological innovation, manufactured goods, indeed the whole of human ingenuity, have been adopted into the family of the real – at least up through the point in human history that cake mix started shipping in a box.

Thus, through the second-order synthesis of the Birthday Cake M&M, the first-order synthetic is no longer distinct from what is genuine. The synthetic is synonymous with the real. Technology is nature, and vice versa. The Buddha is at home in a Xanax bottle.

A new era has dawned.

|  11 July 2014




“The Best of Both Worlds” – Yours Truly on CMD+Space

I was a guest on today’s episode of Mike Hurley’s CMD+Space podcast. Check it out if you’re curious about my thoughts on the past and future of App.net, the strategy behind the design of Unread, and my feelings on the decline of the indie iOS software business.

|  3 July 2014




They Make Such Bloody Good Cameras

The iPhone makes it so easy to feel like a great photographer. The following photo was made in two steps: a quick one-off shot straight from the lock screen camera, and a VSCO Cam filter (with a minor crop).

My wife and son.

This is one of the best photos I’ve ever taken, and it took less then sixty seconds to shoot, edit, and share it. Here’s the untouched original:

Untouched original.

Bravo, Apple.

|  2 July 2014




I Love Teleportation - A Summary of a Wishful Day

|  1 July 2014





Links