Debugging iPhone Apps

October 27, 2010

This is pretty basic, but I didn’t find this info all in one place, so I figured I’d post it here.

If your iPhone app is crashing, here’s a series of steps you can take to troubleshoot it:

  1. Check your build warnings. Just because there were no errors that prevented execution, doesn’t mean your app won’t crash. One common warning I often have to fix is mistyped method names. Objective-C will compile your code if you call methods on an object that don’t exist, but it will throw an exception at runtime.
  2. Check your Xcode console. Most crashes will show up here.
  3. Check your Mac OS X console. This is found under Applications/Utilities/Console. Sometimes a crash won’t output data in the Xcode console for some reason, but it will output it here.
  4. Check your Diagnostic Reports folder. This is found under /Users/youruser/Library/Logs/DiagnosticReports. When your app crashes in the simulator, it should create a new crash file here. Double-clicking one will open it in the Console app, and it should have info on what line number the crash happened on.
  5. Check your CrashReporter folder. This is for if crashes happen on your device when it’s not hooked up to the debugger. The next time you sync, the crash logs will be copied to /Users/youruser/Library/Logs/CrashReporter/MobileDevice/Your Device Name. You don’t want to bother opening these directly in the console, though–it doesn’t include method names and line numbers. Instead, you want to use a command-line utility called symbolicatecrash to translate the crash log into a version with method names and line numbers. Konstantin Anoshkin’s blog describes the process well–the only change I had to make was that in Xcode 3.2 I now find symbolicatecrash at /Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash

A Reusable Cocoa REST Client

October 27, 2010

It wasn’t too difficult to build a REST client in Cocoa for my iPhone app, but there was enough to it that I wanted to wrap it in a service, so I could make a REST call in one line. And so I could reuse it and pass it along to you!

Download a zip file containing the header and class file, or see the code below.

Incidentally, if you’re wondering how I create/consume the data, I use json-framework. It’s very easy to integrate into your project, and converts JSON objects to/from NSMutableDictionaries / NSMutableArrays.

Usage

Note: I haven’t tested this exact usage code, so you may need to tweak it.

RestService *svc = [[RestService alloc] init];
NSHTTPURLResponse *response = nil;

// simplest request: GET, no headers
NSString *result = [svc requestToUrl:@"http://..."
                            response:&response];
NSLog(@"status code: %d", response.statusCode);
NSLog(@"content: %@", result);

// most complex request: all parameters
NSDictionary headers = [NSDictionary dictionaryWithObjectsAndKeys:
                           @"application/json", @"Content-Type"
                           @"Basic ...", @"Authorization",
                           nil];
NSString *requestBody = @"some xml or json";
NSString *result = [svc requestToUrl:@"http://..."
                              method:METHOD_POST
                                body:requestBody
                             headers:headers
                            response:&response];
NSLog(@"status code: %d", response.statusCode);
NSLog(@"content: %@", result);

RestService.h

//
//  RestService.h
//  TypeLinkNav
//
//  Created by Josh Justice on 10/21/10.
//  Copyright 2010 Josh Justice. All rights reserved.
//

#import 

#define METHOD_GET @"GET"
#define METHOD_PUT @"PUT"
#define METHOD_POST @"POST"
#define METHOD_DELETE @"DELETE"

@interface RestService : NSObject {

}

-(NSString *)requestToURL:(NSString *)url
				 response:(NSHTTPURLResponse **)response;

-(NSString *)requestToURL:(NSString *)url
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response;

-(NSString *)requestToURL:(NSString *)url
				   method:(NSString *)method
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response;

-(NSString *)requestToURL:(NSString *)url
				   method:(NSString *)method
					 body:(NSString *)body
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response;

@end

RestService.m

//
//  RestService.m
//  TypeLinkNav
//
//  Created by Josh Justice on 10/21/10.
//  Copyright 2010 Josh Justice. All rights reserved.
//

#import "RestService.h"


@implementation RestService

-(NSString *)requestToURL:(NSString *)url
				 response:(NSHTTPURLResponse **)response
{
	return [self requestToURL:url
					  headers:nil
					 response:response];
}

