inessential by Brent Simmons

January 2013

Modern Software Expectations

Mark Bernstein: How Software Is Built Today:

I spent about a day this week polishing a detail of Tinderbox Six. I’d like to walk you through some of the design because it’s interesting and accessible, and because I think it demonstrates why our expectations of software are leading us into such a terrible mess.

This reminds me of a post I wrote in 2009: Anatomy of a feature, where I talk about adding Send-to-Instapaper support to NetNewsWire.

In Person with Your Tribe

I like this blog post that Matt Alexander wrote after his first Macworld last year.

Twenty or thirty minutes after meeting some of my favorite writers, we were sprinting across a train platform like it was normal. Hours (hours) later we were sitting on the Cupertino campus, chatting about life, kids, and technology. On the train home, I opened Instagram and saw that Shawn Blanc had posted a picture of Ben Brooks and I. Perhaps this all sounds very run of the mill to you, but it was truly explosive to me.

Removing Core Data iCloud Syncing

Jumsoft:

After a rather frantic month of dealing with the unfortunate iCloud sync, we made an always-difficult decision: to do a U-turn.

(Via Michael Tsai. You should subscribe to his feed.)

I almost never worry about the future of Apple — but, when I do, it’s because of this. Syncing is critical.

App.net Storage

The Verge: App.net adds storage:

You can think of it like iCloud or Dropbox with a social layer — a central place to store and share files across platforms and service silos — or you can think of it as free hosting for services that want to build photo-sharing, group-messaging, or Pinterest-like web applications using App.net’s login. In fact, App.net founder Dalton Caldwell said in an interview, a developer could bundle all of these services together and build a near-clone of Facebook.

I like the emphasis on privacy.

Magic Box

NSHipster describes the magic box that is NSValue.

I’ve almost never had reason to use NSValue — but when it’s needed, it’s needed. Just the other day I used -[NSValue valueWithCGRect:] to store some CGRects in an NSArray.

Windscape

My friend Mike Piatek-Jimenez wrote an iPhone-and-iPad app called Windscape that shows you a particle-based visualization of wind, worldwide. It’s fascinating.

App Icons

Louie Mantia:

Instead of focusing on what your app does, focus on what your app is. This will help your personal brand show through, which will be more unique and more recognizable.

Somehow I’d never met Louie until a couple days ago at the Renaissance conference — which was a good conference, and I hope they repeat it next year. I learned things and made new friends.

Louie is one-third of Pacific Helm, which you should know about.

Development Notes on Glassboard 2.3.3

Glassboard 2.3.3 for iPhone was released on the App Store a few days ago. If you don’t have the update, you should get it.

(If you don’t have the app at all but want to try it out: get it, then open the sidebar, then enter the invitation code rovhy to join the inessential board. And say hi.)

The main new feature is that links appear in blue, they’re tappable, and you can have more than one per message. (As in just about every Twitter client ever.)

Though iOS 6 lets you use an NSAttributedString with a UILabel — it lets you create formatted text — it doesn’t give you anything to handle tapping on links. It’s not enough to make them blue. They should highlight on tap and then open a browser screen.

Measuring and Rendering Text

To make this work I had to dig into Core Text, which I ended up enjoying. I shouldn’t have put it off so long.

As part of this work I re-did how the message text is measured and rendered. I’m doing measuring and rendering at the same time now rather than as separate steps. I don’t know if this is the best approach or not, but it’s the best approach I’ve found. Here’s how it works:

  1. When the app needs to know the height of a cell, it uses Core Text to render the message text. From there it’s simple to find out how tall the rendered text is. That height is cached — and the rendered text itself is cached as an image.

  2. When the app needs to draw a cell, it uses the cached image of the text. (The text is actually a UIImageView.)

  3. Memory use is considered, of course, and caches can be tossed (in whole or in part) at any time and everything will rebuild when requested.

I also had to find all the links in a message. In my entire career I’ve never shipped software that uses regular expressions. (By design.)

I tried using NSDataDetector to find links, but it wasn’t as liberal as John Gruber’s URL detection regex, so I used that. And thus NSRegularExpression made its first (and so far only) appearance in any of my code.

I felt weird about it. But keyboard and mouse are recovering fine.

