Useful tools for iOS developers

Although Xcode is all you really need to get started with iOS development, there are a lot of useful tools that can make your life a lot easier.

When you’re writing code, Accessorizer can eliminate a lot of the drudgery. You usually find yourself creating lots of properties, which involves declaring an instance variable, adding a @property declaration, and @synthesize in your implementation file. With Accessorizer, you simply select the instance variables, hit a hotkey, and copy the generated code into your source. For example, if you have the following ivars:

NSString *title;
UIView *aView;
NSInteger count;

Accessorizer will generate the property declarations & implementations, which you can customize. In the simplest case, you’ll get this:

@property (nonatomic, copy) NSString *title;
@property (nonatomic, retain) IBOutlet UIView *aView;
@property (nonatomic, assign) NSInteger count;

@synthesize title;
@synthesize aView;
@synthesize count;


You can also have it generate init & dealloc methods and accessor methods.

For source control, you’ll probably want something more advanced than Xcode’s built in source control, especially if you’re still using Xcode 3.2.x. Even with Xcode 4, it only tracks files included in the project, not related files such as artwork & documentation not included in the project.

For Subversion, I like Versions. After looking at every Git GUI client, the only one I found that I like is Tower. For a file comparison & merge utility more advanced than Apple’s FileMerge, I like Changes. It gives a lot of display options and is also really nice for comparing directories.

If you’re using Cocos2D, you’ll need a few utilities to generate sprite sheets, textures, and particle effects,

One tool that’s absolutely essential is a sprite sheet generator such as TexturePacker. It takes a collection of single images and packs them into a single image file with the most efficient arrangement along with a plist that tells how to access each piece. TexturePacker is available in several free & paid versions.

In many apps, you’ll use some particle effects such as smoke, fire, and explosions. Particle Designer makes creating them fun & relatively easy with a large online collection of shared emitters. The plist file it generates can be used with a single line of code.

If you’re using a tile map, Tiled is a nice, free tile map editor.

If your app uses the accelerometer, you’ll need iSimulate to test it in the simulator. iSimulator consists of two components: an iPhone app and a library you include in your simulator builds. When you run the iSimulate app, all movements & multi-touch events will be sent to your app.

Finally, you’ll need to create a nice demo video for your app. Sound Stage is a great way to record videos from the simulator. It can record either the app content only, the app running inside the iPhone, or a custom background. You can also use iSimulate with it.

iSimulate a must-have for iOS developers

I’m working on a new game that depends on the accelerometer, which makes testing in the simulator difficult. I asked on Twitter about a hack to use the accelerometer in the simulator and was pointed to iSimulate.

iSimulate is a brilliant application that runs on your iPhone, along with a library that needs to be linked into simulator builds of your application. The iSimulate application connects to your computer over your wireless network and lets you send accelerometer, compass, and multi-touch events to the iSimulate-enabled application running in the simulator.

After you finish developing your app, you can then use iSimulate to record video trailers & demos of your app.

Beware of [self init]

In a new app I’m working on, I created a series of lightweight cocos2d sprite subclasses which simply call the superclass init with some parameters. As soon as I called it I ended up crashing with a weird looking stack trace showing lots of nested calls to init methods.

Screen shot 2011-03-06 at 2.18.44 PM.png

My init methods were pretty simple:


@implementation Gummybear

-(id)init {
    return [super initWithName: @"gummybear.png"];
}

@end

My superclass’s init method used CCSprite’s initWithSpriteFrameName: method.


- (id)initWithName: (NSString*)name
{
    self = [super initWithSpriteFrameName:name];
    if (nil != self) {
		// do some more initialization here
    }
    return self;
}

It ultimately ends up calling initWithTexture:, which is where the problem lies. It turns out initWithTexture: is calling [self init]. Guess which init method was getting called?


-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
{
	NSAssert(texture!=nil, @"Invalid texture for sprite");
	// IMPORTANT: [self init] and not [super init];
	if( (self = [self init]) )
	{
		[self setTexture:texture];
		[self setTextureRect:rect];
	}
	return self;
}

The fix was to simply change the name of my init method to something else.

Game Level Design

