iPhone Development – Careful of using the reserved word id

One of the most common variable names for a model class must be id. Particularly with database driven apps or those that connect to external data sources, id is the lifeblood of an object, your one way of knowing which object you’re talking about. Unfortunately in Objective C it will cause you all sorts of problems using id, which you probably won’t see until runtime. I’ve taken to using myid, although it is fairly ugly.

iPhone Development – HTTP cookies for connections and UIWebViews

A very common requirement when connecting to a server from the iPhone or within a UIWebView is the ability to set a cookie. Cookies are often used for authentication in particular. Fortunately there is a nice way to set a global cookie for any external connections your app makes using NSHttpCookieStorage. The function below sets a global cookie with the specified domain, name and value (the path used is /):

+ (void) setCookie:(NSString *)cookieName:(NSString *)cookieValue:(NSString *)cookieDomain
{
    NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
                                cookieDomain, NSHTTPCookieDomain,
                                cookieName, NSHTTPCookieName,
                                cookieValue, NSHTTPCookieValue,
                                @"/", NSHTTPCookiePath,
                                nil];

    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:[NSHTTPCookie cookieWithProperties:properties]];
}

iPhone Development – Transparant UIWebview and UITableView

A distinct lack of posts recently, mainly because of the huge volume of work I’m getting through. But hopefully that’ll mean lots of juicy tips saved up.

Anyway a very quick one again. It’s often quite nice to have transparent table views, especially with a background image on the parent view. Simply set opaque to NO and set the background colour to clear. This can either be done in Interface Builder by unchecking opaque and choosing 0% opacity on the background colour, or programmatically with:

tableView.opaque = NO;
tableView.backgroundColor = [UIColor clearColor];

Make sure your UITableViewCells also have a clear background, otherwise they’ll obviously block the background.

You can achieve a similar effect with UIWebViews as well. Simply follow the same steps. However an extra complication is you need to include a style in the HTML you load into the webview. So somewhere in your HTML styling you will need to specify:

background-color:transparent

This can either be part of the body style tag or included in a CSS file.

iPhone – Error launching program: Failed to get the task for process XXXX.

Just a small tip here, occasionally when right in the middle of development I’ve come across this crazy looking error:

Error launching program: Failed to get the task for process XXXX.

The program launches on the iPhone, blinks and then as disappears. Very worrying. In my case it was because I’d left the build to ad-hoc, switching to debug fixes it straight away. Very odd it would even try and launch though!

iPhone Development – Gradient background UIViews

Here’s a neat little tip I’ve found can spruce up your applications easily. It’s very easy to create a UIView with a solid background colour, but in our new rainbow coloured Web 2.0 shiny iPhone world, gradients are called for. You could of course create a gradient image and set the background image of the view, but this has several drawbacks:

  • For every different sized view, you either need to re-cut the gradient image or scale it.
  • It is a waste of space in your application, plus gradient images are often quite big files.
  • You need an image for each colour.
  • It is relatively expensive to load and render an image as a background for a view.

However using a bit of trickery, you can create a general GradientView class which extends UIView and overrides drawRect to achieve a gradient background. So I present GradientView:

//
//  GradientView.m
//  evilrockhopper
//
//  Created by Daniel Wichett on 10/12/2009.
//
//  Extension of UIView to show a gradient, generally used as a background on other views.
//  Mirrored indicates a gradient that is colour1 -> colour2 -> colour1. Non-mirrored simply goes from colour1 -> colour2.

#import "GradientView.h"

@implementation GradientView

@synthesize mirrored;