(To be clear: I’m using this on plain text, not HTML. Not crossing the streams.)

It was easy to detect a tap and figure out if the text underneath had a link attribute and then open the browser.

The tricky part was something I hadn’t thought about in advance: you need to highlight that link, and the link is not displayed in a single contiguous rectangle. So I had to write code that, given a tap, figures out the array of rectangles in which the link appears.

I did it like this:

  1. Get the run of text for the tap location.

  2. For that run and all the surrounding runs (before and after), create an array of CGRects (via NSValue). I took the simple approach of looping through lines and runs, and that performed just fine even for text of a few thousand characters.

  3. Call setNeedsDisplay.

  4. In drawRect, draw a light blue background for the rects for that link.

Memory Use Tip

Anyone who’s done this kind of work themselves probably realizes that I’m caching the CTFrameRef in order to handle link tapping. Instruments showed me that this used a surprising amount of memory — so I made the simple change of caching the CTFrameRef only when there are links to be handled. For completely plain text there was no need.

Design Changes

That was fun. But I spent way more time on design changes.

When I designed Glassboard 2.0, I thought I was exercising a good amount of restraint.

But, really — why did it need wood grain? And curved shadows? And a weird textured navbar? Looking back on it, 2.0 looked more like a crazy baroque craziness than a well-designed app.

I’ve learned alot since then. (And I agree with Dave and John about the future of UI design.)

For the navbar I picked a blue I liked and set the tint color rather than use a custom asset. I like the way it looks, and it gets out of the way. The focus should be on what people are posting, not in things like the navbar.

I spent most of my time on the new timeline. I wouldn’t call it flat — it still has highlight lines and shadows, but it’s much flatter.

My goal was to make it look as if it wasn’t designed at all, as if I just gave up and decided to just put the information on screen in the right order. Or, put another way, my goal was to make it look as if anyone would have designed it that way, as if it was inevitable and obvious.

In my head I kept saying, “Make it look like nothing.”

It would have been easier were the timeline similar to a Twitter app, where everything appears at the same level and with the same weight. But Glassboard shows comments in-line, which meant I had to separate a message/comments group from the next one, and I had to make sure comments appear subordinate.

There are lots of ways to do this — color, indentation, depth, texture — and I spent weeks trying just about everything I could think of. The hard part was to make it not appear visually noisy.

In the end I didn’t go totally flat, and I sweated buckets of pixels over the exact shades of gray and alpha levels for the highlight shadows and so on.

I’ll leave it to you to judge how well I met my goal (or even if the goal was the right goal) — but I will say that I’m proud of the new timeline.

I already see room for improvement, though. As always.

Performance and Design Changes

One of the nice side effects of the design change was that I got rid of a bunch of compositing the app used to do. The app used to have that stationary wood background and everything scrolled on top.

Though scrolling was okay, it’s faster in this new version, since I got rid of that effect which wasn’t even awesome in the first place.

Lesson Learned

I shouldn’t try to show off. It’s harder to just do a good and appropriate job.

Just because I can create a curved shadow and find a wood grain, doesn’t mean I have to use those kinds of things.

Local Mac Pride

Congratulations to Aged & Distilled on releasing Napkin and congratulations to Black Pixel on Kaleidoscope!

Both apps are awesome. And both are made, at least in part, in Seattle. Local hero Chris Parrish is half of Aged & Distilled, and the Black Pixel machine of cool is on upper Queen Anne.

I love that Napkin and Kaleidoscope are both Mac apps.

I can’t talk about local Mac app makers without mentioning Omni. I wish like crazy I could make it to their OmniFocus 2 event in San Francisco on January 31. OmniFocus saves my bacon.

And there’s Flying Meat, home of the wonderful Acorn and VoodooPad, both of which I recommend highly.

We have a strong tradition of Mac app development in Seattle — and it’s stronger now than ever.

All you have to do to meet all these folks is attend Seattle Xcoders meetings. Some folks just show up for the after-meeting get-together, which is usually at the Cyclops. So add that to your plans.

Pure

NSHipster writes about \_\_attribute__. It would be interesting to see what portions of a typical iPhone app could be marked with pure and const.

Aaron Swartz

