Tasty coffee with AeroPress

When you just want one cup of damn good coffee your best bet is to give the Aeropress Coffee and Espresso Maker a try. The AeroPress makes cleanup quick and is portable enough to throw in your backpack or even take along on your next camping trip. The process, or ritual as some may refer to it as, makes for a much more enjoyable drinking experience as well.

More

Getting started with UI Automation in Xcode 4

Did you know that with a little patience and some knowledge of JavaScript you can write automated UI tests against your iOS applications? What’s even better is with Xcode 4.2 installed its as easy as clicking a record button and having Instruments generate the test scripts for you.

For this tutorial I’m going to download the Core Data Recipes application from Apple’s developer website. Once you’ve opened the app in Xcode go ahead and click on
Product -> Profile in the menu bar (command + I works just as well) to launch the Instruments tool that we’ll be using to run our tests with.

You should see your application launched in the simulator along with a running instruments profiler. Form here you’re going to want to check that the Automation instrument has been created and then click on the Add button just below it to create a new Automator script.

After clicking on Add -> Create you will notice a record button looming towards the bottom that when clicked allows you to begin interacting with your application and generating the JavaScript necessary to automate the test.

Here’s a trivial example that I recorded using the sample application to demonstrate.

var target = UIATarget.localTarget();

target.frontMostApp().mainWindow().tableViews()["Empty list"].cells()["Chocolate cake with chocolate frosting, 1 hour, Chocolate Cake"].tap();
target.frontMostApp().navigationBar().rightButton().tap();
target.frontMostApp().mainWindow().tableViews()["Empty list"].cells()["Dessert"].tap();
target.frontMostApp().mainWindow().tableViews()["Empty list"].cells()["Snack"].tap();
target.frontMostApp().navigationBar().leftButton().tap();
target.frontMostApp().mainWindow().tableViews()["Empty list"].cells()["Snack"].tap();
target.frontMostApp().mainWindow().tableViews()["Empty list"].cells()["Dessert"].tap();
target.frontMostApp().navigationBar().leftButton().tap();
target.frontMostApp().navigationBar().rightButton().tap();
target.frontMostApp().navigationBar().leftButton().tap();

Having Xcode generate the script for you can test your patience with how it tries to target certain elements incorrectly but often times it will at the very least get you a good base to work from if you’re just getting the hang of how to ineract with your app.

It’s worth mentioning that this script will run just fine if you are dealing with UI elements that stay the same across test runs or otherwise remain static. If this isn’t the case then you may run into cases where elements have different attributes or no longer exist if you were to delete a row in a UITableView for example. This is where some knowledge of JavaScript (and the UI Automation Reference) comes in handy to check the existence and state of elements. This is beyond the scope of this article but one possible approach to this problem would be to restore your data prior to each test run.

You’ll notice that the third line has the cell contents hard coded into the script which is just another way of gaining access to it.

target.frontMostApp().mainWindow().tableViews()["Empty list"]
    .cells()["Chocolate cake with chocolate frosting, Chocolate Cake"].tap();

Thankfully we have a couple of much better options to achieve the same result. The first way is by using the element placement index inside of the cells array.

target.frontMostApp().mainWindow().tableViews()["Empty list"]
    .cells()[0].tap();

You can alternatively set the accessability label through Interface Builder.

The accessibilityLabel property can also be set at runtime if you so choose, this is most useful for dynamic layouts.

cell.isAccessibilityElement = YES;
cell.accessibilityLabel = @"Cake";

Given the nature of the Core Data Recipes sample application our best approach would be to use the cell index or full cell label text for demonstration purposes. Ideally the accessibility text would be part of your data source in some fashion so in code we could iterate over the data while creating the table view cells.

Taking another look at the script you can see a lot of repetitive ways of accessing elements, we can pull out some common pieces and have cached versions to work with instead. Not only does this make your script more readable but it will also help speed up the running times.

var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
var navBar = window.navigationBar();

We’ll also go ahead and change the hardcoded string key to access the cell to an ordinal index value here as well by updating tableViews()["Empty list"] to be
tableViews()[0]

Now what’s left is a script that is much more maintainable going forward.

var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
var navBar = window.navigationBar();

// Select Recipe
window.tableViews()[0].cells()[0].tap();

// Change Ingredient
navBar.rightButton().tap();
window.tableViews()[0].cells()["Dessert"].tap();
window.tableViews()[0].cells()["Snack"].tap();
navBar.leftButton().tap();

