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 – 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.