Trifork Blog

iPhone as the guide to Lowlands Paradise

July 24th, 2009 by
|

Recently I have become interested in developing apps for the iPhone. I am about to finish my first app called “LLowGuide”, which is supposed to be a guide for a Dutch music festival called Lowlands. Lowlands will take place on the 21st, 22nd and 23rd of august 2009. As of the moment of writing, I submitted a very early version of the app into the iPhone’s App Store. But it has been in review for 8 days now, with no indication of when the review will be finished. I am starting to doubt whether I am able to release the final version before the festival starts. But in any case, it was a great iPhone programming exercise.

While developing I gathered some good practices, which could be applied in other apps as well. I want to share with you a few practices which I found particularly useful while developing the app. I hope that this way you get excited about iPhone programming (if you weren’t already) and get motivated to write your own app (if you didn’t already).

UPDATE: Last week (August 6th), my Lowlands app passed the review and was put into the App Store. At that time the app had been “in review” for exactly 3 weeks. But unfortunately, Lowlands also decided to write their own app and announced that only 2 weeks ago or something. The company developing that iPhone app called me and asked me to remove my app from the App Store to avoid confusion with their app. Also I was not supposed to use the Lowlands name and logo etcetera… It’s too bad, but the latest version of my app would never make it into the App Store anyway before the festival starts, because app reviews just take too long.

But first, let’s take a look at few screenshots of the current version, so you will have a better understanding of what I am talking about:

Set up a layout with a tab bar / navigation / table view

As you can see in the screenshots above, I used a tab bar with in the first tab a navigation view containing a table view. The navigation view takes care of navigating to for example a detail page and providing a button to go back to the previous page. I spent quite some time to find out how to set up this layout using Interface Builder, so I think it’s worth sharing.

  1. Create a new “Tab Bar Application” in Xcode.
  2. Open MainWindow.xib which you can find in the Resources group (folder). Interface Builder will start and show you the layout of the tab bar.
  3. In Interface Builder you will see two tabs. The first tab directly contains a view with a label and a text view. The second tab is referencing another controller/XIB file for the view. I think it’s a good practice to always set up tabs to reference other XIB files. So remove the entire view controller of the first tab. This is where you can find the controller to remove:
  4. In Xcode, create a new XIB file for the first tab (File > New File > User Interface > View XIB). Call it “FirstView”. Also create a table view controller for the XIB file (File > New File > Cocoa Touch Class). Make sure to select “UITableViewController” from the subclass dropdown. Call it “FirstViewController”.
  5. In MainWindow.xib (in Interface Builder) drag a “Navigation Controller” from the Library (Cmd+Shift+L) onto the “Tab Bar Controller” window. Notice this adds a new tab to the tab bar.
  6. Navigate to the navigation controller you just added and open the inspector window (Cmd+Shift+I). In the identity tab, change the “Class” field to “FirstViewController” (name of the class you just created).
  7. In the Attributes tab of the inspector window, set the “NIB Name” field to “FirstView” (name of the XIB file you just created).
  8. Now open up the FirstView.xib file and set the Class field to “FirstViewController” in the identity tab of the inspector window.
  9. Remove the “View” and drag in a “Table View” from the library. Now connect the “view” property of the File’s Owner by right click dragging from the File’s Owner to the Table View:
  10. That’s it! Now you can run the application from Xcode by clicking the “Build and Go” icon.

Customize the tab bar icons

When trying to customize the icons in your tab bar, you can select from a list of built in icons. Each icon has a specific title associated with it. As soon as you try to change the title, the icon disappears. In other words, you can only use the built-in icons if you’re fine with the default title. You will probably have to find/create your own icons. To create an icon you have to create an image file which is:

  • of type PNG
  • transparent and/or translucent
  • around 30×30 pixels in size

If you select an image for an icon, all colors in the image are ignored. Instead, the alpha value of each pixel is used. So a fully opaque pixel will be displayed as a gray/blue pixel, a translucent pixel will be darker gray/blue pixel and a fully transparent pixel will be black.

Icons like this are hard to find so you will probably end up having to learn some Photoshop to create them yourself.

Default image and app icon

The default image is the image that is shown immediately when you start an app on your iPhone, before the app even starts loading. This image usually is a screenshot of your application in some kind of empty state, giving the user the impression the application has already started up. Instead, this default image can also be used as a splash screen for your app (e.g. with some nice logo in it). But whatever you put in this image, make sure it’s called “Default.png” (note the capital “D”) and is of size 320×480. Put the image file in your Resources folder.