-(NSString *)requestToURL:(NSString *)url
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response
{
	return [self requestToURL:url
					   method:METHOD_GET
					  headers:headers
					 response:response];
}

-(NSString *)requestToURL:(NSString *)url
				   method:(NSString *)method
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response
{
	return [self requestToURL:url
					   method:method
						 body:nil
					  headers:headers
					 response:response];
}

-(NSString *)requestToURL:(NSString *)urlString
				   method:(NSString *)method
					 body:(NSString *)body
				  headers:(NSDictionary *)headers
				 response:(NSHTTPURLResponse **)response
{
	// create the url
	NSURL *url = [NSURL URLWithString:urlString];
	
	NSLog(@"requesting %@, %@",method,url);
	NSLog(@"request body: %@", body);
	
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
	
	// set up the request
	if( nil != method ) {
		[request setHTTPMethod:method];
	}
	if( nil != body ) {
		NSData *bodyData = [body dataUsingEncoding:NSASCIIStringEncoding];
		[request setHTTPBody:bodyData];
	}
	if( nil != headers ) {
		NSEnumerator *e = [headers keyEnumerator];
		NSString *headerName;
		while( headerName = [e nextObject] ) {
			[request setValue:[headers objectForKey:headerName]
		   forHTTPHeaderField:headerName];
		}
	}
	
	// get the response
	NSHTTPURLResponse *myResponse = NULL;
	NSError *error = NULL;
	NSData *resultData = [NSURLConnection sendSynchronousRequest:request
										 returningResponse:&myResponse
													 error:&error];
	// pass response object back to user by reference
	*response = myResponse;
	
	// convert response body to string
	NSString *resultString = [[NSString alloc] initWithData:resultData
												   encoding:NSASCIIStringEncoding];
	NSLog(@"response body: %@", resultString);
	return resultString;
}

@end


Delicious-Style URLs in Grails, or, Not-Matching Regexes

October 27, 2010

I’m building a Grails app where I want to use delicious-style URLs. The domain name is going to be typelink.net, so to get to user jjustice’s public page on it, you would go to http://typelink.net/jjustice. Grails makes it very easy to set up RESTful URLs like that:

"/$userName"{
	controller = "myController"
	action = "listByUser"
}

But one problem is that if I set it to map /anything at the root level of the app to a certain controller, then I can’t get to any other controllers or assets, because it thinks everything is a request for a user.

Conflicting with other controllers, like /login for a login page, wasn’t too big a deal to fix, because I could just create a UrlMapping for /login that was a higher precedence than my username mapping. The bigger problem was my CSS, JS, or images directories that are also at the root level–the app will think I’m trying to get to a user named “images.” And I can’t set up a URL mapping for them that I know of, because there’s no way to say “map this to just returning static assets”–mappings always send you to a controller.

The solution I found was to use a “matches” constraint on the URL mapping, to only match when the username isn’t one of my static asset directories or other controllers. But finding a regex for this was challenging, because there’s no doesntMatch constraint – only a positive matches constraint.

Turns out the regex (at least in Perl and Java) to not-match a pattern in a string is:


/^((?!pattern).)*$/

?! is a lookahead zero-width assertion, which only matches if the string following it does not match this pattern. So this overall pattern means that pattern must not match at any point in the string.

That wasn’t quite enough for me, though. I didn’t want to match “css,” but it was okay if a user’s name was “fredlikescss”. So all I wanted was to make sure the whole string did not match the pattern. To do that, I did:


/^((?!^pattern$).)*$/

In other words, at every point in the string, make sure that “pattern” doesn’t match the entire string.

Finally, I needed to add several patterns, because I wanted to make sure it wasn’t css, js, or images, and you can only specify one matches constraint for a URL mapping. To do that:


/^((?!^pattern1$)(?!^pattern2$)(?!^pattern3$).)*$/

So, in my case, the specific pattern was:


/^((?!^css$)(?!^js$)(?!^images$)(?!^session$)(?!^page$)(?!^share$).)*$/

Where css, js, and images are my static asset directores, and session, page, and share are my other controllers that I don’t want to map to usernames.

