Panic on American ApparelCar in Snow, Part 5Car in Snow, Part 4Car in Snow, Part 3

fill the void

Blog Breadcrumbs: Relevance, Recognition, and Trust

Whenever I encounter a strange error message or a similar roadblock while programming, I google for answers. This approach leads to a wide array of websites, almost all blogs. (In fact, most of this site’s traffic comes from Google, presumably from the same approach.) As soon as I get to the site, I want to quickly assess the post’s relevance and my level of trust. If the site is interesting and useful, I’ll trust it more if I come across answers on it again…if I recognize it. When I designed this blog, I tried to add these same breadcrumbs that I look for when I google: relevance, recognition, and trust.

Relevance

When I click on a Google link, I want to figure out as quickly as possible if the page is relevant. So I look around for a date, an author, tags, and a comment count.

I expect the date to be embedded in the URL, but unfortunately, WordPress’s default permalink format is arcane: http://www.bdunagan.com/?p=123. The URL format is displayed to every single visitor, and yet, all this conveys is how many times I’ve posted before this. It’s a sad default when the very next option conveys both timestamp and title: http://www.bdunagan.com/2009/11/05/sample-post/. The visitor can quickly glance at the URL and instantly know the post’s age and topic. The default permalink format is really an indication of the blog creator’s laziness.

The URL is easy. One step further is the post’s meta information. I want to know the author (for a site like TUAW), tags, number of comments, and frankly, the date again (as I might be looking at the homepage where the date isn’t in the URL). All that information gives me a better idea of whether or not the post is relevant to my goals. Including that information next to the title makes sense. However, the default WordPress theme hides this information, shown above, at the bottom of the post instead of next to the title and above the fold. Worse yet, comments follow that section, so I can’t easily page-down to the bottom of the screen, as it might be tucked away in the middle.

Recognition

The default theme for WordPress is Michael Heilemann’s Kubrick. It’s a fine theme, but far too many authors stay with it, making it difficult for new visitors to trust the site and for repeat visitors to recognize the site. (Moreover, many spam blogs use this theme as well, making sites using it more difficult to trust.) Selecting an alternate theme is ridiculously easy; just go to the admin section, click Appearance, find a theme, and click “Activate”. WordPress is nicely architected such that nothing more is required. And with the large number of free, built-in themes, selecting a relatively unique one is no problem. Many insert the post’s meta information near the title, rather than at the post’s end. Creating an identifiable site helps visitors recognize you. Going with Kubrick is just lazy.

Trust

A new visitor to this site has no idea who I am. No trust at all. The only thing going for the site is the fact that it showed up in Google as possibly relevant to the visitor’s goal. In addition to the content, I put a quick description of myself at the bottom. I put links to Twitter, LinkedIn, and Flickr. Each represents a one-click path for the visitor to find out a bit about me. No guarantee it will be interesting, but it does establish I’m a real person who writes Mac software. I want to establish a modicum of trust with the visitor. It’ll help the next time the visitor comes to the site and recognizes it.

Matt Gemmell’s blog redesign is a great example of this trust in action. I visited his site yesterday but immediately thought I’d clicked on the wrong link; the theme was completely different. Instead of the graphic-rich white-on-black theme I was accustomed to, the site was text-rich black-on-white with lots of white space. I went over to Instintive Code to see if I’d mistakenly gone to Gemmell’s other blog. Nope, no work blog. So I went to the blog’s index page and saw the title “Accessible Blog Redesign”. Ah, a redesign. Still, there was a brief period when I didn’t trust the page I landed on; it wasn’t the one I built my trust on.

To drive home the importance of breadcrumbs for trust, visit Clay Shirky’s blog. Shirky is an impressive speaker and a great writer. When I read a tweet last spring about his sharp critique on the demise of newspapers, I immediately clicked through to the blog post. But I landed on a standard WordPress site with the default Kubrick theme. Weird, seemed like a guy as aware as Shirky would do something a bit more creative. I looked back through the blog’s history. Hmm, created a month ago with only three posts so far. More weird. The blog came from Shirky’s domain, but it had no links back to the homepage. And, Shirky’s homepage didn’t have any links to the blog. At this point, I’m very skeptical about the blog. I actually googled around to make sure other authoritative people linked to the blog articles, which they did. Now, nine months on, the blog still only has six posts and no links. I still find it weird. So many missing breadcrumbs.

Iterate

Ironically, in writing this blog post, I noticed that my single posts didn’t include the number of comments up at the top like the main index does. Fixed that. And I added a nice favicon from Glyphish. Iterate, iterate, iterate.


iPhone Tip: Conditional Toolbar

For an iPhone app I’m developing, I wanted a toolbar at the bottom of the initial view, but I didn’t want it appearing in the other views. I tried simply calling myUINavigationController.hidden, but the effect was a little jarring. UINavigationController has a great wrapper for that method that includes animation: setToolbarHidden:animated:. With animation, the toolbar smoothly slides in and out when going between the initial view and the other views. I inserted the method in UIViewController’s viewWillAppear and viewWillDisappear.