The icon of your app needs less explanation. It is the icon you see on the home screen of your iPhone. If you tap it, the application starts. The only thing you need to do is create a PNG image file of size 57×57 called “icon.png” and put in the Resources folder.

Final note on icons: When you eventually want to submit your app to the App Store, Apple requires you to upload a 512×512 icon image for your application. This icon will be used for displaying your app in iTunes and the App Store. So when you create your app icon, try to make it in high resolution and then scale down to 57×57. This way the app store icon and app icon are similar.

SQLite3 for data storage

On the iPhone you can choose to have a local SQLite3 database to store the data you want to display. Unfortunately to use SQLite3 you have to use an old C library, which is not object oriented. So it’s a good practice to isolate calls to that library in some data access classes. I tried to set up the same architecture as I am used to when writing Java applications. This means I created the following classes for data access:

  • DataSource – This maintains the connection to the database and provides some convenient methods to access data in any table.
  • Dao – A DAO (Data Access Object) class accesses data of a certain type. The name of the DAO indicates the type. Example class names: PersonDao, ContactDao, etc. Each DAO uses a singleton instance of the DataSource to execute queries. A DAO is also responsible for converting data from the database to objects.
  • Dto – A DTO (Data Transfer Object) holds data of a certain type. An instance of this class represents a row in a certain table (type). DAO’s return these objects. Example names: Person, Contact.

My DataSource class eventually looked like this:

#import "DataSource.h"
@implementation DataSource
- (id)initWithFile:(NSString *)databaseFilePath {
	self = [super init];
	int result = sqlite3_open([databaseFilePath UTF8String], &databaseHandle);
	NSAssert1(SQLITE_OK == result, NSLocalizedStringFromTable (@"Unable to open the sqlite database (%@).", @"Database", @""), [NSString stringWithUTF8String:sqlite3_errmsg(databaseHandle)]);
	return self;
}
- (void)close {
	if (databaseHandle) {
		sqlite3_close(databaseHandle);
	}
}
- (sqlite3_stmt *)prepare:(NSString *)sql {
	const char *utfsql = ;
	sqlite3_stmt *statement;
	
	if (sqlite3_prepare_v2(databaseHandle, utfsql, -1, &statement, NULL) == SQLITE_OK) {
		return statement;
	} else {
		return 0;
	}
}
@end

So you construct this class with a reference to the database file, and call the “prepare” method to execute a statement and parse the results. So that’s exactly what a DAO is responsible for. The following is a simplified version of my ActDao, which provides access to all Lowlands acts in the database.

#import "ActsDao.h"
@implementation ActsDao
- (ActsDao *) initWithDataSource:(DataSource *)dataSourceToSet {
	self = [super init];
	dataSource = dataSourceToSet;
	return self;
}
- (Act *) getActById:(int)id {
	NSString *sql = [NSString stringWithFormat:@"SELECT id, name, description FROM acts WHERE id = %i", id];
	
	sqlite3_stmt *statement = [dataSource prepare:sql];
	Act *act;
	if (sqlite3_step(statement) == SQLITE_ROW) {
		act = [[[ActDetails alloc] init] autorelease];
		act.id = sqlite3_column_int(statement, 0);
		act.name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
		act.description = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
	}
	return act;
}
- (void)dealloc {
	[dataSource release];
	[super dealloc];
}
@end

The getActById method returns instances of the Act class. The Act class is just a simple class (DTO) with some properties, so it’s not worth showing.

You typically access the SQLite3 database from table view controllers. Many examples I found suggest executing an SQL query for each row in the table, since this is the most convenient way to query your data. If you do this, you will very quickly discover this doesn’t perform at all. The frame rate of your table view will be horrible. Instead try to execute as few queries as possible. In this case you should fetch the entire contents of the table in one query and store it in memory (in an NSArray for example). This will make the table view perform A LOT better, at the cost of higher memory consumption.

Custom table view cells using Interface Builder

I think it’s a good practice to try to use Interface Builder as much as possible, resulting in less code and better maintainable user interfaces. So when trying to create a customized UITableViewCell I think it’s wise to design that cell using Interface Builder.

  1. Create a new class for your cell in Xcode (File > New File > Cocoa Touch Class). Make sure to select “UITableViewCell” from the subclass dropdown.
  2. Create a new XIB file for your cell, open it and replace the “View” with a “Table View Cell” (drag from the library)
  3. Set the class field of the “Table View Cell” to the name of the class you just created (in identity tab of attributes inspector)
  4. Set the identifier field in the attributes tab to a unique string (e.g. “FavoriteActCell” in my case) which is used for reusing instances of the cell. I will explain this in a moment.
  5. Design the cell any way you want!
  6. Define properties on your cell class for all sub views of the table view cell. Connect the sub views to the appropriate properties using right click drag in Interface Builder.

