NSError and the last challenge


#1

I’ve just for to the last part of the challenge where you ask us to create and hand back new instances of NSError. Can you explain any further what you’re looking for here?

Earlier in the book it discusses handing a pointer to an NSError pointer to the code and then using whatever error it puts in there. Is it something like that or something more complex?

Thanks


#2

I am confused on this too. I am not really sure what the challenge is asking.

I skipped over it for now but I hope one of the mods or maybe Aaron can come by and clear up the confusion :slight_smile:


#3

I believe Aaron wanted you to create your own NSError instance, and return it, rather than the NSError instance that might be created by the NSPropertyListSerialization methods.

This might be of value to you for several reasons. You might want to specify more detailed text than is provided by default. You might also want to add some recovery text and options for the user. Another possibility, is that your app will need to check for error conditions that are specific to it.

Below are two ways that you could accomplish this:

The gist of the first solution is to create your own NSError instance, and set the outError pointer to it. In this case, we’re relying on the NSDocumentController instance to actually display the error message. One thing to note, is that the NSDocumentController instance will not display the NSLocalizedDescriptionKey that you specify, it will always display it’s own description string. However, it will display the other strings that you’ve specified. If you can’t think of something to check for when reading a file, you could just set the “errorFound” variable to “YES”.

[code]- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
todoItems = [NSPropertyListSerialization propertyListWithData:data
options:0
format:nil
error];

// do whatever checks are needed by your app here and set below flag
// to reflect success or failure
BOOL errorFound = YES;

if (errorFound) {        
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    [userInfo setValue:@"Custom Error Description" forKey:NSLocalizedDescriptionKey]; // won't be shown
    [userInfo setValue:@"Custom Failure Reason!" forKey:NSLocalizedFailureReasonErrorKey];
    [userInfo setValue:@"Custom Recovery Suggestion!" forKey:NSLocalizedRecoverySuggestionErrorKey];
    *outError = [NSError errorWithDomain:@"My Custom Domain" code:41 userInfo];
}

return ((todoItems != nil) && !errorFound);

}[/code]

The second solution will give you more control over what is displayed to the user. You will create your own NSError instance and display it yourself, rather than relying on the NSDocumentController to do that for you. Assuming that you’ll still be returning an NSError instance to the NSDocumentController, you’ll want to set a specific domain and error code so that it doesn’t display it’s own error message in addition to yours.

[code]- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
todoItems = [NSPropertyListSerialization propertyListWithData:data
options:0
format:nil
error];

// do whatever checks are needed by your app here and set below flag
// to reflect success or failure
BOOL errorFound = YES;

if (errorFound) {        
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    [userInfo setValue:@"Custom Error Description" forKey:NSLocalizedDescriptionKey];
    [userInfo setValue:@"Custom Failure Reason" forKey:NSLocalizedFailureReasonErrorKey];
    [userInfo setValue:@"Custom Recovery Suggestion" forKey:NSLocalizedRecoverySuggestionErrorKey];
    NSError *customError = [NSError errorWithDomain:@"My Custom Domain" code:41 userInfo];
    [NSApp presentError:customError];
    *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];

}

return ((todoItems != nil) && !errorFound);

}[/code]

I’ve only shown the code for the readFromData:ofType:error: method, but the code will be very similar for the dataOfType:error: method as well. Hope this helps!

Chris


#4

What I came up with is this. I would be grateful for any corrections.

[code]- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
if (!tasks) {
tasks = [NSMutableArray array];
}
NSData *d = [NSPropertyListSerialization dataWithPropertyList:tasks
format:NSPropertyListXMLFormat_v1_0
options:0
error];
if (*outError != nil) {
NSDictionary *myDict = [NSDictionary dictionary];
[myDict setObject:@“File error.” forKey:NSLocalizedDescriptionKey];
[myDict setObject:@“Reason too complicated to explain.” forKey:NSLocalizedFailureReasonErrorKey];
[myDict setObject:@“Think positive.” forKey:NSLocalizedRecoverySuggestionErrorKey];
[myDict setObject:[NSArray arrayWithObjects:@“Accept”, @“Deny”, nil] forKey:NSLocalizedRecoveryOptionsErrorKey];

    NSError *myError = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];
    *outError = myError;
}

return d;

}[/code]


#5

I did something a little different.

[code]- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
NSError *ohCrap = nil;
// This method is being called when a document is being loaded
// We are handed an NSData object and expected to pull our data out of it

// Extract todoItems
todoItems = [NSPropertyListSerialization propertyListWithData:data
                                                      options:0
                                                       format:NULL
                                                        error:&ohCrap];

if (ohCrap != nil) {
    NSAlert *theAlert = [NSAlert alertWithError:ohShit];
    [theAlert runModal]; // Ignore return value.
}

// Return success or failure depending on success of the above call
return (todoItems != nil);

}
[/code]

It works, but I’m not sure if I should be replacing outError like that. If I change it to (NSError *)ohCrap It no longer works. I’m also not really sure what the ** means.

I got the code from the “Using and Creating Error Objects” reference page.


#6

Hi Chris,

Thanks for you guidance on implementing the errors - very interesting, and I got your examples to work with my code nicely.

However, when implementing it in the

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError

method, I can’t seem work out what I should be returning to make it throw up an error when saving.

just throws up an implicit conversion of int to NSData error.

Does anyone have any suggestions on this? Thanks in advance - for now I will continue with the book.

Mark.