我在视图控制器中编辑Person对象的属性. Person是NSManagedObject子类.我正在使用Core Data进行早期(保存前)验证.我正在使用记录的validateValue:forKey:error:这样的方法……
NSError *error; BOOL isValid = [person validateValue:&firstNameString forKey:@"firstName" error:&error]; if (!isValid) { ... }
我在Xcode的Core Data模型编辑器中设置了最小值和最大值.当我验证firstName并且它太短时我得到这样的错误……
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:.
所以我在Person.m中实现了validateFirstName:error:并且方便地,它通过视图控制器中的现有validateValue:forKey:error:方法执行,就像文档所说的那样.
但是在validateFirstName:error:中,即使firstName太短,错误仍然是nil.当我继续并控制返回到视图控制器时,会出现类似于此问题顶部的错误,但同样,这已经太晚了.我希望在控制时间到达validateFirstName:error:时,firstName将根据模型编辑器中指定的约束进行验证,并且填充的错误对象将通过error参数传入.但事实并非如此.
我留下了两个想法,可能会为转换声明带来一个好的家园……
>在Person.m中实现一个自定义方法,如firstNameValidationForValue:error:.视图控制器将调用该方法.在firstNameValidationForValue中:error:call validateValue:forKey:error:.当它返回错误时,构造一个有意义的错误消息,使用该开关,创建一个新的NSError对象并将其返回给视图控制器以供使用.这有效,但它偏离了标准的KVC方法.
>从Xcode中的Core Data模型编辑器中删除所有验证,并执行validateFirstName:error:等方法中的所有验证.根据结果,创建一个新的NSError对象并将其返回给视图控制器以供使用.这具有约束和错误消息在相同方法中的优点.而且,与第一个想法不同,继续遵循标准的KVC方法.
你会怎么做?还有另外一种方法吗?
编辑:编辑周期的其他详细信息……
编辑周期从用户点击添加开始.一个新的Person对象被插入到MOC中.视图显示用于编辑的表单,导航栏上显示“取消”和“完成”按钮.用户开始在fieldA中输入数据,完成并点击fieldB.假设fieldA必须在继续之前有效.在fieldB成为第一个响应者之前,fieldA的验证运行.它失败.视图控制器显示从验证返回的错误消息,而fieldA仍然是第一个响应者.用户修复了问题,然后再次点击fieldB.再次验证运行,这次它通过. fieldB成为第一个响应者.这个“添加数据/点击另一个字段或点击下一个/验证/移动到下一个字段与否”过程继续.
重要的是要知道用户可以随时点击取消.在这种情况下,就MOC而言,我所要做的就是[myMOC rollback] ;.
@ImHuntingWabbits:如果我调用save而不是validateValue:forKey:error:该方法存在问题.假设用户在fieldA中输入数据.用户点击fieldB并运行fieldA的验证.此验证使用“保存然后解析错误”方法.但假设它通过,所有其他领域也通过.所以现在MOC已经被保存了.但是用户没有点击Done并且可以很好地按下取消.如果用户点击取消,则必须撤消保存.如果模型非常简单但可能非常复杂,那可能相对容易.在我的特殊情况下,我不想采取这种方法.
另一个编辑
我们是否可以在github上重新召集:github.com/murraysagal/CoreDataValidationWithiOS我在那里有一个示例应用程序,也许更好地描述了自述文件中的问题.示例应用程序显示,验证通常在iOS上并不困难.该应用程序演示了如何将有意义的错误消息发送回VC并保持完全符合KVC标准.
它讨论了核心数据的两种可能的增强功能,在将它们放到雷达之前,我想要一些反馈.
解决方法
@H_404_48@ 我不会在iOS上使用核心数据验证.核心数据验证是为带有绑定的桌面设计的,而不是针对iOS设计的.您可以更轻松地在视图控制器中进行验证,并使用核心数据验证作为备份,而不是尝试将核心数据验证连接到用户界面.
更新
Could you explain why you think it will be easier to implement validation on the View Controller level. While proper validation and error handling is never easy,I can’t see a reason why the Core Data level validation should be more complex (besides the issue that validations are possibly performed more than one time,even when it is not required). You also don’t answer the case where there is no VC,or when there are more than one VCs handling objects. Also,certain validations can’t be performed on VC level (e.g. check for uniqueness of certain properties,which is a pain anyway).
当Core Data与用户界面之间存在相当紧密的耦合时,为OS X添加了核心数据验证.这种耦合被称为绑定.使用绑定,立即进入文本字段并“自动”添加到与该字段关联的核心数据实体.
此外,当该数据更新时,Core Data可以“回复”验证回到文本字段,以便文本字段可以拒绝数据输入并解释它被拒绝的原因.
这些绑定在iOS上不存在.它们需要手动编写每个数据入口点.
既然我们正在编写这些检查点,那么当我们可以直接将更集中的验证编写到视图控制器中并保存自己的抽象级别时,没有理由尝试挂钩像Core Data的验证这样的通用系统.
在数据导入的情况下,我们再次有一个处理导入的控制器.导入控制器和核心数据之间没有直接连线,所以当我们编写专注的代码来解决问题时,我们还有理由尝试插入像Core Data验证这样的通用系统.
通用系统泄漏边缘情况.如果有人花时间来覆盖大多数边缘情况(如Core Data和OS X上的绑定),那么继续使用它们.但是,如果你必须自己覆盖这些边缘或连接代码,那么集成到通用系统中的价值就很小.对于通用系统不是为处理用例而设计的情况尤其如此(例如核心数据验证和iOS).
Core Data的几个部分早于iOS,并且不适合iOS.验证就是其中之一.