One thing I learned from writing Removr is that designing level maps is a lot more challenging than writing code. There are tools like debuggers & unit tests to verify code, but the only way to test a game level is by playing it repeatedly.

You can’t verify that a level map is ‘correct’ – it’s largely subjective and involves more art than science. A game level needs to be visually appealing and needs to be challenging enough to maintain interest while still being possible to win.

Trying to sneak one past Apple hurts everyone.

Yesterday Apple approved an innocent-looking flashlight app which contained a hidden tethering feature. Needless to say, as soon as Apple found out about it, they pulled the app.

I’ll admit, it was a brilliant feature and I bought the app while it was still available. If you weren’t lucky enough to get Handy Light, iProxy is available at github, which will do exactly the same thing without masquerading as a flashlight app.

As a result of this deception, Apple seems to be a lot more cautious and taking more time to review apps, as reported by several developers on Twitter and the iPhone SDK mailing list. I only submitted Removr yesterday, but it’s still “waiting for review” 24 hours later. Most of the time it was less than an hour waiting for review before it changed to “In Review”.

During beta testing, I had a feature in Removr which checked my server for updated level maps. I was doing very careful version & timestamp checking and could either run SQL code to insert or modify levels or replace the entire level database. I removed that feature for the release version because I was afraid Apple would reject it if they discovered it.

Why I chose SQLite instead of Core Data

A lot has been written about the relative benefits of Core Data or SQLite in an iPhone app. In most cases, Core Data is easier and has some performance benefits, but the data store is difficult to create & modify from outside of your application. SQLite, on the other hand, has some excellent tools like SQLiteManager for creating & editing data. Therefore, if you have a large amount of pre-loaded data that you want to be able to edit easily, SQLite makes more sense.

This is exactly the case with Removr. Game levels are defined by an object that specifies the background image and a bitmap of the layout of pieces on the screen. Although Core Data could easily fetch the object as needed in Removr, it would be very difficult for an external level editor to create those objects and update the persistent store.

My level object happens to be very simple and can easily be mapped to a database structure.

// Level.h
@interface Level : NSObject {
    NSData * _map;
    NSString * _background;
    NSNumber * _index;
}

@property (nonatomic, retain) NSData * map;
@property (nonatomic, retain) NSString * background;
@property (nonatomic, retain) NSNumber * index;

@end

// Level.m
#import "Level.h"

@implementation Level
@synthesize map = _map, background = _background, index = _index;

- (void)dealloc
{
    self.map = nil;
    self.background = nil;
    self.index = nil;
    [super dealloc];
}
@end

The corresponding database structure looks like this:

CREATE TABLE levels (
	ix integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
	background text,
	map blob NOT NULL
);

Fetching the objects from sqlite takes more code than using Core Data but isn’t too difficult. I need to deal with two different SQLite3 objects for the database connection and the prepared query. Rather than interpreting the SQL query every time you execute it, you will prepare the query once and execute it as many times as you want.

A good introduction to the SQLite3 C interface is available here.

Here is my level manager. My init method opens the database connection and prepares the query that will be used later. Note that the query includes a ‘?’ for variable substitution.

The GetLevel method is where the interesting stuff happens. The function sqlite3_bind_int() tells it which level number we’re looking for and sqlite3_step() actually executes the query. In this case, I’m only interested in a single row, but in many cases it will be called repeatedly as long as it returns SQLITE_ROW. I then use the sqlite3_column functions to extract data from the result row and populate my Level object. Finally, calling sqlite3_reset() will allow the prepared statement to be reused.

// LevelManager.h
@interface LevelManager : NSObject {

    NSString *_dbpath;

    sqlite3 * db;
    sqlite3_stmt * query;
}

@property (retain,nonatomic) NSString *dbpath;

@end

// LevelManager.m
#import "LevelManager.h"
#import "Level.m"

@implementation LevelManager

@synthesize dbpath = _dbpath;

- (id) init
{
    if ((self = [super init])) {
        self.dbpath = [[NSBundle mainBundle] pathForResource:@"levels" ofType:@"sqlite3"];
        sqlite3_open([self.dbpath UTF8String] , &db);

		// this query will be used to obtain a level from the database.
		// The '?' will be replaced with the level number when we perform the query
        sqlite3_prepare_v2(db, "SELECT * FROM levels WHERE ix=?", -1, &query, NULL);

    }
    return self;
}

