XCode / iTunes Connect Buggy Version Numbers

Here’s an annoying little gotcha, I recently tried to submit an update to an app from 1.41 to 1.5. Of course in versioning 1.5 should be higher than 1.41 but still I got the message:


The binary you uploaded was invalid. The key CFBundleVersion in the Info.plist file must contain a higher version than that of the previously uploaded version.

Very odd, I got fairly frustrated and then tried 1.6, even 2.0 to no avail. Then after reading this post I had an idea, maybe Apple are using some sort of weird comparison and effectively 1.41 became 141, much higher than 15 (from 1.5) or even 16 (from 1.6). A good test of this was trying 1.50, which sure enough worked like a charm. Hopefully that’ll stop some people banging their head against the wall.

Protected: More portfolio content

This content is password protected. To view it please enter your password below:

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!

Android Development – Setting your location using latitude, longitude in the emulator

This is a quick post since it took me a little while to figure this one out. If you are using location services in your application and testing them on the emulator, you might be wondering how to set your location. In fact the iPhone doesn’t provide for this and defaults you to Cupertino (Apple’s HQ) which can be annoying.

But Google have thought a bit more about it. All you need to know is the port your emulator is running on (it’ll be in the title bar of the emulator), typically 5554.

Fire up a terminal and away you go, let’s say you want to set it around London, UK. That is roughly 52,0 in lat long so connect to the port through Telnet and enter:

telnet localhost 5554
geo fix 52 0 0

The final 0 is optional and sets your altitude. Easy, and now in your application your callback method onLocationChanged should be called.

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.

Older Posts »