Re: Bindings validation for NSMutableArray of NSMutableDictionary
Re: Bindings validation for NSMutableArray of NSMutableDictionary
- Subject: Re: Bindings validation for NSMutableArray of NSMutableDictionary
- From: "email@hidden" <email@hidden>
- Date: Thu, 13 Dec 2012 22:18:33 +0000
On 13 Dec 2012, at 11:54, email@hidden 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 (email@hidden)
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:
This email sent to email@hidden