- (void)drawRect:(CGRect)rect
 {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    CGGradientRef glossGradient;
    CGColorSpaceRef rgbColorspace;
    size_t numLocations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };

    //Two colour components, the start and end colour both set to opaque.
    CGFloat components[8] = { startRed, startGreen, startBlue, 1.0, endRed, endGreen, endBlue, 1.0 };

    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, numLocations);

    CGRect currentBounds = self.bounds;
    CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
    CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds)/2.0);
    CGPoint bottomCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds));

    if (!mirrored)
    {
        // draw a gradient from top to bottom centred.
        CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, bottomCenter, 0);
    }
    else
    {
        // draw a gradient from top to middle, then reverse the colours and draw from middle to bottom.
        CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, 0);
        CGFloat components2[8] = { endRed, endGreen, endBlue, 1.0, startRed, startGreen, startBlue, 1.0 };
        CGGradientRelease(glossGradient);
        glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components2, locations, num_locations);
        CGContextDrawLinearGradient(currentContext, glossGradient, midCenter, bottomCenter, 0);
    }

    // Release our CG objects.
    CGGradientRelease(glossGradient);
    CGColorSpaceRelease(rgbColorspace);
}

// Set colours as component RGB.
- (void) setColours:(float) _startRed:(float) _startGreen:(float) _startBlue:(float) _endRed:(float) _endGreen:(float)_endBlue
{
	startRed = _startRed;
	startGreen = _startGreen;
	startBlue = _startBlue;

	endRed = _endRed;
	endGreen = _endGreen;
	endBlue = _endBlue;
}

// Set colours as CGColorRefs.
- (void) setColoursWithCGColors:(CGColorRef)color1:(CGColorRef)color2
{
	const CGFloat *startComponents = CGColorGetComponents(color1);
	const CGFloat *endComponents = CGColorGetComponents(color2);

	[self setColours:startComponents[0]:startComponents[1]:startComponents[2]:endComponents[0]:endComponents[1]:endComponents[2]];
}

- (void)dealloc
{
    [super dealloc];
}

@end

And the header file GradientView.h as so:

//
//  GradientView.h
//  evilrockhopper
//
//  Created by Daniel Wichett on 10/12/2009.
//

#import <UIKit/UIKit.h>

@interface GradientView : UIView
{
    float startRed;
    float startGreen;
    float startBlue;

    float endRed;
    float endGreen;
    float endBlue;

    BOOL mirrored;
}

@property (nonatomic) BOOL mirrored;

- (void) setColoursWithCGColors:(CGColorRef)color1:(CGColorRef)color2;
- (void) setColours:(float) startRed:(float) startGreen:(float) startBlue:(float) endRed:(float) endGreen:(float)endBlue;

@end

So an example usage could be:

    GradientView *redToBlack = [[GradientView alloc] initWithFrame:CGRectMake(0,0,320,50)];
    [gradientBg setColoursWithCGColors:[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0].CGColor:[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0].CGColor];

This creates a vertical gradient from red to black. If you wanted it to go from red to black to red, you can just set redToBlack.mirrored = YES.

It currently ignores the alpha value of the colours passed in, this would be trivial to add of course if required. If you want to experiment with other types of gradients, read the documentation for CGGradientCreateWithColorComponents. Feel free to use the code above, you can download the source by clicking on the menu in the top right of each code snippet.

iPhone Development – XCode quirks, bugs and provisioning problems

Although XCode is not the worst IDE in the world, it is definitely buggy.

Countless times I have come up against a bug, almost always to do with provisioning profiles, build failures or sync issues which make no sense whatsoever. To prevent you banging your head constantly against a wall, when a problem seems totally illogical I always take these steps.

  1. Stop the app
  2. Delete from the device / simulator as appropriate
  3. Restart XCode
  4. Clean all builds
  5. Pray

If that doesn’t work:

  1. As above
  2. Restart Mac
  3. Restart iPhone

Trust me, more often than not that will fix all sorts of problems.

iPhone Development – Keeping the UI responsive and a background thread pattern

One of the biggest mistakes you can make as an iPhone developer is running any long tasks on the main thread.

Let’s take a concrete example, say you want to retrieve some data from a web server somewhere. If you run this method on the main thread, all user interface activity is blocked until it finishes. The app will cease to respond to any user input (bar a reset / program kill) and the screen will freeze. Not good.

