Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I override NSError presentation when bindings is involved?

One thing I've always had trouble with in Cocoa Bindings has been error presentation, for example when the user types the wrong value into a text field with a formatter attached. Normally I would override willPresentError: somewhere in the responder chain, but my problem is the NSError objects created by the Bindings system doesn't contain enough information for me to tell what failed, or if it's even an error I'm interested in customizing. I could completely remove bindings from the equation and create my own errors when validation problems occur, but I feel like I would be throwing out some useful stuff that way.

I've been able to get around this by implementing the NSControl delegate methods and storing the control that failed in an instance variable in my view controller. If it's non-nil by the time willPresentError: rolls around, I know what failed to validate.

- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error;
{
    _errorSender = [control retain];
    return NO;
}

- (NSError *)willPresentError:(NSError *)error;
{
    if ( _errorSender != nil )
    {
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[error userInfo]];
        NSString *help = NSLocalizedString( @"Why are you always messing up? You are a terrible person.", @"" );

        [_errorSender release];
        _errorSender = nil;
        [userInfo setObject:help forKey:NSLocalizedRecoverySuggestionErrorKey];

        return [NSError errorWithDomain:[error domain] code:[error code] userInfo:userInfo];
    }

    return [super willPresentError:error];
}

This works when the first responder changes, but not when I call commitEditing on the view controller, so it's only partially useful to me.

The only other option I can see is taking NSFormatter out of the equation, and using validateValue:forKey:error: in my Core Data managed objects to handle validation. This doesn't make as much sense to me as using a formatter, but at least I'd have full control over the NSError object.

I feel like I must be missing something for there to be this kind of disconnect with error handling. Any suggestions?

like image 762
Marc Charbonneau Avatar asked May 23 '09 18:05

Marc Charbonneau


1 Answers

I could completely remove bindings from the equation and create my own errors when validation problems occur, but I feel like I would be throwing out some useful stuff that way.

You can use NSUnderlyingErrorKey to wrap one error (the object for that key) in another error (the one whose userInfo contains that key).

The only other option I can see is taking NSFormatter out of the equation, and using validateValue:forKey:error: in my Core Data managed objects to handle validation. This doesn't make as much sense to me as using a formatter, but at least I'd have full control over the NSError object.

These are two separate levels, and they aren't mutually exclusive. The formatter validation is at the view layer; key-value validation (in this case, in your managed objects) is at the model layer.

If the validation in question should happen at the view layer, subclass your NSFormatter class (if you haven't already) and implement getObjectValue:forString:errorDescription: to return a more specific error description. (I have no idea whether Bindings actually uses this error description, though. You should check.)

If the validation should happen at the model layer, implement validate<Key>:error: (the single-property version of validateValue:forKey:error:) in your NSManagedObject subclass.

If some of the constraints are at the model layer and others are at the view layer, do both. You're free to implement some checks in the formatter and other checks in the model, if that makes sense for your app and your checks.

like image 111
Peter Hosey Avatar answered Oct 01 '22 18:10

Peter Hosey