iPhone Multitouch

When handling touch events, the events you get via the touch events in the UIView class is pretty much the raw events from the hardware, wrapped in Objective C objects. If you’re dragging around one finger, then while keeping that finger down touch with a second, you get a new touchesBegan event. Lift the first finger but keep the second, and you get a touchesEnded event for the first finger, but the move events for the second continue.

I suppose this is what one would expect. But it means that if you drag with two fingers, you probably are going to receive touchesMoved events for each finger separately, rather than receiving a touchesMoved event for both fingers at the same time.

This implies that if you want to track all the fingers moving around at the same time, you need to maintain the state of affairs; that is, you need to keep track of the touch events that haven’t moved as well as the ones that have moved.

Here is some code I wrote which keep the current touch events in a secondary set, so I can see and track all the fingers as they move. This isn’t the best code in the world, but it does prove the concept.

@implementation TestTouch

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
		self.multipleTouchEnabled = YES;
		set = [[NSMutableSet alloc] initWithCapacity:10];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
	[[UIColor whiteColor] setFill];
	UIRectFill(rect);
	
	[[UIColor blackColor] setFill];
	for (UITouch *t in set) {
		CGRect r;
		CGPoint pt = [t locationInView:self];
		r.origin.x = pt.x - 22;
		r.origin.y = pt.y - 22;
		r.size.width = 44;
		r.size.height = 44;
		UIRectFill(r);
	}
}

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

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	for (UITouch *t in touches) {
		[set addObject:t];
	}
	[self setNeedsDisplay];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	[set removeAllObjects];
	[self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	for (UITouch *t in touches) {
		[set removeObject:t];
	}
	[self setNeedsDisplay];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	[self setNeedsDisplay];
}

@end

If you need to capture when all the touches start and when they all end, you can do this by testing if the set (defined as NSMutableSet in the class) is empty.

For whatever reason I want to think that the touches set passed is full of all of the fingers, but no–you only get the finger that changed, not the fingers that stayed still or didn’t change. Thus, you need a set to capture them all.

UI Performance

I just spent the weekend rewriting an Android application for performance.

When I first start learning a UI framework, be it for iOS, Android, Java Swing, GWT, MacOS, Windows, or X, the two questions I first want to answer are:

  • How do I build a custom view?

and

  • How do I build a custom view container and perform custom layout of the children within that container?

With those two bits of information you can rule the world. (Or at least the framework.)

The problem with most applications running like a dog, especially on mobile devices, is that most frameworks are inefficient at maintaining more than a couple of dozen views within a window. This isn’t a problem when you’re talking about putting up a dialog with a bunch of controls or have a relatively static display. But when you start talking about dragging and dropping objects, or when you are talking about scrolling items in a scroll view, things can go to hell very quickly.

To take a concrete example, I put together a view which contains a scrolling area, and inside the area each item in the list of items is represented by an image, a couple of buttons and a label of text. The natural way in Android to do this is to build a ListView, create a ListAdapter and in response to each request for a view, use a LayoutInflater (as needed) to construct a view hierarchy that contains a layout or three, representing the buttons as views, the image as a view, and the text as a view, all layered on other views. On the iPhone it’s the same story; a UITableViewCell can contain a hierarchy of other views which represent the contents of the cell.

For a list of 20 items, this translates into over a hundred-something views, minimum.

And on both Android and iOS, dragging around all that crap takes forever.

My solution in each of these cases is to reduce the complexity. On the iPhone override the UITableViewCell as a single custom view which draws the buttons, widgets and components in the -drawView() method. That way, on the screen you have 7 views, not over a hundred.

On Android the solution was even more radical: instead of a list view, I just used a ScrollView and created a custom view which draws the entire list. Use the Canvas’ getClipBounds() method in the canvas passed into onDraw() to determine what needs to be drawn, and draw it all in one view.

With this technique you eliminate manipulating a hundred views, and can easily make something go from impossibly jerky to smooth as silk, even on slower devices.

Just received the following e-mail suggesting changes to FlowCover.

I don’t really have the time to respond or expand on this e-mail, and I don’t know if the code that Ilkka Pirttimaa provided is correct. But I wanted to post this here so other people playing with FlowCover can play with the code.

Here are his suggested changes:

Hello!

I like your Flow Cover implementation a lot, thank you!
I needed to get event when animation is stopped. This is my suggestion to code:

