Monday 18 January 2010

Writing: New article in CVu

The January issue of ACCU's CVu magazine is hitting doormats around the world. It contains the latest in my Professionalism in Programming series - Live To Love To Learn. This article is the first in a mini-series investigating how we learn as software developers.

Thursday 7 January 2010

Writing: 97 Things Every Programmer Should Know

The 97 Things Every Programmer Should Know book from O'Reilly, edited by Kevlin Henney has almost come to completion; it's due for publication next month.

They've just posted the list of contributions appearing in the book. I'm pleased to say that three of my five submissions are in the list. You'll be able to purchase a paper copy of the following entries:
  • You've Gotta Care about the Code (link)
  • Improve Code by Removing It (link)
  • Don't Ignore That Error! (link)

Tuesday 5 January 2010

iPhone: Application preferences/defaults/settings. With defaults.

How to extend NSUserDefaults with default values.

Time for more iPhone shenanigans. Lots of people have contacted me saying these posts are useful - thanks. As I do more iPhone development I discover things that aren't immediately obvious from the SDK documentation (excellent as it is). I'm documenting some of them in my blog partly to find out if I've missed the obvious, and partly to give back to the community - Google has certainly pointed me to plenty of other people's blogs with useful answers.

Today's installment: application settings. Or preferences, if that's what you want to call them. Or settings, if you prefer.

Finding the preferred term

The iPhone SDK provides a neat API for persisting simple sets of application settings. It's easy enough to find, if you know what it's called when you search for it. Don't search for preferences, like I did, or you'll be sent down the Core Foundation dead-end of Preferences Programming Topics. This is a rich and fairly heavyweight API for persisting state. But for a simple iPhone app it's overkill.

What you do need to look for is the Cocoa NSUserDefaults class. That's the puppy you want. It's simple when you know what to look for. This class provides an easy way to read and write fundamental data types (integers, floats, bools, NSStrings, etc).

What's missing? NSUserDefault's defaults.

Again, perhaps I've missed something. But this class is missing something fairly obvious. It has a simple set of APIs to write values against a given NSString "key" name, e.g. setInteger:forKey: It has a set of APIs to read those values back for a given NSString "key" name, e.g. integerForKey:

However, there is no way to discover whether or not a setting has been saved yet. This is important since the first time your application is run no preferences have been saved, and you'll need to fall back on a set of well-chosen default values. The NSUserDefaults accessors return "default initialised" values if a key has not been set yet; e.g. 0 for integers, 0.0 for floats, or a nil string. You can't always inspect an integer for zero and replace it with a default - sometimes zero is a valid setting value.

So what can you do about this?

Adding defaults to "defaults"

We can add this functionality to NSUserDefaults easily enough. The first trick is to determine whether a value has been stored for a particular key or not. It turns out that, no matter what kind of key has been stored, internally everything can be accessed as an object and the objectForKey: method returns nil if no value has been stored.

So you can see whether an integer, for example, has been stored by calling objectForKey: with its key name. The returned id value is nil if the integer has not yet been stored, and non-nil if it has.

Useful.

Now, ideally we'd have a suite of methods that read values from the NSUserDefaults class and return a specified default value if that setting has not yet been made. This can be done neatly using Objective-C's class category facility. Categories allow you to extend existing classes with extra functionality.

Here is some example code showing how to do this for some of the basic types. Once you've put this snippet into your application you can use the methods as if they were an original part of the NSUserDefaults API.

@interface NSUserDefaults (ReadWithDefaults)
- (NSInteger) integerForKey:(NSString *)key withDefault:(NSInteger)value;
- (BOOL) boolForKey:(NSString *)key withDefault:(BOOL)value;
- (float) floatForKey:(NSString *)key withDefault:(float)value;
- (id) objectForKey:(NSString *)key withDefault:(id)value;
@end

@implementation NSUserDefaults (ReadWithDefaults)

- (NSInteger) integerForKey:(NSString *)key withDefault:(NSInteger)value
{
if ([self objectForKey:key])
return [self integerForKey:key];
else
return value;
}

- (BOOL) boolForKey:(NSString *)key withDefault:(BOOL)value
{
if ([self objectForKey:key])
return [self boolForKey:key];
else
return value;
}

- (float) floatForKey:(NSString *)key withDefault:(float)value
{
if ([self objectForKey:key])
return [self floatForKey:key];
else
return value;
}

- (id) objectForKey:(NSString *)key withDefault:(id)value
{
id obj = [self objectForKey:key];
if (obj)
return obj;
else
return value;
}

@end

Writing: Creating a Framework for the iPhone

December's Overload (an ACCU magazine) was published at the end of 2009, containing an expanded article form of my Creating a Framework for the iPhone work.

You can read it online in the Overload Online section of the ACCU website.

If you're a programmer who cares about code, and how to write good code, you should consider joining ACCU. It's not expensive, and is an excellent way to improve your skills in a great international community of programmers. You can read PDF versions of Overload magazine for free if you're not an ACCU member, but to get CVu magazine you have to become a member.