10

This is from the Apple sample code:

if (![fetchedResultsController_ performFetch:&error]) {
     /*
      Replace this implementation with code to handle the error appropriately.
      ...
      If it is not possible to recover from the error, ...
      */
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

I am wondering whether it is really necessary to always terminate the app? How could you "replace this implementation with code to handle the error appropriately"? And how would you "recover from the error"?

Any suggestions would be appreciated, Fabian

fabian789
  • 8,348
  • 4
  • 45
  • 91
  • 2
    The `abort()` call is there as a scare tactic. – BoltClock Dec 29 '10 at 18:22
  • 1
    Anyway it would depend on what the fetched results controller is looking for. That means for different things in your app you'll want to handle the error differently. Your question on *what exactly to do* with that `NSError` object remains valid though, so +1 – BoltClock Dec 29 '10 at 18:23
  • I tend to get an alert up onto the screen showing the NSError's localizedDescription, so that the user has something to report to tech support. – Amy Worrall Dec 29 '10 at 20:12
  • 1
    i think `abort()` is also a no-no for app acceptance, an indication of an error and an empty array is my preference – bshirley Jul 01 '11 at 16:58

1 Answers1

7

Well, apparently nobody has another (better?) solution, so here is my approach:

In my AppController I added a instance variable errorString and this method:

- (void)presentCoreDataError:(NSError *)error
                    withText:(NSString *)text
{
    NSMutableString *localErrorString = [[NSMutableString alloc] init];

    [localErrorString appendFormat:@"Failed to %@: %@", text, [error localizedDescription]];

    NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    if(detailedErrors != nil && [detailedErrors count] > 0) {
        for(NSError* detailedError in detailedErrors) {
            [localErrorString appendFormat:@"- Detail: %@", [detailedError userInfo]];
        }
    } else {
        [localErrorString appendFormat:@"- %@", [error userInfo]];
    }

    UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"Failed to %@", text]
                                                     message:@"Please send a report to the developer."
                                                    delegate:self
                                           cancelButtonTitle:@"Cancel"
                                           otherButtonTitles:@"Send Report", nil] autorelease];
    [alert show];

    self.errorString = localErrorString;
    [localErrorString release];
}

The UIAlertView delegate displays an MFMailComposeViewController with the errorString in the cool courier font :) if "Send Report" is tapped. Otherwise it calls abort():

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {     // Send Report
        MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
        picker.mailComposeDelegate = self;
        NSArray *toRecipients = [NSArray arrayWithObject:@"first@example.com"];
        [picker setToRecipients:toRecipients];
        [picker setSubject:@"Error Report"];
        [picker setMessageBody:[NSString stringWithFormat:@"The application crashed with the following error:<br><br><FONT FACE=%@> %@ </FONT>", 
                                @"courier", errorString] 
                        isHTML:YES];

        [navigationController presentModalViewController:picker animated:YES];
        [picker release];
    } else {
        abort();
    }
}

And the MFMailComposeViewControllerDelegate displays a second UIAlertView with only one button (obviously the button has index 0, so it will call abort()):

- (void)mailComposeController:(MFMailComposeViewController *)controller
          didFinishWithResult:(MFMailComposeResult)result
                        error:(NSError *)error
{
    [navigationController dismissModalViewControllerAnimated:YES];

    NSMutableString *messageString = [[NSMutableString alloc] init];

    if (result == MFMailComposeResultSent) {
        [messageString appendFormat:@"Thanks! "];
    }

    [messageString appendFormat:@"The application has to quit now."];
    UIAlertView *abortAlert = [[[UIAlertView alloc] initWithTitle:nil
                                                          message:messageString
                                                         delegate:self
                                                cancelButtonTitle:@"OK"
                                                otherButtonTitles:nil] autorelease];

    [abortAlert show];

    [messageString release];
}
fabian789
  • 8,348
  • 4
  • 45
  • 91
  • Have you used this solution in production? Also, have you ever gotten any emails? – abellina Oct 24 '12 at 20:40
  • I have used it in production and I have gotten two e-mails. – fabian789 Oct 27 '12 at 10:02
  • 1
    @fabian789 Still helpful years later, thanks! You may also want to use `[MFMailComposeViewController canSendMail]` to determine if the device is configured for sending email (and provide a fallback alert message if not) before initializing `MFMailComposeViewController`. – JWK Nov 06 '13 at 23:19
  • Hey @fabian789, I should call the above function in this if condition "if (![context save:&error]) {....} " right, So here I can pass the error parameter, but what is this text parameter that I should pass. – Ranjit Aug 05 '14 at 11:41
  • @Ranjit whathever text you want the user to see :) – fabian789 Aug 05 '14 at 12:16
  • ok, so you mean to say that it is the text that we should design, and prefix to the error right? – Ranjit Aug 05 '14 at 12:22
  • well, as you can see from the code, `text` is appended to the title of the alert view, so the user will see "Failed to `text`". – fabian789 Aug 08 '14 at 09:14