So my whole URL mapping for the usernames ended up being (copy-paste to see the whole string)

"/$userName"{
	controller = "myController"
	action = "listByUser"
	constraints {
		userName(matches:/^((?!^css$)(?!^js$)(?!^images$)(?!^session$)(?!^page$)(?!^share$).)*$/)
	}
}


Stupid Cocoa Bugs: Bad Access

October 27, 2010

That is to say, bugs in my Cocoa code that are stupid–not stupid bugs in Cocoa itself =]

After updating something trivial in my iPhone app, I started getting EXC_BAD_ACCESS crashes on the device–but only when it wasn’t hooked up to the debugger. I found out that the error happened in the dealloc method of one of my UIViewControllers, on a line where I release one of the object’s property objects. I checked all my code and it seemed like I was following standard memory management rules.

The solution ended up being dumb: at some point I had gotten it into my head that I was supposed to call the superclass’s dealloc method, then release the properties:

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

But, when I looked in the Memory Management Programming Guide, it seems that you’re supposed to release your properties first:

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

This makes sense, assuming that at some point up the superclass chain the object itself gets released or free’d.

The crazy thing was that I’d used the wrong order for the app for over a week without any crashes. I guess this is part of what a JVM guy like me needs to learn about memory management: the problems don’t always show up reliably or right away.


Different CSS and JS for PC, iPad, and iPhone

October 17, 2010

I’m working on a webapp that will run on PCs, iPad, and iPhone. To get the optimum experience for each, I have some styles and JavaScript that I want to run on each, some that are only for iPad, some only iPhone, and some for a combination of the above. Here’s the patterns I’m using to implement this.

JavaScript is easy: I just use regular user agent detection to determine which platform we’re on.

function iOS() { return null != navigator.userAgent.match(/(iPad|iPhone)/i); }
function iPad() { return null != navigator.userAgent.match(/(iPad)/i); }
function iPhone() { return null != navigator.userAgent.match(/(iPhone)/i); }

CSS is a bit more complicated, but not much. I’ve found recommendations for how to target CSS specifically to iPad and iPhone. But really the iPad one is for all iOS devices, so I end up with an iOS CSS file and an iPhone CSS file:


<link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 1024px)" href="iOS.css" />
<link rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" href="iPhone.css" />

So I put my PC styles in my standard stylesheet. Then, I fill iOS.css with styles that I want on all iOS devices or only on iPad. Then I fill iPhone.css with styles I want only on iPhone; and, if there are any styles in iOS.css that were for iPad only, I overwrite them in iPhone.css. For this to work, I have to list iOS.css before iPhone.css, as I did above.


Grails replaceAll With Captured Groups/Backreferences

October 13, 2010

In a Grails app, I found myself needing to do a regex search, save captured groups/backreferences (I’m not sure of the distinction between those terms), and replace them with new content, which includes those backreferences. But I was having trouble finding a way to do it.

I tried using $1 or \1, but whenever I did that, I got an error that it was an invalid captured group or backreference.

The solution I found was the following.


content = content.replaceAll( /(a*b)/,
{ full, word -> "${word}" } )

This takes every example of a*b in the string content (like ab, aaab, etc.), and puts an anchor tag around it.


Installing Java 6 JDK on Ubuntu

October 3, 2010

None of the posts I found on this worked quite right, so I’m writing up what I did here for future reference.


sudo apt-get update
sudo apt-cache search java6

When I did this, the results I got included:


default-jdk
default-jre
default-jre-headless
openjdk-6-jdk
openjdk-6-jre
openjdk-6-jre-headless

If I’m interpreting it right, that means the Sun JDK is not available for installation via apt-get, at least the way I have it configured. But people said that the OpenJDK works fine too. So I decided to install it. It seems that specifying “default-jdk” just installs “openjdk-6-jdk” anyway:


sudo apt-get install default-jdk

When this completed, /usr/bin/java was set up for me. But I did have to add a JAVA_HOME environment variable to my .bashrc file:


export JAVA_HOME="/usr/lib/jvm/java-6-openjdk/jre"

After that, I was set to go.