On 13 Dec 2012, at 11:54, jonat...@mugginsoft.com wrote:

> I bind an NSArray of NSMutableDictionary instances to an NSTableView and 
> enable NSTableColumn editing..
> 
> How can I best implement KVO based validation when editing the view?
> 
> Subclassing NSArray controller is a no go as validateValue:forKeyPath:error 
> is never called.
> Subclassing the NSMutableDictionary model obviously isn't desirable.
> 
> I can refactor the NSMutableDictionary instances into custom objects and use 
> model based validation if needed.
> 
> However, binding dictionaries into table views is often convenient so a 
> working validation strategy would be useful.
> 
> Jonathan

The following category enables NSMutableDictionary KVC validation routing to a 
delegate.

NSMutableDictionary *connection = [self selectedConnection];
connection.validationDelegate = self;

The delegate then performs validation in:

- (BOOL)validateValue:(id *)ioValue forKey:(NSString *)key error:(NSError 
**)outError sender:(NSMutableDictionary *)sender

See https://github.com/mugginsoft/NSMutableDictionary-KVCValidation

Simple refactoring would enable routing of KVC validation for any class.

Jonathan


#import "NSMutableDictionary+KVCValidation.h"
#import <objc/runtime.h>

const char validationDelegateKey;

/*
 
 MethodSwizzle()
 
 ref: 
http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html

 */
void MethodSwizzle(Class klass, SEL origSEL, SEL overrideSEL)
{
    Method origMethod = class_getInstanceMethod(klass, origSEL);
    Method overrideMethod = class_getInstanceMethod(klass, overrideSEL);
    
    // try and add instance method with original selector that points to new 
implementation
    if (class_addMethod(klass, origSEL, 
method_getImplementation(overrideMethod), 
method_getTypeEncoding(overrideMethod))) {
        
        // add or replace method so that new selector points to original method 
        class_replaceMethod(klass, overrideSEL, 
method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    } else {
        
        // class already has an override method so just swap the 
implementations.
        method_exchangeImplementations(origMethod, overrideMethod);
    }
}

@implementation NSMutableDictionary (KVCValidation)

/*
 
 + load
 
 */
+ (void)load
{
    MethodSwizzle(self, @selector(validateValue:forKey:error:), 
@selector(swizzle_validateValue:forKey:error:));
}
    
/*
 
 - setValidationDelegate:
 
 */
- (void)setValidationDelegate:(id)validationDelegate
{
    objc_setAssociatedObject(self, &validationDelegateKey, validationDelegate, 
OBJC_ASSOCIATION_RETAIN);
}

/*
 
 - validationDelegate
 
 */
- (id)validationDelegate
{
    return objc_getAssociatedObject(self, &validationDelegateKey);
}

/*
 
 - swizzle_validateValue:forKey:error:
 
 */
- (BOOL)swizzle_validateValue:(id *)ioValue forKey:(NSString *)key 
error:(NSError **)outError
{
    id validationDelegate = self.validationDelegate;
    SEL validationSelector = @selector(validateValue:forKey:error:sender:);
    BOOL isValid = NO;
    
    if ([validationDelegate respondsToSelector:validationSelector]) {
        isValid = [validationDelegate validateValue:ioValue forKey:key 
error:outError sender:self];
    } else {
        // remember, we swap IMPS at run time
        isValid = [self swizzle_validateValue:ioValue forKey:key 
error:outError];
    }
    
    return isValid;
}
@end

Jonathan


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to