FlowCoverView.h:

@protocol FlowCoverViewDelegate
- (int)flowCoverNumberImages:(FlowCoverView *)view;
- (UIImage *)flowCover:(FlowCoverView *)view cover:(int)cover;
- (void)flowCover:(FlowCoverView *)view didSelect:(int)cover;
- (void)flowCover:(FlowCoverView *)view animationStoppedAt:(int)cover;  //ADDED

@end

FlowCoverView.m:

- (void)driveAnimation
{
	double elapsed = CACurrentMediaTime() - startTime;
	if (elapsed >= runDelta) {
		[self endAnimation];
		/*
		 * Change from Ilkka Pirttimaa <ilkka.pirttimaa@gmail.com>:
		 * You can listen this callback if you need to change UI when animation
		 * is stopped.
		 */
		if (delegate) [delegate flowCover:self animationStoppedAt:(int)floor(offset + 0.01)];	// make sure .99 is 1
	} else {
		[self updateAnimationAtTime:elapsed];
	}
}

The code appears to be a useful addition to the existing code base. Someday, when I have some spare time (*cough*), I’ll update the code base with this and other suggestions.

And then Apple changes the Rules. Again.

So I uploaded J2OC, and had lost interest in it. After all, who needs a second “let’s recompile Java into Objective C” in order to build iPhone and Android applications, if Apple isn’t going to allow it?

Then Apple does this: Statement by Apple on App Store REview Guidelines

In particular, we are relaxing all restrictions on the development tools used to create iOS apps, as long as the resulting apps do not download any code. This should give developers the flexibility they want, while preserving the security we need.

What I would ideally want is a Java VM kernel that can be linked into an iPhone application, one capable of running a jar file. Because ideally I’d like to write model code in Java–so I can port that model code to Android. Yet I don’t want UI bindings into the Apple API–I’d rather just build the UI twice, while the (more complicated) model code remains the same.

Thank you Apple. Maybe I’ll document J2OC better and provide some sample programs. It really is a cool little bit of technology. 🙂

Something Funny Happened To Me On The Way To Release.

So I started playing with parsing Java class files, creating a cross compiler capable of converting Java class files into Objective C files. I even had a sufficient amount of Apache Harmony running so I could use a good part of the java.lang and java.util classes; roughly in parity with the GWT cross compiler that can compile Java class files into Javascript.

Then Apple dropped the “no cross compiling” bombshell.

Now, keep in mind that I’m just me, tinkering on my spare time during weekends. I don’t have the desire or the time to go up against Apple. I’d rather allow the XMLVM project (which has a well established ecosystem, or so it seems) to decide to go (or not go) against Apple’s wishes.

Then time went by, and I sort of lost interest in this thing.

So I’ve taken the liberty to post the source code here: the Java to Objective C Compiler sources, and the J2OC RTL, which contains a subset of the Apache Harmony project, and implementing the java.lang and java.util classes.

It’s been an interesting project, and hopefully in the next few weeks I’ll document how this all works–including the wierdnesses and pitfalls I came across with the Java VM to get Apache Harmony to work. (Nothing like working through a very large collection of class files to find all the fringe cases.) The output code was intended to be human readable–but it really isn’t for some expressions.

But I’ll describe that in the next few weeks.

And at some point I’ll post an example iPhone application which includes Java code.

Note that my approach was different than the XMLVM project. Instead of providing Java bindings of the iOS libraries, my intent was to only allow the compilation of a computational kernel, then have the user provide the UI elements separately for Android, the iPhone, the iPad, and whatever other target the code was to compile for.

So you won’t find a turn-key solution for recompiling Android code and have it run on the iPhone. You should really check out the XMLVM project instead.

All this code, by the way, is being published under a BSD style license: go ahead and use the code, but leave me out of it and don’t blame me if it goes haywire.

separator.png

While I don’t intend to get into the functioning of the compiler, I will give a taste of how the code works. The bulk of the .class file parser, which reads and loads the .class file data into memory, is contained in the class ClassFile in com.chaosinmotion.j2oc.vm. This class takes in its constructor an input stream opened to the first byte of a .class file, and loads the entire class file into memory.