As an example, this is what my favorite acts table cell looks like in Interface Builder:


With related properties defined in FavoriteActCell.h:

@interface FavoriteActCell : UITableViewCell {
	IBOutlet UILabel *actName;
	IBOutlet UILabel * timeString;
	IBOutlet UILabel *stageName;
	IBOutlet UIImageView *actImage;
	IBOutlet UILabel *tags;
}

@property (nonatomic, retain) UILabel *actName;
@property (nonatomic, retain) UILabel * timeString;
@property (nonatomic, retain) UILabel *stageName;
@property (nonatomic, retain) UIImageView *actImage;
@property (nonatomic, retain) UILabel *tags;

@end

So now you created a custom cell, it’s time to use it in a table view. To do this, implement the cellForRowAtIndexPath method of your table view controller as follows:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *cellIdentifier = @"FavoriteActCell";
	FavoriteActCell *cell = (FavoriteActCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
	if (cell == nil) {
		NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FavoriteActCell" owner:nil options:nil];
		for(id currentObject in topLevelObjects) {
			if([currentObject isKindOfClass:[FavoriteActCell class]]) {
				cell = (FavoriteActCell *)currentObject;
				break;
			}
		}
	}
	FavoriteAct *act = [[favoriteActsByDate objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

	cell.actName.text = act.name;
	cell.actImage.image = [UIImage imageNamed:act.imageName];
	cell.timeString.text = act. timeString;
	cell.stageName.text = act.stage;
	cell.categories.text = act.categories;

    return cell;
}

You see that relatively complex code is needed to create an instance of the cell using XIB, but because instances of the cell are reused this isn’t a performance issue. A cell is reused through the dequeueReusableCellWithIdentifier method using a reuse identifier (this is the same string you entered in Interface Builder in the identifier field).

Summary

So I explained about some of the things I learned while writing the Lowlands iPhone app. I really like developing for the iPhone platform, although the Objective C language is something to get used to. I especially like the fact that all view instances are managed by XIB files which keeps the code clean.

I hope that this way I got you excited about iPhone programming and motivated you to start writing your own app and maybe even use some of the practices I just explained. If you already have experience with the platform, I hope these tips help you out or improve your code.

11 Responses

  1. July 24, 2009 at 16:20 by Alef Arendsen

    Hey,

    way cool! I hope it’s going to be published ‘coz I would really need this!

    Problem is: where to find charging points for your iPhone at LowLands without having to queue up for hours!

  2. July 24, 2009 at 16:27 by Bram Smeets

    Nice article Tom, guess I need to buy a Mac now to start developing my own apps 😉

    But the big question is: how to get your app on my iPhone before Lowlands?

    Greetings,
    Bram

    • July 24, 2009 at 19:24 by Tom van Zummeren

      Well Bram, you need a Mac anyway. That is a good practice in general 😉

      But I think I can find a way to get the app on your iPhone. I can deploy it on my own iPhone, so why not on yours? 🙂

  3. July 24, 2009 at 16:33 by Frank Scholten

    Good stuff, hope your app will be released soon!

  4. July 24, 2009 at 17:26 by Leonard WOlters

    Very nice article. Good work Tom

  5. July 25, 2009 at 12:45 by Rob van Maris

    There’s also an easy way to distribute your app to other iPhones that doesn’t require the App Store. It’s called Ad Hoc distribution, and allows the app to be uploaded from a computer to the iPhone using iTunes. More info in Apples iPhone Dev Portal:
    http://developer.apple.com/iphone/manage/distribution/index.action

  6. July 25, 2009 at 20:47 by Uri Boness

    Good stuff Tom!!!!

  7. August 1, 2009 at 14:37 by Neil

    where can you get this app if your in the UK?? help me please i cant figure out the lowlands website!

  8. August 12, 2009 at 14:39 by Tom van Zummeren

    Neil, right now a Lowlands app is released by Lowlands itself. You can find it by searching for “lowlands”, but I’m not sure if they put it in the UK app store as well…

  9. June 29, 2010 at 05:07 by Keri Witkus

    We’ve got a desire for this area of interest for a short time now. I’ve been hiding and looking at the comments avidly so wanted to convey my thanks for supplying me with quite a few excellent studying materials. I watch for much more, and choosing a more positive element inside talks here, whilst studying as well!!

  10. February 17, 2012 at 04:26 by best iphone games

    best iphone games…

    […]iPhone as the guide to Lowlands Paradise « Dutchworks Blog / Dutchworks: Enterprise Java, Open Source, software solutions, Amsterdam[…]…