As any programmer who has worked with interfaces should know, all long running tasks need to be run on a background thread. One more vital point is that any changes to the user interface (ANY changes to a UI component) must always be run back on the MAIN thread. That is because those components are not thread safe and should not be changed by multiple threads. So let’s say after you have fetched your data, you want to change a label on the screen, this must be done on the main thread. I’ve come up with a little pattern I use in these cases, for example:

- (IBAction) performLongTaskOnClick: (id)sender
{
    statusMessage.text = @"Running long task";
    [self performSelectorInBackground:@selector(performLongTaskInBackground) withObject:nil];
}

- (void) performLongTaskInBackground
{
    // Set up a pool for the background task.
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    // perform some long task here, say fetching some data over the web.
    //...

    // Always update the components back on the main UI thread.
    [self performSelectorOnMainThread:@selector(completeLongRunningTask) withObject:nil waitUntilDone:YES];

    [pool release];
}

// Called once the background long running task has finished.
- (void) completeLongRunningTask
{
    statusMessage.text = @"Finished long task";
}

To summarise, we have 3 methods, performLongTaskOnClick might be called in response to a button click say, and kicks off the long running task on a new thread with performLongTaskInBackground. Then when that finished it calls completeLongRunningTask back on the main thread. Notice that we set up an autoreleasepool in the background method, you must do that or you will be leaking memory from that thread. Now you should keep your iphone app nice and responsive.

iPhone Development – Reverse Geocoding

A rather nifty new feature of the iPhone SDK is the ability to reverse geocode a location. Some of you may be wondering what normal geocoding is! Well, geocoding is turning an address into a latitude and longitude. So reverse geocoding is, without surprise, turning a latitude and longitude into an address.

So, in combination with the normal iPhone lookup services, you can find out the lat,long of someone and then convert that into a friendly place name such as the town they are in. It’s very easy to do, firstly you need to implement the MKReverseGeocoderDelegate protocol in your chosen class which has two callbacks, one for when a reverse geocode is successful and one for when there is an error:

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder  didFindPlacemark:(MKPlacemark *)placemark;
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error;

So we create and initialise a reverse Geocoder with the following code. Note the delegate is set to self so the methods defined above should be places in the same class. locationToLookup defines where the location name we want to look up is (as a latitude, longitude coordinate).

        // use whatever lat / long values or CLLocationCoordinate2D you like here.
        CLLocationCoordinate2D locationToLookup = {52.0,0};
        MKReverseGeocoder *reverseGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:locationToLookup];
        reverseGeocoder.delegate = self;
        [reverseGeocoder start];

The first time I tried this I got the very cryptic error message below:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[LBSGAddressComponent _mapkit_cache_heapTime]: unrecognized selector sent to instance 0x166c00'

After a lot of head scratching I realised this is because you must only ever have one reverse geocoder running at once. So I made the reverse geocoder a class variable (to keep track of it) and added:

        if (reverseGeocoder != nil)
        {
            // release the existing reverse geocoder to stop it running
            [reverseGeocoder release];
        }

        // use whatever lat / long values or CLLocationCoordinate2D you like here.
        CLLocationCoordinate2D locationToLookup = {52.0,0};
        MKReverseGeocoder *reverseGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:locationToLookup];
        reverseGeocoder.delegate = self;
        [reverseGeocoder start];

This will stop any existing lookups. And so if successful your callback method will be called with an instance of MKPlacemark that represents where your lookup is. Easy.

Localsalefinder – BView’s iPhone App

Eating OutLondon Bridge results


To accompany my technical ADD, as part of my job at BView we’ve now released another product in another language entirely, an iPhone application.

Called Localsalefinder and powered by the BView API, it’s a fantastic iPhone app for finding local offers and redeeming them without printing out or clipping coupons.

Once we finish off all the finishing touches to the vouchers, this is going to be one powerful app. It includes integrations from the main restaurant booking engines (Toptable / Livebookings and more) plus our own vouchers and those sourced by 3rd parties.

If you’ve got an iPhone check it out! In terms of Objective-C development, I’m going to start a series of articles about the common problems and pitfalls of iPhone development.

Dan.