Story-Centered Design

We like to get functional prototypes to stakeholders as early as possible in the development process. Prototypes help frame the discussion by putting features in context, helping teams make better design decisions and uncovering edge cases.

In this excellent piece on story-centered design, Braden Kowitz describes an approach that moves that kind of discussion back into the design process:

Designers present every sentence the customer reads, every action they take, and every screen that system generates in response. The designs follow a customer from an initial trigger all the way through completing a goal, and they show how the design supports every step in that flow.

Combining a design process like this with functional prototypes creates partnership between design and development teams that can lead to faster feature cycles, fewer bugs, and less rework by all involved.

Why good storytelling helps you design great products

Frame geometry macros to improve your UIKit code

Frame geometryI find myself doing more and more dynamic UI layout in iOS apps these days. When elements of a UI need to resize to fit their contents, or move to accommodate other elements, the layout code can get complex and verbose. We’ve developed a set of Objective-C UIKit macros that help make this code more readable, self-documenting, and easy to change.

When a view is offset from another view, you might write something like this:

someView.frame = CGRectMake(someOtherView.frame.origin.x + someOtherView.frame.size.width + padding...

These macros help make that code more concise and scannable:

someView.frame = CGRectMake(RIGHT(someOtherView) + padding...

You can download the macros from our TBMacros Github repo. First let’s take a look at an example Continue reading

Objective-C Singleton Pattern Updated For Testability

SingletonAt Two Bit Labs we do a fair amount of unit testing. In places where we use singletons we use a variation on the the Objective-C dispatch_once pattern of thread safe singleton creation. This variation supports resetting the singleton or replacing it with a mock object.

That way in our unit tests we can do the following:
Continue reading

Objective-C Blocks Cheat Sheet

iOS Blocks Objective-CBlocks are an incredibly powerful addition to Objective-C, introduced in iOS 4. However, their syntax can be maddeningly difficult to remember. Matt Gallagher has an excellent post that breaks down the syntax to help you understand it. If you haven’t read this article, go do it now.

Even after working with blocks for a while, I still get tripped up. So I created the cheat sheet below, which I frequently refer to when declaring blocks. Continue reading

GSM 0338 encoding for SMS

Here’s a little Objective-C helper class we wrote to make it easier to convert NSString’s to GSM 03.38 encoding on the iPhone, iPad, or Mac:

  • Handles stripping out characters not included in the GSM0338 character set
  • Calculates the length of a GSM 0338 string (e.g. ^,[,|,etc each require 14 bits instead of 7
  • Gives you a truncate method (e.g. ^hello truncated to length 4 is ^he because ^ is 14 bits)
#import <Foundation/Foundation.h>

@interface GSM0338Helper : NSObject
+(int)gsmStringLength:(NSString *)textToShare;
+(NSString *)gsmStringFromString:(NSString *)textToShare replacementChar:(NSString *)replace;
+(NSString *)gsmShorten:(NSString *)text length:(int)length truncationText:(NSString *)truncationText;
@end
#import "GSM0338Helper.h"

@implementation GSM0338Helper

// gsm has certain characters like pipe that require 14 bits instead of 7 so we need to double count those
+(int)gsmStringLength:(NSString *)textToShare {
    if (textToShare == nil || [textToShare length] == 0) return 0;
    NSError *error = NULL;
    // count the number of times special escape characters appear that will take up 2 characters instead of one
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\^\\{\\}\\[~\\]\\|€\\\\]" options:NSRegularExpressionCaseInsensitive error:&error];
    NSArray *matches = [regex matchesInString:textToShare options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [textToShare length])];
    return [textToShare length] + [matches count];
}

// replaces non gsm characters with the replace string you specify, can be empty string @""
+(NSString *)gsmStringFromString:(NSString *)textToShare replacementChar:(NSString *)replace {
    if (textToShare == nil || [textToShare length] == 0) return textToShare;
    NSError *error = NULL;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9@£$¥èéùìòÇ\fØø\nÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%%&'()\\*\\+,-./:;<=>\\?¡ÄÖÑܧ¿äöñüà\\^\\{\\}\\[~\\]\\|€\\\\]" options:NSRegularExpressionCaseInsensitive error:&error];
    NSString *modifiedString = [regex stringByReplacingMatchesInString:textToShare options:0 range:NSMakeRange(0, [textToShare length]) withTemplate:replace];
    return modifiedString;
}

// shortens gsm string to length gsm chars and if it truncates it, it appends the truncation text
+(NSString *)gsmShorten:(NSString *)text length:(int)length truncationText:(NSString *)truncationText {
    if (textToShare == nil || [textToShare length] == 0) return text;
    int gsmTextLength = [self gsmStringLength:text];
    if (gsmTextLength > length) {
        // We have to first figure out how many characters to remove since gsm length is possibly > [title length]
        int numCharsToRemove = gsmTextLength - length;
        int newTextLength = [text length] - numCharsToRemove;
        if (newTextLength > 0 && newTextLength < [text length])
            text = [NSString stringWithFormat:@"%@%@", [text substringToIndex:newTextLength], truncationText];
    }
    return text;
}

@end

Kickstart your apps with the iOS Xcode Starter Project

We created the iOS Xcode Starter Project to make it quick and easy to start a new iPhone or iPad app. Our goal was to create a template to save the hours of effort it takes on a new project configuring essential open source libraries, the unit and functional testing environments, analytics, multiple targets, multiple app IDs (for development and production), and so on.

Starting a new project with the iOS Xcode Starter Project couldn’t be easier: Continue reading

Block initialization for testability and reuse

BlocksSince Apple introduced block support in iOS 4, more and more APIs are moving from delegation to block callbacks. While block callbacks can be declared inline, in most cases you should initialize your block callback in a method that returns the block. This keeps the code that calls an external API succinct, allows you to reuse the block in different contexts, and makes it easier to unit test the code in the block.

Let’s see what this looks like in practice.
Continue reading

Toward a better “Rate this app”

<3 <3 <3 Rate this app!!! <3 <3 <3If you’ve had an iPhone for any length of time, you’ve certainly had the experience of opening an app and facing a desperate plea for app store ratings. “Your reviews help other people find this app”, we’re told. And it’s true–app reviews and ratings are an important part of the app store ranking algorithm. Unfortunately, the state of the art in app rating is an approach that nags you when you open the app. Surely we can do better than that.

Continue reading

The reviews are in!

So be honest: What’s your answer to the question “Do you enjoy reading the latest app store reviews for your app?”

(a) “Our users are so smart, kind, and thoughtful that I relish the chance to read their pearls of wisdom.”

(b) “Unless they’re written on the bottom of a pint glass, no.”

(c) “I’d rather be run over by a rhino.”

Continue reading