Once read, the entire class file can be accessed using the getters associated with that class. The bulk of the code contained inside the .vm (and subpackages within .vm) are used to represent the contents of the class file. The .vm.data classes contain the various data types used to store the meta data within a class file (such as the method names, the attributes fields, and the like), and the .vm.code classes contain a code parser to convert the code within the .class files into an array of processed instructions.

Once the instructions are parsed (by the vm.code.Code class), the code in a method is represented as an array of code segments; a run of instructions that starts with an instruction first jumped into by another instruction, and terminates with either the end of the method or with a jump instruction. In other words, a CodeSeg (Code.CodeSeg class) is a section of instructions that always enters at the first instruction and executes sequentially to the last instruction in the segment. Additional information, such as the list of variables that are used when the segment is entered are noted; this is the current state of the Java operator stack as this segment is entered.

Ultimately the code parser and class file reader represents the code in a .class file in memory in an intermediate state that can then be used to write Objective C with the WriteOCMethod class (com.chaosinmotion.j2oc.oc). A class, CodeOptimize (.oc package) provides utilities that determine if code preambles must be written for memory management or for exception handling: memory management preamble does not need to be written if I never invoke another method. (This is the case for simple functions which return a field or does simple math.)

The theory is that in practice, it should be possible to replace the code writer method with a writer method capable of writing a different language, such as C++ or C.

separator.png

In the future, when I have more time, I’ll write more about the J2OC project. But for now, if there are any segments or parts you want to use or play with, be my guest.

GWT, ScrollPanel and the iPad.

GWT makes it easy to create a scrolling panel within an HTML driven web site, by using the ScrollPanel class. ScrollPanel itself expands into a div tag containing the overflow attribute; specifying which axes the scroll bars land on translate into overflow-x or overflow-y attributes in the generated HTML DOM object.

And the iPad (and iPhone and iPod Touch) do not allow you to scroll within the overflow tags; the touch-drag gesture only scrolls the entire page.

What this means is if you are putting together a GWT application that you want to work on the iPad, the natural instinct to create a root panel the full size of the screen and tossing in a ScrollPanel area in the lower right that resizes with the window is incorrect. Instead, you are better off generating your GWT code so that it generates the properly sized HTML, and scroll around the GWT application using the window’s scroll bars.

iPhone OS Fragmentation.

Apple Lists iPhone OS 4 Compatibility, Excludes Original iPhone and 1st Gen iPod Touch

Up until now the biggest advantage of the iPhone ecosystem is that you could simply code for the latest OS version to ship, and unless you needed certain hardware (such as the camera in the iPhone), you just wrote your software and you could guarantee that you could run on whatever platform you wanted.

This represents a serious advantage to the iPhone development model over Android, where currently shipping phones contain every OS version from v1.6 to v2.1, or Windows Mobile which has a similar spread of currently shipping versions from 6.0 to 6.5.

The first crack in this came from OpenGL support–but that made sense: only the latest 3rd generation iPod Touch or iPhone 3GS would have the hardware necessary to support OpenGL ES v2.0 instead of v1.1. The differentiation could be handled (for the most part) during initialization. And really, this only affects 3D games.

The next crack came with the introduction of the iPad. When the iPad was announced as running iPhone OS 3.2, I just naturally assumed within a week of the iPad launch we’d see an iPhone OS 3.2 update come out for the iPhone and iPad Touch which perhaps provided some minor APIs used to detect screen size, and maybe wrap some of the new UI calls with stubs that alert the developer. Well, Apple released build tools that make it possible to ship code that can run on v3.1 or v3.2, and at run time detect if the symbols are available. The work is relatively simple: from my code I just wrote:

- (IBAction)doOptions:(id)sender
{
	NSArray *array = [[NSBundle mainBundle] loadNibNamed:@"OptionsViewMenu" owner:self options:nil];
	OptionsViewController *ctrl = [array objectAtIndex:0];
	ctrl.menu.delegate = self;
	
	if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
		if (popup != nil) [popup release];
		
		Class uiPopoverController = NSClassFromString(@"UIPopoverController");
		
		popup = [[uiPopoverController alloc] initWithContentViewController:ctrl];
		ctrl.popup = popup;
		CGRect loc = ((UIView *)sender).bounds;
		[popup presentPopoverFromRect:loc inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
	} else {
		ctrl.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
		[self presentModalViewController:ctrl animated:YES];
	}
}