Though Aaron and I have been in different circles for the past few years, he was a huge help to me in the early days of NetNewsWire. He hosted the beta testers mailing list at notabug.com (love the domain name). He wrote the code that NetNewsWire uses to show differences between two versions of the same post. (If you look inside the Mac package, you’ll find htmldiff.py inside the Resources folder.)

But all that was little stuff, though a big help to me, and we haven’t talked in a while.

He’d gone on to do cool things — and make some mistakes, and get in trouble for them. But I knew he was extraordinary, and I expected him to grow up to become an American hero. I was happy to watch that happen.

Now I won’t get to. This world is poorer.

My condolences to all his family and friends.

***

Here’s Cory Doctorow writing about his friend Aaron. And Lawrence Lessig. And Mark Bernstein.

Outliner Code

Jake Savin speculates that my code formatting style is a result of writing UserTalk in Frontier’s outline-based script editor.

I see his point — but the more likely reason is that the Frontier kernel code uses the brace-at-end-of-line style, and that was my first large C project.

Code Formatting Style

On the Debug podcast (which I adore), Craig Hockenberry (whom I adore) mentioned that my code formatting style is crazy.

He may be right. I’ll let you judge.

Here’s a simple example from a view controller’s init method:

- (id)initWithAccount:(GBAccount *)account {

    self = [self initWithNibName:@"Settings\_iPhone" bundle:nil];
    if (self == nil)
        return nil;

    _account = account;
    return self;
}

My formatting style is pretty much K&R style, plucked straight from the C Programming Book — with one modification: opening braces for methods and functions appear at the end of the line rather than on the next line.

With K&R style, you put the opening brace at the end of the line for other blocks (if, while, etc.) — but not with functions. I prefer the more consistent approach of putting the brace at the end of the line for all blocks.

You might not agree with its readability — but I find it cleaner and easier to scan my style than this:

- (id)initWithAccount:(GBAccount *)account
{
    self = [self initWithNibName:@"Settings\_iPhone" bundle:nil];
    if (self == nil)
        return nil;

    _account = account;
    return self;
}

And much easier than this:

- (id)initWithAccount:(GBAccount *)account
{
    self = [self initWithNibName:@"Settings\_iPhone" bundle:nil];
    if (self == nil)
    {
        return nil;
    }

    _account = account;
    return self;
}

Or this:

- (id)initWithAccount:(GBAccount *)account
{
    self = [self initWithNibName:@"Settings\_iPhone" bundle:nil];
    if (self != nil)
    {
        _account = account;
    }

    return self;
}

It’s a matter of what you’re used to, I think — I don’t for one second contend that there are right and wrong ways. There aren’t. Consistency matters more, because any reasonable style is readable and scannable as long as it’s consistent.

Seattle Xcoders Thursday: Auto Layout

My minimal experience with auto layout is unhappy. I need to learn it. So I’m looking forward to Kyle Sluder’s presentation January 10 on Auto Layout.

Note that this meeting is not at the normal place: it’s at the Microsoft Accelerator on Westlake.

There will also be a Q&A with the Google Maps for iOS team. (A bunch of the work on that app was done locally.)

Xcode PNG Compression

This case study by the ImageOptim folks shows that Xcode PNG compression isn’t so great.

Xcode-optimized images were significantly slower to display. Decoding speed appears to be correlated to image file size more than anything else (most likely savings on byteswapping are negligible compared to additional disk I/O and extra data to decompress).

(This test was done in March 2012, but I only just saw it today. It’s possible I’m the last person to get the news.)

CocoaConf 2013

The CocoaConf spring tour includes conferences in Chicago, Washington, DC, Dallas, and San Jose.

I spoke at CocoaConf in Portland last year. It was a good conference and I learned things and met people. Totally worth doing.

Dave on the Future of Design

Dave Wiskus writes for Macworld: Apple and the future of design:

It’s curious how Apple’s hardware and software have taken such divergent paths. Looking at iOS hardware and software separately, one might think they were produced by different companies. The drop-shadows and textures of iOS stand in sharp contrast to the clean lines and invisible seams of Apple’s hardware.

Four Nothings

NSHipster on nil and friends:

Additionally, in Foundation/NSObjCRuntime.h, Nil is defined as a class pointer to nothing. This lesser-known title-case cousin of nil doesn’t show up much very often, but it’s at least worth noting.

