In Cocoa and Objective C the favorite method for managing error seems to be using an NSError *
object, to construct an error object however, we need to call the following method
+ (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict
My question is, what are some of the best practices for managing error domain, error code definitions and user info dictionary across the entire application so that error code, domain and user info dict always stays consistent?
In Objective-C programming, error handling is provided with NSError class available in Foundation framework. An NSError object encapsulates richer and more extensible error information than is possible using only an error code or error string.
Like exit status codes, an NSError -code signals the nature of the problem. These status codes are defined within a particular error domain , in order to avoid overlap and confusion. These status codes are generally defined by constants in an enum .
The core attributes of an NSError object—or, simply, an error object—are an error domain, a domain-specific error code, and a “user info” dictionary containing objects related to the error, most significantly description and recovery strings.
If you have a hefty amount of error construction, your life could be much simpler by using a class. I actually use C++ for this so the calls a program does not need may be removed (unlike objc), but you can use C, ObjC, or C++ for this:
MONErrorDomain.h
// you won't normally need an instance here
@interface MONErrorDomain : NSObject
+ (NSString *)domain; // << required override
- (NSString *)domain; // << returns [[self class] domain]
// example convenience methods:
// uses [self domain]
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode; // << user info would be nil
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode userInfo:(NSDictionary *)userInfo;
@end
MONKoalaError.h
@interface MONKoalaError : MONErrorDomain
+ (NSError *)outOfEucalyptus;
@end
extern NSString * const MONKoalaErrorDomain;
typedef enum MONKoalaErrorCode {
MONKoalaErrorCode_Undefined = 0,
MONKoalaErrorCode_OutOfEucalyptus
} MONKoalaErrorCode;
MONKoalaError.m
// apple recommends we use reverse domains
NSString * const MONKoalaErrorDomain = @"com.mon.koala-library.MONKoalaErrorDomain";
@implementation MONKoalaError
+ (NSString *)domain
{
return MONKoalaErrorDomain;
}
+ (NSError *)outOfEucalyptus
{
NSDictionary * info = …;
return [self errorWithErrorCode:MONKoalaErrorCode_OutOfEucalyptus userInfo:info];
}
@end
Then the error creation is all in one place for each domain, and the clients can easily pick their errors without actually building them manually:
if (outError) {
*outError = [MONKoalaError outOfEucalyptus];
}
and error handling takes the form:
if ([e.domain isEqualToString:MONKoalaErrorDomain]) {
switch (e.code) {
case MONKoalaErrorCode_OutOfEucalyptus : {
self.needsEucalyptus = true;
…
One common way is to define some appropriate constants in a header file, and then include that header file wherever needed. It's a pretty simple approach, and looks like:
const NSString * kMyAppErrorDomain = @"com.example.myapp";
const NSInteger kMyAppSomeError = 2;
// key into user info dictionary
const NSString * kMyAppProblemKey = @"MyAppProblemKey";
I've also seen some applications which create convenience methods for creating these, either as a category on NSError
or as a separate utility class or set of functions. It's also entirely reasonable to subclass NSError
, for example to customize the localized description.
If you have not already seen it, Apple has released the Error Handling Programming Guide which discusses how these should be used in Cocoa.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With