My options user interface view controller now shows up in a pop-up on the iPad, and drills down (with a rotation animation) on the iPhone. To get the size right I simply included in my options view controller the following code in -viewDidLoad:

	if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
		CGSize size = self.contentSizeForViewInPopover;
		size.height = 44 * 10;
		self.contentSizeForViewInPopover = size;
	}

And the height is kept to a reasonable size.

Okay, I’m not a huge fan of conditional code, but honestly this only comes up in a few places, so it doesn’t affect the majority of my code base.

But now the final straw is coming with the iPhone OS v4 release in the summer. That final straw is simply this:

There will be phones in the iPhone/iPod Touch echosystem in use by users which are cannot run the latest iPhone OS operating system, which must be supported by developers if they are to reach the entire Apple iPhone/iPod Touch ecosystem.

Sure, it’s not as bad as the Android situation, where v1.6 of the operating system is shipping in hardware being sold today. (The Cliq and G1, specifically.) But it’s still a problem: as a developer it means I need to maintain older hardware to validate that my software runs in v3.1.3 which will probably be the latest version of the iPhone OS that can run in older hardware.

And worse: Apple announced that Multitasking will only run on the 3rd generation hardware. Which means all those applications that want to take advantage of multitasking will need to have code in place to deal with hardware that doesn’t have multitasking calls available to deal with the large numbers of iPhone 3G devices (such as mine) floating around out there.

Apple’s fragmentation perhaps could be seen as inevitable. But I don’t think so: at the very least ship a version of the iPhone OS v4 that runs on the basic hardware that has the appropriate calls to allow software to detect features that are not present, like multitasking. Provide a way in the simulator to easily test against older versions. And because the number of hardware combinations is growing substantially that need to be tested, provide an easier way to distribute beta test code. (If Apple won’t allow applications to be shipped outside of the App Store, then at least provide a “beta” section of the Apple Store that is not linked to the main Apple Store home page, with applications in beta testing that can only be linked to via an explicit URL.)

Apple’s ecosystem is large and powerful–but it’s still a little young, and experiencing some growing pains. This is the biggest growing pain: how do developers deal with a non-homogeneous hardware and OS ecosystem? Once we adjust to the iPad and iPhone, the next step will be dealing with a third size form-factor, or different iPhone screen sizes. But that won’t be overcome until Apple can help reduce the testing burden–which is the biggest problem you have when you have a non-homogeneous target environment.

Yes it has, yes I am, and really?!?

One fix installations stuck at ‘Preparing…’

Since moving to Snow Leopard, I’ve noticed an innumerable number of times where software installations would get stalled at the “Preparing…” stage. If you’ve noticed it too, chances are you’re an iPhone developer, too…

It turns out the simple fix for this problem is to quit the iPhone Simulator, and then try the installation again.

A useful little chunk of iPhone code.

This is something I hacked together for a program I’ve been tinkering with. Add this to your application delegate and it will trap uncaught Objective C exceptions, uncaught signals, and uncaught C++ exceptions (if you like messing around with Objective C++), and write a stack trace and detailed report which your application will offer to e-mail in the event that your program crashes in the field.

The preliminaries: the headers to add, global variables and the like:

#include <execinfo.h>
#include <exception>

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

static NSString *GBug;

/************************************************************************/
/*																		*/
/*	Crash Reporter														*/
/*																		*/
/************************************************************************/

static NSString *GetBugReport()
{
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *dir = [paths objectAtIndex:0];
	return [dir stringByAppendingPathComponent:@"bug.txt"];
}

Note that this defines a function which saves any uncaught exceptions to a file ‘bug.txt’ on your phone. If this file is present the next time your application starts up, the user is presented with the option to e-mail the bug to you.

Now to catching uncaught Objective C exceptions:

/*	UncaughtExceptionHandler
 *
 *		Handle uncaught exceptions
 */

