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.
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
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;
}
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.
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