Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct Structure to check for Errors using NSError

I'm coding up various routines and I'm trying my best to keep it neat and refactored.

Methods I'm creating are starting to look similar to this code:

-(IBAction)buttonPress:(id)sender {
    // Create Document Shopping List with this document
    [self doSomething:&error];

    if(error) {
        [NSApp presentError:&error];
        return nil;
    }

    [self doSomethingElse:&error];

    if(error) {
        [NSApp presentError:&error];
        return nil;
    }

    [self doYetSomethingElse:&error];

    if(error) {
        [NSApp presentError:&error];
        return nil;
    }
}

I love NSError, but this seems like an awfully clumsy way of handling all my errors.

A few thoughts I've had about alternative ways:

a) the error checking could be built into the methods doSomething, doSomethingElse etc, but then I wouldn't be able to exit the button press method without doing some kind of checking on the return value, which would lead me back to a similar structure.

b) I could set up the NSError as being Key Value Observed, but something about this feels deeply wrong. I'm very aware of the possibilities of abuse with KVO, so I'm trying to do everything without it whereever possible.

Surely I'm missing something really basic here? Is there a pattern that can help me? Or is this structure OK?

like image 658
John Gallagher Avatar asked Jun 26 '09 14:06

John Gallagher


People also ask

How do you convert error to NSError?

It should be possible to turn an arbitrary Swift enum that conforms to Error into an NSError by using the qualified type name as the domain key, the enumerator as the error code, and turning the payload into user data.

Which of the following is used when creating a NSError object?

The core attributes of an NSError object are an error domain (represented by a string), a domain-specific error code and a user info dictionary containing application specific information.

What is NSError code?

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 .

What is NSError domain?

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.


2 Answers

I think that none of the answers presented here are taking into account Apple's recommended practices.

Basically you should never check for the NSError object to determine if there was a problem. You check if there was a problem by checking the value returned from the method that takes a pointer to a pointer to an NSError.

From the Apple documentation:

Important Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.

like image 121
Ivan Sanchez Avatar answered Oct 11 '22 17:10

Ivan Sanchez


The gist of your question is whether there are structural improvements you can make to your error handling. I think so, by essentially introducing more layers of nesting, either by extracting more code into separate methods/functions, or by introducing nesting in your high level sample method.

The idea is, when it comes to handling most errors, you probably are either interested in performing an alternate task, or in failing and propagating the error up the chain so that some responsible controller can convey the error to the user through UI.

Using this idea of "propagate or handle", I would rewrite your sample method like this:

-(IBAction)buttonPress:(id)sender {

    // Create Document Shopping List with this document
    [self doSomething:&error];    
    if(error == nil) {
        [self doSomethingElse:&error];
        if (error == nil) {
            [self doYetSomethingElse:&error];
        }
    }

    if(error) {
        [NSApp presentError:&error];
    }    
}

Note that there are good arguments against introducing too much nesting in a particular method. Nesting such as this is essentially a short alternative to extracting methods. It might make more sense, for instance, that "doSomething:" itself calls doSomethingElse:, which calls doYetSomethingElse: for instance. This would impose the same structure on the code as the if-nest, but would be arguably more maintainable.

As an aside, I am not a fan of inline return statements. In this particular instance, the sample method doesn't actually call for a return value, but if it did, I prefer to set a local variable to the returned value and only return at the end of the flow control. Jumping out of a function or method prematurely is a sure way to encounter weird bugs, IMHO.

like image 21
danielpunkass Avatar answered Oct 11 '22 16:10

danielpunkass