static void UncaughtExceptionHandler(NSException *exception)
{
	/*
	 *	Extract the call stack
	 */
	
	NSArray *callStack = [exception callStackReturnAddresses];
	int i,len = [callStack count];
	void **frames = new void *[len];
	
	for (i = 0; i < len; ++i) {
		frames[i] = (void *)[[callStack objectAtIndex:i] unsignedIntegerValue];
	}
	char **symbols = backtrace_symbols(frames,len);
	
	/*
	 *	Now format into a message for sending to the user
	 */
	
	NSMutableString *buffer = [[NSMutableString alloc] initWithCapacity:4096];
	
	NSBundle *bundle = [NSBundle mainBundle];
	[buffer appendFormat:@"PComp version %@ build %@nn",
			[bundle objectForInfoDictionaryKey:@"CFBundleVersion"],
			[bundle objectForInfoDictionaryKey:@"CIMBuildNumber"]];
	[buffer appendString:@"Uncaught Exceptionn"];
	[buffer appendFormat:@"Exception Name: %@n",[exception name]];
	[buffer appendFormat:@"Exception Reason: %@n",[exception reason]];
	[buffer appendString:@"Stack trace:nn"];
	for (i = 0; i < len; ++i) {
		[buffer appendFormat:@"%4d - %sn",i,symbols[i]];
	}
	
	/*
	 *	Get the error file to write this to
	 */
	
	NSError *err;
	[buffer writeToFile:GetBugReport() atomically:YES encoding:NSUTF8StringEncoding error:&err];
	NSLog(@"Error %@",buffer);
	exit(0);
}

This exception handler is called when you have an uncaught Objective C excption. This formats a bug report (note that I use build numbers in the info.plist file, under the key ‘CIMBuildNumber’), and writes the bug report both to the phone’s log and to the log file before force quitting the application.

Next, the signal handler:

/*	SignalHandler
 *
 *		Handle uncaught signals
 */

void SignalHandler(int sig, siginfo_t *info, void *context) 
{
	void *frames[128];
	int i,len = backtrace(frames, 128);
	char **symbols = backtrace_symbols(frames,len);
	
	/*
	 *	Now format into a message for sending to the user
	 */
	
	NSMutableString *buffer = [[NSMutableString alloc] initWithCapacity:4096];
	
	NSBundle *bundle = [NSBundle mainBundle];
	[buffer appendFormat:@"PComp version %@ build %@nn",
			[bundle objectForInfoDictionaryKey:@"CFBundleVersion"],
			[bundle objectForInfoDictionaryKey:@"CIMBuildNumber"]];
	[buffer appendString:@"Uncaught Signaln"];
	[buffer appendFormat:@"si_signo    %dn",info->si_signo];
	[buffer appendFormat:@"si_code     %dn",info->si_code];
	[buffer appendFormat:@"si_value    %dn",info->si_value];
	[buffer appendFormat:@"si_errno    %dn",info->si_errno];
	[buffer appendFormat:@"si_addr     0x%08lXn",info->si_addr];
	[buffer appendFormat:@"si_status   %dn",info->si_status];
	[buffer appendString:@"Stack trace:nn"];
	for (i = 0; i < len; ++i) {
		[buffer appendFormat:@"%4d - %sn",i,symbols[i]];
	}
	
	/*
	 *	Get the error file to write this to
	 */
	
	NSError *err;
	[buffer writeToFile:GetBugReport() atomically:YES encoding:NSUTF8StringEncoding error:&err];
	NSLog(@"Error %@",buffer);
	exit(0);
}

This more or less does the same thing, except for uncaught C signals.

And now C++:

/*	TerminateHandler
 *
 *		C++ exception terminate handler
 */

void TerminateHandler(void)
{
	void *frames[128];
	int i,len = backtrace(frames, 128);
	char **symbols = backtrace_symbols(frames,len);
	
	/*
	 *	Now format into a message for sending to the user
	 */
	
	NSMutableString *buffer = [[NSMutableString alloc] initWithCapacity:4096];
	
	NSBundle *bundle = [NSBundle mainBundle];
	[buffer appendFormat:@"PComp version %@ build %@nn",
			[bundle objectForInfoDictionaryKey:@"CFBundleVersion"],
			[bundle objectForInfoDictionaryKey:@"CIMBuildNumber"]];
	[buffer appendString:@"Uncaught C++ Exceptionn"];
	[buffer appendString:@"Stack trace:nn"];
	for (i = 0; i < len; ++i) {
		[buffer appendFormat:@"%4d - %sn",i,symbols[i]];
	}
	
	/*
	 *	Get the error file to write this to
	 */
	
	NSError *err;
	[buffer writeToFile:GetBugReport() atomically:YES encoding:NSUTF8StringEncoding error:&err];
	NSLog(@"Error %@",buffer);
	exit(0);
}

