4
Apr
2008
 

Cocoa Tutorial: Using NSError to Great Effect

by Marcus Zarra

Error handling is rarely fun. I find myself often re-coding a method after I realize that I need to handle one error condition or another. Usually, error handling involves either try/catch or some return code strategy. Neither of those is pretty or easy to maintain. For Objective-C development, however, there is another option — NSError.

Try/Catch

I truly dislike using try/catch. It indents my code all to hell, is difficult to follow and even more difficult to debug. Anyone who has written Java code for any length of time knows the true hell that is try/catch. Therefore I try to avoid this method of error handling if at all possible. I find that it interrupts the logical flow of the code and is just plain rude.

Return Codes

This is probably the more common method of handling errors. Return an integer and let the calling code decide how to handle it. This is definitely better than the try/catch solution as it does not indent the code badly and does not interrupt the logic flow. However, I find that return codes are harder to maintain than they should be. What was 1? What about 2? And 12342 was what again?!?

And worse, what happens when everything goes right and I need to return an object? Oops…

Introduction to NSError

Fortunately, there is a better solution. NSError allows me to return whatever I want, handles errors without breaking the logic flow, and has quite a few other features. First some code to demonstrate how Apple uses NSError:

1
2
3
4
5
6
7
NSError *error = nil;
BOOL success = [myContext save:&error];
if (!success) {
  [NSApp presentError:error];
  return;
}
//Continue processing

In this simple example, I am calling save on my NSManagedObjectContext and I am passing in an NSError pointer. If there is an issue, then that pointer will reference an NSError object that I can then utilize however I need. In this case I am simply passing it off to the NSApp to be displayed to the user. However, I could just as easily have done this:

1
2
3
4
if (!success) {
  NSLog(@"%@:%s Error saving context: %@", [self class], _cmd, [error localizedDescription]);
  return;
}

I suggest reviewing Apple’s Documentation on NSError to discover all of the information that is available.

Passing Pointers to Pointers

Using the NSError class in the above example is quite simple. I just pass it in and if the call failed for whatever reason I have an NSError object to explain what the problem was. But how does the other side of that call work?

The first part of this is to understand the concept of passing a pointer to a pointer rather than a pointer to an object. Normally when a message is sent, a pointer to the object is being passed in the message. This is denoted with the single asterisk(*) in the method signature such as

-(void)appendString:(NSString*)newString

This is different then passing a pointer to a pointer. First, that type of argument is denoted with two asterisk(**) such as:

-(BOOL)save:(NSError**)error

Second, the other difference is this allows the receiving method to control what the pointer (that the pointer is pointing to) is referencing. This is arguably one of the more confusing parts of pointer programming but once you get it, it is a very powerful tool. To put it another way, by passing a pointer to the pointer, the receiving method can decide what your variable’s value is. As you can see in my code above, I initialized the error variable to nil. However, if the save call fails, that variable will no longer be nil but will reference an actual NSError object which I can than interrogate.

If you wish to understand how this works, I would suggest the double indirection wikipedia article as a great starting point.

Using NSError Handling in My Code

With a firm understanding of double indirection pointers </smile> I am now able to build my own methods that accept a pointer to an NSError object and I can utilize that pointer if anything goes wrong. Consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (id)fetchDocument:(NSURL*)url error:(NSError**)error
{
  NSString *myString = [[NSString alloc] initWithContentsOfURL:url];
 
  //start to do something wickedly cool with the data
  if (YES) {
    //An error occurred
    NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
    [errorDetail setValue:@"Failed to do something wicked" forKey:NSLocalizedDescriptionKey];
    *error = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];
    return nil;
  }
  //Finish doing something wicked
  return [[[NSObject alloc] init] autorelease];
}

In this example, I have passed in an NSURL. Using that NSURL object I have populated a string with it and the “do something wicked” with the contents of the NSString. However, there is an error in the processing and therefore I want to notify the calling code that it failed. To do this I first constructed an NSMutableDictionary that contains all of the information about the failure. In this example I only populated the localized description key but I could have put a lot more in there.

Once the dictionary is populated, I then initialized an NSError object using the helper method that gives me back an auto-released object. I then assigned that object back to the pointer that was passed in. The interesting piece of code here is how I am referencing the passed in pointer. To update the pointer that the passed in pointer is pointing to I need to include an asterisk before the name of the variable. This tells the compiler that I want to change what the pointer to pointer is pointing to. Yes — it is a bit confusing but the end result is that the NSError reference in the calling method will reference the NSError object that has been constructed in the called method and thus allowing me to pass information back to the calling method along a second path.