// in a UIViewController subclass

- (void)viewWillAppear:(BOOL)animated {
	[self.navigationController setToolbarHidden:NO animated:YES];
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
	[self.navigationController setToolbarHidden:YES animated:YES];
	[super viewWillDisappear:animated];
}

iPhone Tip: Automatic Focus for Text Input

Google popularized this trend in web forms years ago. When I go to the Google homepage, the page automatically puts the cursor in the search text box, so that I can immediately start typing. In Things on the iPhone, when I start to add a new item, the “New To Do” page automatically selects the Title text field. Again, I can immediately start typing. This autofocus is very easy to achieve: simply call [myUITextField becomeFirstResponder]. Changing the keyboard style is equally trivial: myUITextField.keyboardType = UIKeyboardTypeEmailAddress. Both are attributes of the UITextInputTraits protocol.

// Automatically highlight the field and change to email keyboard.
UITableViewCell *cell = (UITableViewCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[cell.field becomeFirstResponder];
cell.field.keyboardType = UIKeyboardTypeEmailAddress;

Design Iteration

The feature I alluded to in my “Rereading Info.plist” post is a logging preference. It has two requirements: user-configurable (i.e. not code) but not immediately visible. When a user starts having technical issues and calls tech support, they find out how to turn on logging, and with that output, development has a better idea of what the issues are.

A year ago, when I first implemented it, I was averse to adding a secret preferences window, because I couldn’t think of a natural place for accessing it. Instead, I added a quick key-value pair to Info.plist. Users wouldn’t happen across it; most of them probably don’t know app bundles are folders. But the preference was there if needed. So, I shipped that. Multiple times. Recently, I decided this preference needed to be dynamic, for those times when a user wants to enable logging without restarting the application. I set up an NSTimer to read the key-value pair from Info.plist every minute. After finding out the dictionary was cached, I figured out how to manually read it. Problem solved.

And yet, I completely ignored all the signs that pointed to a bad user experience. The workflow for setting the preference was complicated to explain to other developers (who know app bundles are folders). Non-developers asked for the specific steps multiple times. And yes, the Info.plist dictionary is cached, implying it’s for general application information, not debug settings. Still, it took two thoughtful comments on my other post to snap me out of my developer haze and think about the feature from a user’s perspective.

The goal of the feature was to hide the user-configurable preference from immediate view, not to bury it completely. The Info.plist workflow didn’t work, so I started brainstorming. How about a secret preference window? Fine, but where would it go? Perhaps an NSMenuItem, but that’s not hidden. Then I realized an alternate NSMenuItem was the perfect fit: hold the option key and “Preferences…” switches to “Secret Preferences…”. A user won’t stumble across it, but the menu item is a key away. Moreover, an NSTimer isn’t necessary; Cocoa Bindings make a preference change instant. That’s what I implemented today.

Never be afraid to iterate your design. The product I work on went through many iterations before arriving at its current form. Apple agrees. When I’ve talked to their UI evangelists, John Geleynse and Eric Hope, both consistently cite design iteration as one of the most important processes for developing award-winning applications (along with product definition statements). Most apps start out looking mediocre; the best user experiences are the culmination of many iterations. Keep iterating.


Rereading Info.plist

Apple has a nice mechanism for storing important app bundle information outside of the application executable: Info.plist. All the applications in /Applications have one; just right-click on any app, select “Show Package Contents”, then click on “Contents”. The file elegantly houses many facts (version, name, copyright) about your app. And Apple has a straight-forward API to get at that data: [[NSBundle mainBundle] infoDictionary]. The call returns an NSDictionary filled with key-value pairs from the Info.plist file. This workflow allows me to easily add my own key-value pairs to the file and quickly access them in code.

Recently, I wanted to read the key-value pair dynamically. That way, the user could change the value at any time, and within a minute, the app would use the new value. This proved harder. As it turns out, Apple optimized the infoDictionary query, so that it caches the contents at startup and returns that cached version through the application lifetime. If the user modified the key-value pair, that change wouldn’t show up until the next startup. Instead of using that builtin method, I had to read the Info.plist file directly. See the code below.

// [[NSBundle mainBundle] infoDictionary] is cached at app startup, so we need to read the file directly for latest data.
NSString *infoPlistFilePath = [NSString stringWithFormat:@"%@/Contents/Info.plist", [[NSBundle mainBundle] bundlePath]];
NSString *infoDictionary = [NSDictionary dictionaryWithContentsOfFile:infoPlistFilePath];
NSString *value = [infoDictionary valueForKey:@"InfoPlistKey"];

UPDATE: This is a lesson in design. While the code does function as described, I now agree that no user should be subjected to this workflow. Check out my longer explanation about the feature’s design.


← Before After →