Which is the same thing for C++.

Next are the routines for hooking into uncaught signals:

/*	SetupUncaughtSignals
 *
 *		Set up the uncaught signals
 */

static void SetupUncaughtSignals()
{
	struct sigaction mySigAction;
	mySigAction.sa_sigaction = SignalHandler;
	mySigAction.sa_flags = SA_SIGINFO;
	
	sigemptyset(&mySigAction.sa_mask);
	sigaction(SIGQUIT, &mySigAction, NULL);
	sigaction(SIGILL, &mySigAction, NULL);
	sigaction(SIGTRAP, &mySigAction, NULL);
	sigaction(SIGABRT, &mySigAction, NULL);
	sigaction(SIGEMT, &mySigAction, NULL);
	sigaction(SIGFPE, &mySigAction, NULL);
	sigaction(SIGBUS, &mySigAction, NULL);
	sigaction(SIGSEGV, &mySigAction, NULL);
	sigaction(SIGSYS, &mySigAction, NULL);
	sigaction(SIGPIPE, &mySigAction, NULL);
	sigaction(SIGALRM, &mySigAction, NULL);
	sigaction(SIGXCPU, &mySigAction, NULL);
	sigaction(SIGXFSZ, &mySigAction, NULL);
}

Once an exception has been caught, it’d be nice to send it to you via e-mail. To do this, define the following Objective-C methods in your app delegate:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 
{
	if (buttonIndex == 1) {
		/*
		 *	Send an e-mail with the specified title.
		 */
		
		NSMutableString *url = [NSMutableString stringWithCapacity:4096];
		[url appendString:@"mailto:bugs@nowhere.meh?subject=Bug%20Report&body="];
		[url appendString:[GBug stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
		[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
	}
	
	[GBug release];
}

- (void)sendBugsIfPresent
{
	NSError *err;
	NSString *path = GetBugReport();
	
	GBug = [[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err] retain];
	if (GBug == nil) return;
	[[NSFileManager defaultManager] removeItemAtPath:path error:&err];
	
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unexpected exception" 
			message:@"An unexpected exception was caught the last time this program ran. Send the developer a bug report by e-mail?" 
			delegate:self 
			cancelButtonTitle:@"Cancel" 
			otherButtonTitles:@"Send Report",nil];
	[alert show];
}

Remember to change the e-mail and the message that is displayed. This will display an alert to the user, and if the user selects “Send Report”, he’ll be dropped into the e-mail application to send the e-mail.

Of course you could do other things here, like upload via HTTP to some remote server. I like the e-mail approach; this way the user knows exactly what he’s telling you.

The -sendBugsIfPresent determines if a bug report has been written out, and if one is present, send it.

Now, once you’ve added this code, it’s simply a matter of wiring everything up to catch uncaught exceptions and alert the user if there was a crash the last time. To your -applicationDidFinishLaunching method add:

- (void)applicationDidFinishLaunching:(UIApplication *)application 
{
... Other initialization here ...

	/* Register for uncaught exceptions, signals */
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
	SetupUncaughtSignals();
	std::set_terminate(TerminateHandler);
	
	[self sendBugsIfPresent];
}

And that does it: we set up for uncaught Objective C exceptions, C++ exceptions and uncaught C signals, and we send a bug report if the last time we ran, we crashed.

Hope you find this as useful as I have.

Oh, and if you want to credit me with this, feel free–or not. (For the legally minded, I’m placing the above code snippets into the public domain. Just don’t sue me if it doesn’t work.)

An odd bug.

Found an odd bug tonight in an iPhone application I’ve been tinkering with.

The problem was that I had some code in a main UIViewController which was resizing the contents of it’s view. The code was being triggered by a button in another UIViewController which was placed in front of my main UIViewController through -presentModalViewController:animated:. And the code was crashing with a segment violation.

Some research later, and I figured out the problem: if you have a view controller representing a view hierarchy that is not currently on the screen (because it’s been kicked into the background by presentModalViewController), the view data structures can be unloaded from memory.

When this happens, calling [view bounds] on a view in your background view controller will crash, because the bounds is no longer present in memory.

Lesson: if a dialog or foreground view controller causes the background view controller to rearrange itself, you are better off detecting the change after the dialog or foreground view controller dismisses itself. In other words, only update views when they are visible.