diff --git a/CoreDataValidationTest.xcodeproj/project.pbxproj b/CoreDataValidationTest.xcodeproj/project.pbxproj index 4cb4b7d..8b757a8 100644 --- a/CoreDataValidationTest.xcodeproj/project.pbxproj +++ b/CoreDataValidationTest.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 72BEF6CA185BAA77000FA0CB /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72BEF6A5185BAA76000FA0CB /* CoreData.framework */; }; 72BEF6D2185BAA77000FA0CB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 72BEF6D0185BAA77000FA0CB /* InfoPlist.strings */; }; 72BEF6D4185BAA77000FA0CB /* CoreDataValidationTestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 72BEF6D3185BAA77000FA0CB /* CoreDataValidationTestTests.m */; }; + EDF5ED4F18D37D4900CBA7DA /* fwLocalizedStrings.strings in Resources */ = {isa = PBXBuildFile; fileRef = EDF5ED4E18D37D4900CBA7DA /* fwLocalizedStrings.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,6 +63,7 @@ 72BEF6CF185BAA77000FA0CB /* CoreDataValidationTestTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CoreDataValidationTestTests-Info.plist"; sourceTree = ""; }; 72BEF6D1185BAA77000FA0CB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 72BEF6D3185BAA77000FA0CB /* CoreDataValidationTestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreDataValidationTestTests.m; sourceTree = ""; }; + EDF5ED4E18D37D4900CBA7DA /* fwLocalizedStrings.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = fwLocalizedStrings.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -142,6 +144,7 @@ 72BEF6A8185BAA76000FA0CB /* Supporting Files */ = { isa = PBXGroup; children = ( + EDF5ED4E18D37D4900CBA7DA /* fwLocalizedStrings.strings */, 72BEF6A9185BAA76000FA0CB /* CoreDataValidationTest-Info.plist */, 72BEF6AA185BAA76000FA0CB /* InfoPlist.strings */, 72BEF6AD185BAA76000FA0CB /* main.m */, @@ -245,6 +248,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + EDF5ED4F18D37D4900CBA7DA /* fwLocalizedStrings.strings in Resources */, 72BEF6C0185BAA77000FA0CB /* Images.xcassets in Resources */, 72BEF6AC185BAA76000FA0CB /* InfoPlist.strings in Resources */, 722C2BFD188A6146001A0565 /* SSViewController.xib in Resources */, diff --git a/CoreDataValidationTest/Person.m b/CoreDataValidationTest/Person.m index bde81a7..7d21c85 100644 --- a/CoreDataValidationTest/Person.m +++ b/CoreDataValidationTest/Person.m @@ -14,31 +14,105 @@ @implementation Person @dynamic firstName; @dynamic lastName; +-(NSDictionary *)privateValidationPredicates { // used in case the model does not define limits + return @{@"firstName" : @[[NSPredicate predicateWithFormat:@"length >= 2"],[NSPredicate predicateWithFormat:@"length <= 10"]], + @"lastName" : @[[NSPredicate predicateWithFormat:@"length >= 2"],[NSPredicate predicateWithFormat:@"length <= 10"]], + @"someNumber" : @[[NSPredicate predicateWithFormat:@"self >= 2"],[NSPredicate predicateWithFormat:@"self <= 10"]]}; +} -- (BOOL)validateFirstName:(id *)ioValue error:(NSError **)outError { - - // firstName's validation is not specified in the model editor, it's specified here. - // field width: min 2, max 10 - - BOOL isValid = YES; - NSString *firstName = *ioValue; +-(BOOL)validateValue:(__autoreleasing id *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)outError { NSString *errorMessage; NSInteger code; - - if (firstName.length < 2) { - - errorMessage = @"First Name must be at least 2 characters."; - code = NSValidationStringTooShortError; + BOOL isValid; + NSAttributeDescription *mad = [[[self entity] propertiesByName] valueForKey:key]; // refractor and don't shorten My Attribute Description ;) + if (NO == [mad isOptional] && nil == *value) { + // raise error because values must not be nil + errorMessage = NSLocalizedStringFromTableInBundle(@"fw_val_expectedNoNil", @"fwLocalizedStrings" , [NSBundle mainBundle], @"did not expected a nil value" ); + code = 100; isValid = NO; - - } else if (firstName.length > 10) { - - errorMessage = @"First Name can't be more than 10 characters."; - code = NSValidationStringTooLongError; - isValid = NO; - + } else { + switch ([mad attributeType]) { + case NSInteger16AttributeType: + case NSInteger32AttributeType: + case NSInteger64AttributeType: + case NSDoubleAttributeType: + case NSFloatAttributeType: + case NSBooleanAttributeType: + isValid = [*value isKindOfClass:[NSNumber class]]; + if (NO == isValid) { // not a NSNumber + errorMessage = NSLocalizedStringFromTableInBundle(@"fw_val_expectedNSNumber", @"fwLocalizedStrings" , [NSBundle mainBundle], @"expected a number here" ); + code = 101; +// code = NSValidation StringTooShortError + } else { // validate NSNumber against any limit set in the model + for (NSPredicate* predicate in [mad validationPredicates]) { + NSString *predString =[predicate predicateFormat]; + isValid = [predicate evaluateWithObject:*value]; + NSArray *splitString = [predString componentsSeparatedByString:@" "]; + NSString *expectedLimit = [splitString lastObject]; + BOOL shouldBeBigger = ([predString rangeOfString:@">"].length > 0); + NSLog(@"Firstname: %@, predicate: %@, match :%d, shouldBeBigger: %d ",*value,[predicate predicateFormat], isValid, shouldBeBigger); + if (NO == isValid) { + if (shouldBeBigger) { + code = NSValidationNumberTooLargeError; + errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"fw_val_NumberToBig", @"fwLocalizedStrings" , [NSBundle mainBundle], @" must be at least " ), key, expectedLimit]; + } else { + code = NSValidationNumberTooSmallError; + errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"fw_val_NumberToSmall", @"fwLocalizedStrings" , [NSBundle mainBundle], @" can't be more than " ), key, expectedLimit]; + } + } + } + } + break; + + case NSStringAttributeType: + isValid = [*value isKindOfClass:[NSString class]]; + if (NO == isValid) { // not a NSString + errorMessage = NSLocalizedStringFromTableInBundle(@"fw_val_expectedNSString", @"fwLocalizedStrings" , [NSBundle mainBundle], @"expected a string here" ); + code = 101; + } else { // validate NSString + NSArray *validationPredicatesToUse; + if ([[mad validationPredicates] count] == 0) { // no limits have been set in the model, lets get our own. Defined above + validationPredicatesToUse = [self privateValidationPredicates][key]; + } else { // obtain the limits as set in the model + validationPredicatesToUse = [mad validationPredicates]; + } + + for (NSPredicate* predicate in validationPredicatesToUse) { + isValid = [predicate evaluateWithObject:*value]; // do the validation + NSString *predString =[predicate predicateFormat]; + BOOL shouldBeLonger = ([predString rangeOfString:@">"].length > 0); // find out if is lower or upper limit + NSArray *splitString = [predString componentsSeparatedByString:@" "]; + NSString *expectedLimit = [splitString lastObject]; // get limiting value i.e. from "length >= 2" get the two. + NSLog(@"Firstname: %@, predicate: %@, isValid :%d, isBigger: %d, limit: %@ ",*value,[predicate predicateFormat], isValid, shouldBeLonger, expectedLimit); + if (NO == isValid) { + if (shouldBeLonger) { + code = NSValidationStringPatternMatchingError; + errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"fw_val_StringMustBeAtLeast", @"fwLocalizedStrings" , [NSBundle mainBundle], @" must be at least characters" ), key, expectedLimit]; + } else { + code = NSValidationStringTooShortError; + errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"fw_val_StringCantBeMore", @"fwLocalizedStrings" , [NSBundle mainBundle], @" can't be more than characters" ), key, expectedLimit]; + } + break; // this is not a switch break. We want to break out of the For..in loop +#warning validation is not covering for NSValidationStringPatternMatchingError + } + } + } + + + break; // this is switch break + case NSDateAttributeType: + isValid = [*value isKindOfClass:[NSDate class]]; + break; + case NSDecimalAttributeType: + isValid = [*value isKindOfClass:[NSDecimalNumber class]]; + break; + + default: + //NSUndefinedAttributeType, NSBinaryDataAttributeType, NSTransformableAttributeType, NSObjectIDAttributeType + isValid = NO; + break; + } } - if (outError && errorMessage) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : errorMessage }; NSError *error = [[NSError alloc] initWithDomain:@"test" @@ -48,6 +122,43 @@ - (BOOL)validateFirstName:(id *)ioValue error:(NSError **)outError { } return isValid; +} + +- (BOOL)validateFirstName:(id *)ioValue error:(NSError **)outError { + NSLog(@"[<%@ %p> %@ line= %d] *ioValue= %@", [self class], self, NSStringFromSelector(_cmd), __LINE__, *ioValue); + NSLog(@"[<%@ %p> %@ line= %d] *outError= %@", [self class], self, NSStringFromSelector(_cmd), __LINE__, *outError); + return YES; + // firstName's validation is not specified in the model editor, it's specified here. + // field width: min 2, max 10 +#warning validateFirstName has been commented +// BOOL isValid = YES; +// NSString *firstName = *ioValue; +// NSString *errorMessage; +// NSInteger code; +// +// if (firstName.length < 2) { +// +// errorMessage = @"First Name must be at least 2 characters."; +// code = NSValidationStringTooShortError; +// isValid = NO; +// +// } else if (firstName.length > 10) { +// +// errorMessage = @"First Name can't be more than 10 characters."; +// code = NSValidationStringTooLongError; +// isValid = NO; +// +// } +// +// if (outError && errorMessage) { +// NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : errorMessage }; +// NSError *error = [[NSError alloc] initWithDomain:@"test" +// code:code +// userInfo:userInfo]; +// *outError = error; +// } +// +// return isValid; } diff --git a/CoreDataValidationTest/fwLocalizedStrings.strings b/CoreDataValidationTest/fwLocalizedStrings.strings new file mode 100644 index 0000000..80d4ed1 --- /dev/null +++ b/CoreDataValidationTest/fwLocalizedStrings.strings @@ -0,0 +1,27 @@ +/* fwLocalizedStrings.strings + CoreDataValidationTest + + Created by Olaf on 14.03.14. + Copyright (c) 2014 Murray Sagal. All rights reserved. */ + +/* number is to big */ +"fw_val_NumberToBig" = "%1@ should be smaller than %2@"; + +/* number is to small */ +"fw_val_NumberToSmall" = "%1@ should be bigger than %2@"; + +/* can't be more than characters */ +"fw_val_StringCantBeMore" = "%1@ can't be more than %2@ characters"; + +/* must be at least characters */ +"fw_val_StringMustBeAtLeast" = "%1@ must be at least %2@ characters"; + +/* expected a number here */ +"fw_val_expectedNSNumber" = "Please enter a number"; + +/* expected a string here */ +"fw_val_expectedNSString" = "Please enter a string"; + +/* did not expected a nil value */ +"fw_val_expectedNoNil" = "Please enter a value"; +