I didn’t know about Nil.

Fred Wilson on Privacy

A VC: Feature Friday: Privacy:

The thing I want to talk about here is the emergence of privacy as the defining feature of the next breakaway app on the social internet. What does this mean for where we are and where we are going? Is open social out and closed private in?

My co-worker Walker Fenton says, “Yes, privacy is ‘in,’ but you might not hear about it.”

That’s the tricky part.

On Coalescing Calls

Doug Russell writes about using performSelector:withObject:afterDelay: to coalesce multiple calls into one.

I’ve done similar coalescing, but by using an NSTimer. I haven’t gone to the trouble of writing a category for this (but I should). Here’s what I do:

  1. Create a method like scheduleReloadData.

  2. In that method, I create a timer that fires soon (like in .2 seconds). If a timer already exists, I invalidate it and release it.

  3. When the timer fires, it calls a method like reloadTimerDidFire:(NSTimer *)timer. In that method I get rid of the timer and actually call reloadData.

I think my method would coalesce calls over a longer period than Doug’s method — but at a cost, of course. (Latency, for one thing.)

This reminds me of another form of coalescing I’ve used a little bit, but that doesn’t seem to be common (as far as I can tell): NSNotificationQueue.

You can coalesce multiple notifications into one, and choose how they’re coalesced (end of the run loop or until the run loop is idle).

I haven’t found NSNotificationQueue to be massively useful — but it’s occasionally useful. File under Cocoa nooks and crannies.

Update a few minutes later: I totally missed Doug’s note at the end about using an interval greater than 0.0 to coalesce calls over a longer period. That should do the trick to replace my timer-based method.

Functional Programming

Tim Bray links to an article on functional programming that explains why it’s important.

I’m not sure I get functional programming yet — though I’m curious.

I also suspect that I may already be using functional programming principles. My parents taught me to eliminate side effects as a matter of good design way back in 1980. (Hello GOSUB!) Today I use blocks and GCD; I minimize state and mutability and I’m strict about what can change what.

I’m not really sure that functional programming is more than a set of old-fashioned solid practices that have gained more emphasis (quite rightly) in the age of concurrency.

But I suspect that a functional programming advocate would say that I’m at step 1: vague understanding, with a sense that some of this is familiar. That hypothetical advocate would agree that I don’t really get it yet.

I look forward to learning more.

Greg Interview

MacStories interviews Greg Pierce:

Automation will look very different on iOS, but it will come. As more power users try to replace desktop computers with iPads, the demand will only go up.

I’ve known Greg for years — we both come from the UserLand Frontier community of the middle and late ’90s. I’m not at all surprised when alumni are still, years later, interested in inter-application communication.

Paul on UITableViewCell

Paul Goracke: UITableViewCell Is Not a Controller, But…:

I’ve been down a number of UITableViewCell paths that bit me in the end, many of which Brent seems to have experienced as well, but I have ended up settling on creating UITableViewCell subclasses which take the model object they’re intended to display and break out the properties to the individual subviews so the controller doesn’t have to.

Paul makes great points. I’m not persuaded to change my position, but I respect his.

One thing I’ll clarify:

When my view controller is bossing the UITableViewCell around, it’s not directly accessing the cell’s subviews. (Paul assumed it was, which is totally understandable because I didn’t specify.)

I keep a cell’s subviews totally private: they don’t appear in the header file.

Instead, my controller does something like this:

cell.username = username;
cell.statusText = statusText
etc.

In the end, I have a UITableViewCell that knows nothing or almost nothing of the outside world (since it knows nothing of model objects), and that doesn’t expose its implementation (since it doesn’t expose its subviews).

But the most important thing — which Paul and I agree on — is to keep your UITableViewCells stupid and simple. Paul writes:

Avoid the temptation to bypass the controller and start key-value observing (notifications are also verboten) on the received model object. This is the hubris that will lead to your MVC downfall (been there, done that). Leave all of the updating logic to the “true” view controller, and your cell subclass will remain a happy and healthy data transformer.

If you do use Paul’s approach, there’s one simple piece of advice which should be treated as an absolute iron-clad rule:

Don’t even keep a reference to the received model object.