views:

298

answers:

1

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?

+3  A: 

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.

Peter Hosey
I am subclassing NSFormatter, and while bindings does use the NSString error message I provide the final NSAlert is still pretty bare bones (I'd like to add a recovery suggestion to the error, at a minimum). The validation I'm doing seems like it's better suited for my NSFormatter subclasses, which is why I'm hesitant to implement key-value validation on my model. I'd end up doing all kinds of string parsing that has nothing to do with the data model, just to be able to customize the front-end error message when something goes wrong.
Marc Charbonneau