InQBarna, mobile multimedia customized solutions and services

InQBarna

"InQBarna, mobile multimedia customized solutions and services"

iOS developers: Error Handling with Cocoa Touch


man-typing

Easing the pain of Error Handling within Cocoa Touch

In many iOS applications and libraries we find error handling to be under-estimated and poorly used. Creating errors or accessing the information they contain, may sometimes feel a bit rough. And, unlike in Cocoa, the lack of ready-to-use solutions makes error presention awkward.

NSError objects are a great tool for dealing with runtime errors and usually contain valuable information, so just ignoring them will never be a clever decission.

What follows here, are our 3 recipies to make the use of NSError easier and to get the most of it.

Define getters for your most used user info values

If your code makes heavy use of any of the user info values from NSError, there is no point in keep seeking these values using objectForKey:. Defining a category on NSError with some getters for these values, which are highly used, will make using error objects more pleasent:

@interface NSError (Getters)

// Getter for 'NSURLErrorFailingURLErrorKey' user info value.

@property (nonatomic, readonly) NSURL *failingURL;

// Getter for 'NSUnderlyingErrorKey' user info value.

@property (nonatomic, readonly) NSError *underlyingError;

[...]

@end

Create your own error objects using the Builder Pattern

Most Java programmers, among others, are aware of this pattern because of the StringBuilder class (and its thread-safe equivalent, StringBuffer). But Builder pattern is useful for much more than just building strings.

Builder pattern is a design pattern meant for creating complex objects step by step. The Builder returns the product object as a final step.

Given that the logic of the instantation process is isolated from the actual creation steps, you can use different Concrete Builders for creating different kinds of product objects with the same set of simple operations.

When developing with Cocoa or Cocoa Touch, the most common method for composing strings is using an NSMutableString or an NSMutableArray. See example below:

NSMutableArray *array = [NSMutableArray array];

while (source.hasNext) {

[array addObject:source.next];

}

NSString *string = [array componentsJoinedByString:@", "];

The product object is built up with every object added to the array, but the actual string is not created until [NSArray componentsJoinedWithString:] is invoked.

The process for creating an NSError instance is similar. First, you create the user info dictionary and then put it together with the error code and domain in an init method:

NSDictionary *dict;

if (error) {

dict = @{ NSUnderlyingErrorKey: error

, NSLocalizedDescriptionKey: NSLocalizedString(@"Example error", nil) };

} else {

dict = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Example error", nil) };

}

NSError *newError = [NSError errorWithDomain:ExampleErrorDomain

code:kExampleErrorCode

userInfo:dict];

This process is good for creating small error objects. However given that NSErrorinstances are immutable, this method forces you to gather all necessary information before the creation of the error object and, having done that, you still have to watch for nil values if you don't want to crash when attempting to insert them in the user info dictionary.

Another problem is adding new values to an existing error. As you cannot expect all properties of the error to be present in the user info, you will need to copy them yourself when creating the dictionary for the user info with your new values.

These two problems could be solved by implementing an Error Builder. You only have to do it once and you can reuse it again and again. See how the above example would look when using an Error Builder object:

ErrorBuilder *builder = [ErrorBuilder builderWithDomain:errorWithDomain:ExampleErrorDomain

code:kExampleErrorCode

userInfo:nil];

builder.localizedDescription = NSLocalizedString(@"Example error", nil);

builder.underlyingError = error;

NSError *newError = builder.error;

Note that using the Error Builder you no longer have the need to check for nilvalues, as the builder can handle them by itself:

- (void)setUnderlyingError:(NSError *)underlyingError {

[self setUserInfoValue:underlyingError forKey:NSUnderlyingErrorKey];

}

- (void)setUserInfoValue:(id)value forKey:(NSString *)key {

if (value) {

[_userInfo setObject:value forKey:key];

} else {

[_userInfo removeObjectForKey:key];

}

}

The Error Builder could also have an init method that takes an NSError object as parameter, that could be used as a base for building another error object.

ErrorBuilder *builder = [ErrorBuilder builderWithError:error];

builder.localizedFailureReason = NSLocalizedString(@"The operation is not supported", nil);

NSError *newError = builder.error;

Automamate error presentation and recovery

Some runtime errors, may require some kind of user interaction before the application can continue with its normal operation. Others may not, but if the application cannot continue with a requested task, the user should be notified.

When presenting errors in iOS, you cannot rely on the same presentation and recovery architectures available in Mac. But with some tunning UIAlertView can do the job.

@interface UIAlertView (Error)

// Convenience method for initializing an alert view with data from an error object.

+ (UIAlertView *)alertWithError:(NSError *)error;

@end

Defining a category like the one from above, gives your application a fast and standarized method for showing information and getting feedback from the user.

Note that we haven't declared any delegate, because NSError can respond to the user actions with its recovery attempter (stored under user info's NSRecoveryAttempterErrorKey).

As in the example shown below, the error object may also contain a helpAnchor for giving extra information to the user:

 alert error handling

Following these few recipies will make using NSError objects a rewarding experience, that will save you time and let you achieve more with less coding.

Here you can download the code for what has been discussed in this text.

Contact us

info@inqbarna.com

twitter facebook linkedin

Phone: (+34) 93 356 87 33

InQBarna
Fundaci├│n Palo Alto 
C/ Pellaires 30-38, A1
08019 - Barcelona