// Change Ingredient Again
window.tableViews()[0].cells()["Snack"].tap();
window.tableViews()[0].cells()["Dessert"].tap();
navBar.leftButton().tap();

// Done Editing
navBar.rightButton().tap();

// Back To Recipe List
navBar.leftButton().tap();

Where to go from here?

One aspect of the documentation that is worth exploring is the UIALogger class. UIALogger gives you the ability to define a beginning and end of your test cases and not only provide a means of structuring tests but you can record pass or fail messages to give more context to the results. These messages will get output in the Trace Log portion of the Instruments tool.

More

Blocks save the day

I’m consistently amazed how useful blocks are in Objective-C. So much so that I’ll be attempting to document useful ways that blocks can make your life easier as an iOS developer. Here’s the first of (hopefully) many posts of its kind.

Take this code

- (void)viewDidAppear:(BOOL)animated {
{
    UIView *viewToAnimate = ...
    [self performSelector:@selector(animateIt:) withObject:viewToAnimate afterDelay:1.0f];
}

-(void)animateIt:(id)sender 
{
    // animate viewToAnimate
}

It seems like this could be better by having the animation code where you have your selector delay setup. Well guess what? Blocks to the rescue!

@implementation NSObject (PerformBlockAfterDelay)

- (void)performBlock:(void (^)(void))block 
          afterDelay:(NSTimeInterval)delay 
{
    block = [[block copy] autorelease];
    [self performSelector:@selector(fireBlockAfterDelay:) 
               withObject:block 
               afterDelay:delay];
}

- (void)fireBlockAfterDelay:(void (^)(void))block 
{
    block();
}

@end

This snippet is using category that you can #import into your class. Using this in practice you get this.

- (void)viewDidAppear:(BOOL)animated {
{
    UIView *viewToAnimate = ...
    [self performBlock:^void() {
        // animate viewToAnimate
    } afterDelay:1.0f];
}

Credit goes out to this StackOverflow thread for the tip.

More

Notes on Rework

Rework has been on my “things to read” list since it was published over a year and a half ago. I was surprised when I came across a copy at my local library and couldn’t pass it up. Rework is a book that is entirely made up of material from the signal vs noise blog from the founders of 37 signals. It contains quite a bit of unconventional advice about business related topics. It was quite the fast read coming in at 288 pages with chapters only being a few pages long. They actually point out that the original draft was double the size but they chose to weed out the parts that distracted from the flow of each of the chapters. Or in their own words.. build half a product, not a half-assed product.

What follows are some of the more memorable take aways from this book..


By learning from other people’s mistakes you’ll be discouraged from trying to figure out a better, more successful solution to the problem at hand.

Draw a line in the sand. When it comes to developing software you will inevitably get requests from your audience for new features and things that you should be doing differently. Keep in mind that these are simply suggestions and even though they may be valid suggestions that sometimes its not essential to be so accommodating and determine what the primary purpose of your application is.

Just because your competition is adding feature X doesn’t mean that you should be chasing them to stay on par with their features. Your software is unique and doing something extremely well says more than doing many different things that are half-assed.

Make tiny decisions. Get into the rhythm of making decisions rather than postponing for the perfect solution. If you don’t make the smaller decisions they’ll eventually pile up and be harder to tackle until they get tossed aside and never resolved.

Don’t be afraid to launch your product sooner than when you’re comfortable with, launching in an iterative approach lets you release the essentials first instead of spending the time on extras that aren’t necessary for day one. Quick wins like these are important since momentum fuels motivation.

Teaching your customers can be just as effective if not more so than marketing to them. By doing so you help your customers improve upon their skills and is a way of gaining their loyalty. Think of how a cooking show teaches their audience to become better at preparing meals thus more inclined to buy products that they recommend. Or how your local home improvement store holds do-it-yourself clinics throughout the year.

Things like answering the phones, your software’s error messages, or the after dinner mint are all forms of marketing. Everyone should be involved.

Own your bad news by being the first to tell your customers when things go wrong. Better this than having it spread to other sources and become construed into something it isn’t.

Be close to your customers. By doing this you see first hand what others are saying about your products. Just like how the bad news can become twisted around and unreliable, feedback from your customers can become the same way.

Sound like you. Avoid things like buzzwords and corporate speak, also write to be read, don’t write just to write. Write like its just for one person so that you avoid generalities and awkwardness.

Inspiration is perishable, so don’t put it off. Set aside time in the near future and dive in and you’ll find that you can get much more done when you’re inspired then when you get around to doing it later on down the road.

More