Channels ▼
RSS

Design

Handling Errors in iOS and OS X with Cocoa


In Figure 6 are the two methods declared by the protocol. The first method attemptRecovery:optionIndex: handles recovery from an application-wide error. The second method attemptRecovery:optionIndex:delegate:... handles document-specific errors. Both methods get the same two input arguments: the NSError object, and the ID of the clicked dialog button. The method attemptRecovery:optionIndex:delegate:... gets three more inputs: the delegate object assigned to run after recovery, a method from the delegate, and a generic pointer to that method's arguments block.


Figure 6: Two methods from the recovery attempt protocol.

Listing Seven shows the basic structure of a simple recovery class. This sample class, FooRecover, declares two recovery IDs, one for each dialog button, grouping them into the enum constant FooRecoveryType (lines 4-8). Then it implements the protocol method attemptRecoveryFromError:optionIndex:. The method starts by identifying the error object (line 28). If the error is NSFileReadUnknownError, FooRecover invokes the right recovery task with a switch block (lines 33-48). If the error is NSUserCancelledError, FooRecover uses another set of tasks in a second switch block(lines 51-66). FooRecover returns a YES to signal a successful recovery. For all other errors, it returns a NO (line 69).

Listing Seven

// -- FooRecover.h
#import <Cocoa/Cocoa.h>

enum FooRecoveryType;
{
	FooRecoverRedo,
	FooRecoverReset
};

@interface FooRecover : NSObject
{
	// RESERVED...
}
// RESERVED...
@end

// -- FooRecover.m
#import "FooRecover.h"

@implementation FooRecover
// Recover from an application-level error
- (BOOL)attemptRecoveryFromError:(NSError *)anErr 
	optionIndex:(NSUInteger)anIdx
{
	NSInteger *eID;
	
	// identify the error
	eID = [anErr code];
	switch (eID)
	{
		case NSFileReadUnknownError:
			// identify the chosen recovery task
			switch (anIdx) 
			{
				case FooRecoverRedo:
					// -- error:recovery:task:redo
					// repeat the affected task
					// ...
					break;
				case FooRecoverReset:
					// -- error:recovery:task:defaults
					// reset the affected task
					// ...
					break;
				default:
					// -- RESERVED
					break;
			}
		case NSUserCancelledError:
			// identify the chosen recovery task
			switch (anIdx) 
			{
				case FooRecoverRedo:
					// -- error:recovery:task:redo
					// repeat the affected task
					// ...
					break;
				case FooRecoverReset:
					// -- error:recovery:task:reset
					// reset the affected task
					// ...
					break;
				default:
					// -- RESERVED
					break;
			}
		default:
			// -- unsupported error
			return (NO);
	}
	
	// return the recovery result
	return (YES);
}
@end

Listing Eight shows how to add a recovery object to the NSError instance. After the snippet defines the localized strings (lines 16-22), it creates two NSString objects for the button labels (lines 25-29). It stores the string objects into an NSArray object (eTask) and stores the array under the key NSLocalizedRecoveryOptionsErrorKey (lines 31-32). Next, the snippet creates an instance of FooRecover (line 34). It stores that recovery object under the key NSRecoveryAttempterErrorKey (lines 35-36).

The error object is then dispatched into the responder chain as described earlier.

Listing Eight

// -- FooRecover.h
#import <Cocoa/Cocoa.h>

enum FooRecoveryType;
{
	FooRecoverRedo,
	FooRecoverReset
};

@interface FooRecover : NSObject
{
	// RESERVED...
}
// RESERVED...
@end

// -- FooRecover.m
#import "FooRecover.h"

@implementation FooRecover
// Recover from an application-level error
- (BOOL)attemptRecoveryFromError:(NSError *)anErr 
	optionIndex:(NSUInteger)anIdx
{
	NSInteger *eID;
	
	// identify the error
	eID = [anErr code];
	switch (eID)
	{
		case NSFileReadUnknownError:
			// identify the chosen recovery task
			switch (anIdx) 
			{
				case FooRecoverRedo:
					// -- error:recovery:task:redo
					// repeat the affected task
					// ...
					break;
				case FooRecoverReset:
					// -- error:recovery:task:defaults
					// reset the affected task
					// ...
					break;
				default:
					// -- RESERVED
					break;
			}
		case NSUserCancelledError:
			// identify the chosen recovery task
			switch (anIdx) 
			{
				case FooRecoverRedo:
					// -- error:recovery:task:redo
					// repeat the affected task
					// ...
					break;
				case FooRecoverReset:
					// -- error:recovery:task:reset
					// reset the affected task
					// ...
					break;
				default:
					// -- RESERVED
					break;
			}
		default:
			// -- unsupported error
			return (NO);
	}
	
	// return the recovery result
	return (YES);
}
@end

Conclusion

Writing error-tolerant code helps improve the overall user experience of software applications. It can keep user data intact and secure, and it can ensure a stable and reliable product.

The Cocoa framework provides two classes for writing error-tolerant code for an OS X or iOS application. With the NSError class, we can encapsulate the error and any data describing the error. With the NSResponder class, we can dispatch the error object and respond to it from key points in the application.

In this article, we discovered how to create an NSError object and how to supply relevant data through a support dictionary. We learned how a Cocoa application prepares its response chain, and how the error object travels in that chain. We learned how to dispatch an error object, as well as how to respond to it. Finally, we reviewed how to prepare and attach a recovery object for the error.

Further Reading

Error Handling Programming Guide [PDF]. iOS Developer Library.

NSError Class Reference [PDF]. iOS Developer Library.

NSErrorRecoveryAttempting Protocol Reference [PDF]. iOS Developer Library.

Marcus Zara. Using NSError To Great Effect. Cocoa is my Girlfriend. Zarra Studio.


José Cruz is a freelance engineering writer based in British Columbia. He frequently contributes articles to MacTech, REALStudio Developer, and Dr. Dobb's.


Related Article

Understanding Core Data on iOS


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video