Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the correct use of NSErrorRecoveryAttempting, NSError, and UIAlertView in iOS?

I'm having trouble finding examples of the correct way to use NSError, UIAlertView, and NSErrorRecoveryAttempting together on iOS. Most of the documentation and examples I can find cover the equivalent functionality on OS X, where the relevant behaviors are integrated by Cocoa. But in iOS it seems to be necessary do do this "by hand", and I can't find good examples of how it's done.

I'd very much appreciate a few examples of best practice in using information in NSError to support recovery attempts from NSErrors reported to the user.

like image 274
orome Avatar asked May 11 '11 01:05

orome


3 Answers

I'm trying to mirror AppKit's error handling mechanism in UIKit, mainly because I want to take advantage of the responder chain to forward errors upwards. I haven't tested this fully, but at the moment it's looking like below.

It reflects AppKit pretty closely, but the will/did hooks can be overridden to perform custom error presentation and recovery respectively. The default behaviour is to show a UIAlertView for presentation and use a psuedo-NSErrorRecoveryAttempting object for recovery.

@implementation UIResponder (ErrorHandling)

- (void)presentError:(NSError *)error
        completion:(void (^)(BOOL recovered))completion
{
    if (nil == (error = [self willPresentError:error])) {
        return;
    }
    if (self.nextResponder) {
        [self.nextResponder presentError:error completion:completion];
        return;
    }

    // Code to create and show UIAlertView
    // e.g. https://github.com/jayway/CWUIKit/blob/master/Classes/UIAlertView%2BCWErrorHandler.m

    // The UIAlertViewDelegate calls didPresentError...
}

/*
 Override to customise the error object as in AppKit.
 You can also perform your own error presentation, and return nil to terminate the default handling.
 Custom error presentation UI should still call didPresentError... when dismissed
 */
- (NSError *)willPresentError:(NSError *)error
{
    return error;
}

/*
 Override to perform custom error recovery.
 */
- (void)didPresentError:(NSError *)error optionIndex:(NSInteger)optionIndex completion:(void (^)(BOOL recovered))completion
{
    id recoveryAttempter = [error recoveryAttempter];
    if ([recoveryAttempter respondsToSelector:@selector(attemptRecoveryFromError:optionIndex:completion:)]) {
        [recoveryAttempter attemptRecoveryFromError:error optionIndex:optionIndex completion:completion];
    }
}

@end
like image 92
jamesmoschou Avatar answered Nov 15 '22 22:11

jamesmoschou


According to Apple's documentation:

Important: The NSError class is available on both Mac OS X and iOS. However, the error-responder and error-recovery APIs and mechanisms are available only in the Application Kit (Mac OS X).

So, I'm not sure if you can use NSErrorRecoveryAttempting even though it does appear to be defined in the documentation (it looks like this is an area of the UIKit docs that have not yet been updated after being copied from AppKit's documentation).

Here is how I handle errors in my code:

NSError *error = nil;
id result = [SomeClass doSomething:&error];

if (!result) {
    NSLog(@"Do something failed: %@", error);
    UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Something failed!" message:@"There was an error doing something." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
    [alert show];
    return;
}
like image 23
Abhi Beckert Avatar answered Nov 15 '22 22:11

Abhi Beckert


I found a great example of this.

See the following blog post and GitHub code (including sample project) by James Beith

http://www.realmacsoftware.com/blog/cocoa-error-handling-and-recovery

https://github.com/realmacsoftware/RMErrorRecoveryAttempter

I was able to successfully use this on the iPhone simulator.

like image 35
Scott Carter Avatar answered Nov 15 '22 22:11

Scott Carter