- (void)dealloc
{
    sqlite3_finalize(query);
    sqlite3_close(db);

    self.dbpath = nil;

    [super dealloc];
}

- (Level*)GetLevel: (int)number
{
    Level *lvl = nil;

	// specify the level number we want for the query
    sqlite3_bind_int(query, 1, number);

	// request a row from the query result
    if (sqlite3_step(query) == SQLITE_ROW) {
        void *blob;
        int nbytes;

		// first, create a new level object
        lvl = [[Level alloc] init];

		// integer columns are easy
        lvl.index = [NSNumber numberWithInt: sqlite3_column_int(query, 0)];

		// string columns are a bit more complex since we need to convert a C string to a NSString
        lvl.background = [NSString stringWithCString:(char*)sqlite3_column_text(query, 1)
                                                                 encoding:NSUTF8StringEncoding];

		// BLOB columns require two calls to obtain the actual data and the length
        blob = (void*)sqlite3_column_blob(query,2);
        nbytes = sqlite3_column_bytes(query,2);

		// we use the bytes & length to create a NSData
        if (blob && (nbytes > 0)) {
            lvl.map = [NSData dataWithBytes:blob length:nbytes];
        }
    }

	// get ready to reuse the query
    sqlite3_reset(query);

	// we should return an autoreleased object
    return [lvl autorelease];
}

@end

Quick and easy image caching for UITableViews

PicSlide uses a table that lets you choose a picture to play with. For each picture it shows a thumbnail. When the thumbnails are loaded from the application’s resources, you don’t have to be too concerned with image loading time.However, when I load those thumbnails from the web, as I do now with the Magic Panda, scrolling the table can become extremely slow.

Previously, I was loading the thumbnail directly from my table data source’s cellForRowAtIndexPath: method.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"tvCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"tvCell" owner:self options:nil];
        cell = tvCell;
        self.tvCell = nil;
    }
    NSString *url = [picUrls objectAtIndex: indexPath.row];
    UIImageView *img = (UIImageView*)[cell viewWithTag: 2];
    img.image = [UIImage imageWithData: [NSData dataWithContentsOfURL:[NSURL URLWithString: url]]];
}

If you don’t have too many rows, a NSMutableDictionary makes a quick & easy image cache. I simply added one method to implement image caching:

NSMutableDictonary *imageCache;

- (UIImage *)getCachedImage: (NSString*)url
{
    UIImage* theImage = [imageCache objectForKey:url];
    if ((nil != theImage) && [theImage isKindOfClass:[UIImage class]]) {
        return theImage;
    }
    else {
        theImage = [UIImage imageWithData: [NSData dataWithContentsOfURL:[NSURL URLWithString: url]]];
        [imageCache setObject:theImage forKey:url];
        return theImage;
    }
}

When I load my table, I now call getCachedImage rather than fetching the image directly.

    img.image = [self getCachedImage: url];

The result can be seen below:

Yazzem API is live

If you haven’t heard of Yazzem, it’s a site similar to Twitter which lets you start topics about anything you want. Unlike Twitter, Yazzem is conversation oriented. You create or join a topic and add comments to the thread. Topics & comments are limited to 200 characters, making them more like a tweet and much easier to use and more lightweight than a traditional forum. Yazzem was founded by Dustin Snider and Zachary Collins, both 15, and was acquired by Teens In Tech Networks (which I’m the CTO of).

The Yazzem team has been working on an API which will allow developers to build applications that interact with Yazzem. It will be featured at the Hacker dojo API hackathon tomorrow.

Read the Yazzem team’s API announcement here. The API is documented at api.yazzem.com.

For Mac & iPhone developers, I’ve created a Cocoa wrapper class with both Mac & iPhone demo applications, available at http://github.com/mike3k/Cocoa-Yazzem.

Time to drop 2.x support.

Pocket Gamer reports that Apple is no longer charging for the 3.x firmware upgrades for iPod Touch. This means that developers can now safely drop support for firmware 2.x and move forward.

I was already planning to make I Can Has Cheezburger 2.0 require firmware 3.x, since Three20 requires it. This makes my decision a lot easier.