Ios 如何通过核心数据验证将有意义的错误消息返回给视图控制器?
编辑:我想让大家知道,在我发布这篇文章的一个月里,我一直在解决这个问题。现在有一个演示如何轻松实现这一点并保持与KVC兼容的示例。没有理由避免在iOS上进行核心数据验证。它可能与MacOSX不同,但并不困难Ios 如何通过核心数据验证将有意义的错误消息返回给视图控制器?,ios,objective-c,validation,core-data,Ios,Objective C,Validation,Core Data,编辑:我想让大家知道,在我发布这篇文章的一个月里,我一直在解决这个问题。现在有一个演示如何轻松实现这一点并保持与KVC兼容的示例。没有理由避免在iOS上进行核心数据验证。它可能与MacOSX不同,但并不困难 我正在视图控制器中编辑Person对象的属性。Person是NSManagedObject子类。我正在对核心数据进行早期(保存前)验证。我使用的是有文档记录的validateValue:forKey:error:方法,如下所示 NSError *error; BOOL isValid =
我正在视图控制器中编辑Person对象的属性。Person是NSManagedObject子类。我正在对核心数据进行早期(保存前)验证。我使用的是有文档记录的
validateValue:forKey:error:
方法,如下所示
NSError *error;
BOOL isValid = [person validateValue:&firstNameString forKey:@"firstName" error:&error];
if (!isValid) {
...
}
Error Domain=NSCocoaErrorDomain Code=1670 "The operation couldn’t be completed. (Cocoa error 1670.)" UserInfo=0x8f44a90 {NSValidationErrorObject=<Event: 0xcb41a60> (entity: Event; id: 0xcb40d70 <x-coredata://ADB90708-BAD9-47D8-B722-E3B368598E94/Event/p1> ; data: {
firstName = B;
}), NSValidationErrorKey=firstName, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1670.), NSValidationErrorValue=B}
switch ([error code]) {
case NSValidationStringTooShortError:
errorMsg = @"First name must be at least two characters.";
break;
case NSValidationStringTooLongError:
errorMsg = @"First name is too long.";
break;
// of course, for real, these would be localized strings, not just hardcoded like this
}
我在Xcode的核心数据模型编辑器中设置了最小值和最大值。当我验证firstName
时,它太短,我会得到这样一个错误
NSError *error;
BOOL isValid = [person validateValue:&firstNameString forKey:@"firstName" error:&error];
if (!isValid) {
...
}
Error Domain=NSCocoaErrorDomain Code=1670 "The operation couldn’t be completed. (Cocoa error 1670.)" UserInfo=0x8f44a90 {NSValidationErrorObject=<Event: 0xcb41a60> (entity: Event; id: 0xcb40d70 <x-coredata://ADB90708-BAD9-47D8-B722-E3B368598E94/Event/p1> ; data: {
firstName = B;
}), NSValidationErrorKey=firstName, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1670.), NSValidationErrorValue=B}
switch ([error code]) {
case NSValidationStringTooShortError:
errorMsg = @"First name must be at least two characters.";
break;
case NSValidationStringTooLongError:
errorMsg = @"First name is too long.";
break;
// of course, for real, these would be localized strings, not just hardcoded like this
}
这在概念上是好的,但其他视图控制器上的firstName
和其他Person
属性是可编辑的,因此必须在任何视图控制器编辑的firstName
上再次实现切换。一定有更好的办法
查看用于属性级验证的核心数据文档可以发现
If you want to implement logic in addition to the constraints
you provide in the managed object model, you should not override
validateValue:forKey:error:. Instead you should implement methods
of the form validate<Key>:error:.
如果要实现约束之外的逻辑
如果在托管对象模型中提供,则不应重写
validateValue:forKey:错误:。相反,您应该实现方法
表单的验证:错误:。
所以我亲自实现了validateFirstName:error:
。而且很方便,它通过视图控制器中现有的validateValue:forKey:error:
方法执行,正如文档所说的那样
但是在validateFirstName:error:
内部,error
仍然为零,即使firstName
太短。当我继续并将控制返回到视图控制器时,会出现一个错误,如问题顶部所示,但再次说明,为时已晚。我希望在控件到达validateFirstName:error:
时,firstName
将根据模型编辑器中指定的约束进行验证,并且填充的error对象将通过error参数传入。但事实并非如此
我剩下的两个想法可能会给switch语句带来一个好的归宿
firstnamevalizationforvalue:error:
。视图控制器将调用该方法。在firstNameValidationForValue:error:
中调用validateValue:forKey:error:
。当返回错误时,使用开关构造一条有意义的错误消息,创建一个新的NSError对象,并将其返回给view controller以供使用。这是可行的,但它偏离了标准的KVC方法validateFirstName:error:
等方法执行所有验证。根据结果,使用开关构造一条有意义的错误消息,创建一个新的NSError对象,并将其返回给视图控制器以供使用。这样做的优点是约束和错误消息位于同一方法中。而且,与第一个想法不同,它继续遵循标准的KVC方法save
而不是validateValue:forKey:error:
则该方法存在问题。假设用户正在fieldA中输入数据。用户点击fieldB并运行fieldA验证。此验证使用“保存并分析错误”方法。但是假设它通过了,所有其他字段也通过了。所以现在主运行中心已经保存。但是用户还没有点击Done,很可能会按Cancel。如果用户点击“取消”,则必须撤消保存。如果模型非常简单,但可能非常复杂,那么这可能相对容易。在我的特殊情况下,我不想采取这种方法
另一次编辑
我们可以在github再次开会吗:我在那里有一个示例应用程序,也许在自述中对这个问题有更好的描述。该示例应用程序表明,在iOS上进行验证通常并不困难。该应用程序演示了如何将有意义的错误消息返回给VC,并保持与KVC完全兼容
它讨论了对核心数据的两种可能的增强,我希望在将它们放到雷达上之前得到一些反馈 也许您已经考虑过这一点,但您似乎可以在
Person
类中重写NSManagedObject的-(BOOL)validateForUpdate:(NSError**)error
方法,并调用super
的实现来执行MOC验证(即,让核心数据确定您的属性是否符合数据模型的限制条件),如果返回错误,您可以进行一些快速逻辑,以精确缩小错误范围,并包括您自己的,m
// ViewController
-(void)checkPerson:(Person *)myPerson {
NSError *error;
if ([myPerson validateForUpdate:&error]) {
// all is well
error = nil; // zap any previous error
} else {
// validation failed
if ([[error domain] isEqualToString:_kErrorDomain]) { // error is from our code
// display error
NSLog(@"Error message received: %@", [error localizedDescription ]);
// handle the errorCode
if ([error errorCode] == -1) // do something
else // do something else
} else {
// error is not our domain. If needed, then take care of the others, too
}
} // end if/then validateForUpdate
}
User:
name: NSString optional:no
email: NSString optional:no, min_length:3
@interface User : NSManagedObject
+ (User*) createWithParameters:(NSDictionary*)parameters
inManagedObjectContext:(NSManagedObjectContext*)context;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain, readonly) NSString * user_id;
@property (nonatomic, retain) NSString * email;
@end
NSDictionary* user = @{
@"name": @"John Appleseed",
@"user_id": @"634aa621-c63d-4085-aa27-bc3f3b02bcda",
@"email": @"e
};
NSError* error;
if (![context save:&error]) {
NSLog(@"ERROR: %@", error);
}
Error Domain=NSCocoaErrorDomain
Code=1670
"The operation couldn't be completed. (Cocoa error 1670.)"
UserInfo=0x2917570 {
NSValidationErrorObject=<User: 0x28bbc20> (...),
NSValidationErrorKey=email,
NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1670.),
NSValidationErrorValue=e
}"
enum {
NSManagedObjectValidationError = 1550, // generic validation error
NSValidationMultipleErrorsError = 1560, // generic message for error containing multiple validation errors
NSValidationMissingMandatoryPropertyError = 1570, // non-optional property with a nil value
NSValidationRelationshipLacksMinimumCountError = 1580, // to-many relationship with too few destination objects
NSValidationRelationshipExceedsMaximumCountError = 1590, // bounded, to-many relationship with too many destination objects
NSValidationRelationshipDeniedDeleteError = 1600, // some relationship with NSDeleteRuleDeny is non-empty
NSValidationNumberTooLargeError = 1610, // some numerical value is too large
NSValidationNumberTooSmallError = 1620, // some numerical value is too small
NSValidationDateTooLateError = 1630, // some date value is too late
NSValidationDateTooSoonError = 1640, // some date value is too soon
NSValidationInvalidDateError = 1650, // some date value fails to match date pattern
NSValidationStringTooLongError = 1660, // some string value is too long
NSValidationStringTooShortError = 1670, // some string value is too short
NSValidationStringPatternMatchingError = 1680, // some string value fails to match some pattern
...
}
`@"The string for attribute '%@' is too short."`
NSString* descFormat = [CoreDataValidationErrorHelper errorDescriptionForCode:error.code];
NSString* desc = [NSString stringWithFormat:descFormat, error.user[NSValidationKeyErrorKey]];