By utilizing an NSError in this manner, I am able to return anything I need when the method is successful and still able to send back all of the error information required when something goes wrong.

Displaying NSError Objects

Once you get an NSError object back from a method there are a lot of things that can be done with it. Since Apple uses them so often in their own code, a way to present them to the User has been included in the Cocoa APIs.

1
2
3
4
5
6
7
8
9
10
- (IBAction)thrower:(id)sender;
{
  NSError *error = nil;
  id result = [self fetchDocument:[NSURL URLWithString:@"http://www.apple.com"] error:&error];
  if (error) {
    [NSApp presentError:error];
    return;
  }
  NSLog(@"Result received: %@", result);
}

In this example, I am checking to see if the error is still nil after my message call. If it is no longer nil I know that an error occurred and that I need to display it to the user. Apple provides a built in method to do this with a call to presentError: on the NSApplication instance. If you run the example project included you will see that this presents a dialog window to the user with the localized description displayed.

Conclusion

This article will be followed up with another one detailing some of the more interesting things that you can do with NSError objects. Hopefully this taste will highlight the usefulness of this method of error handling and how it is an improvement over both try/catch blocks and return codes.

xcode.png
Download the Example Project

Marcus Zarra

Marcus S. Zarra is a founding partner of MartianCraft, LLC. He has been developing Cocoa software since 2003, Java software since 1996, and has been in the industry since 1985. Currently Marcus is producing software for iOS and OS X. In addition to writing software, he assists other developers by blogging about development and supplying code samples on Cocoa Is My Girlfriend. Marcus is also the author of Core Data (2nd edition): Data Storage and Management for iOS, OS X, and iCloud and Co-Author of Core Animation: Simplified Animation Techniques for Mac and iPhone Development. You can find Marcus on Twitter, on App.net and on StackOverflow.

More Posts - Website

Follow Me:
Twitter

Comments

tompa says:

Reading this code makes me laugh:

if (!error) { [NSApp presentError:error];

So, if not an error, then present the error.

Thinking about it again, I just want to cry.

/tompa

icy says:

Hi,

thanks a lot for this post. This is really some valuable information. But shouldn’t it be “if (error)” instead of “if (!error)” in the last listing?

Marcus Zarra says:

Icy,

Yes it should be if (error) and it is correct in the project. Thanks for catching the typo!

lodea says:

You’re completely wrong about exceptions and try/catch; when used correctly it is a far better mechanism than NSError, in general. However, for Cocoa, I agree that NSError is the best way to deal with errors. Exceptions are a huge pain when you don’t have garbage collection, which is new in Objective-C and still not available on the iPhone. In Cocoa, exceptions should only be used to indicate programming bugs, in which case you want your program to crash as fast as possible, and you don’t care about freeing memory properly. This is exactly what the Apple docs say on using NSError vs Exceptions.

Also, the Cocoa library support you get for NSError is very good. Every Cocoa app should use NSError.

One minor point – the convention seems to be that callers who don’t care about the details of any errors are allowed to pass NULL for the *NSError pointer. I never assign to *error without checking if error is NULL first. I guess this only matters if you’re writing code that might be called by someone else.

Another minor point – you’re checking if error was set to detect an error. But the examples I’ve seen do this by checking for a special value returned by the method, usually nil or NO. From memory this is what the Apple docs recommend. I can’t really see anything wrong with checking the error pointer, however.

Marcus Zarra says:

lodea,

An opinion, no matter what you may think of it, can never be wrong. I dislike try/catch blocks and consider them to be a bad design choice. You are welcome to have your own opinion.

As for my opinion — an application should never crash due to a bug that the developer is aware of. Therefore if there is an exception that can be caught — it should be caught. The only time an application should crash is when something happens totally outside of the developer’s expectations. If the code is throwing an exception then it is within the realm of expectation and therefore should be dealt with.

Realize that this is an example of using NSError. It is not meant to be perfect coding practice and that everyone must follow my lead. It demonstrates how to use the NSError object and the often complex subject of double indirection. There are many weaknesses in the code that if corrected would cloud the message.

As for your last point, not everything that Apple does is gold. Just because they do something does not make it right.

minofifa says:

Hi Marcus

I noticed in several of your NSError article examples, you give the status code number a seemingly random value. I’m fairly sure this is simply to keep the examples short and sweet. I was wondering if, in your production code, you have some header that you include with a bunch of these error codes defined? Is that the best approach?

Thanks for the great blog.

Marcus Zarra says:

Yes, I normally use #define statements in the Prefix.pch