在上一教程中 ,您学习了如何在数据模型中定义通用约束。 在本教程中,我向您展示如何在代码中定义更高级的约束。
1.项目设置
从GitHub下载我们在上一个教程中创建的项目,并在Xcode中打开它。 打开AppDelegate.swift并更新application(_:didFinishLaunchingWithOptions)
,如下所示。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let entity = NSEntityDescription.entityForName("User", inManagedObjectContext: self.managedObjectContext) {
// Create Managed Object
let user = NSManagedObject(entity: entity, insertIntoManagedObjectContext: self.managedObjectContext)
// Populate Managed Object
user.setValue(44, forKey: "age")
user.setValue("Bart", forKey: "first")
user.setValue("Jacobs", forKey: "last")
user.setValue("me@icloud.com", forKey: "email")
do {
try user.validateForInsert()
} catch {
let validationError = error as NSError
print(validationError)
}
}
return true
}
如果您在模拟器或物理设备上运行该应用程序,则不会引发任何错误。 换句话说,我们在application(_:didFinishLaunchingWithOptions)
创建的托管对象将通过验证,以插入到应用程序的持久性存储中。
2.子类化NSManagedObject
为了验证代码中属性的值,我们需要创建一个NSManagedObject
子类。 从Xcode的File菜单中选择New> File ...,然后从模板列表中选择Core Data> NSManagedObject子类模板。
选择项目的数据模型,然后检查要为其创建NSManagedObject
子类的实体。
选中对原始数据类型使用标量属性 ,告诉Xcode您要将子类的文件保存在何处,然后单击创建 。
3.属性验证
要验证实体的属性,请实现采用以下格式的方法validate<PROPERTY>(_:)
。 该方法应该是投掷方法。 如果验证失败,则会引发错误,并通知Core Data该属性的值无效。
在下面的示例中,我为User实体的first
属性实现了一种验证方法。 将以下代码段添加到User.swift中 。
import CoreData
import Foundation
class User: NSManagedObject {
let errorDomain = "UserErrorDomain"
enum UserErrorType: Int {
case InvalidFirst
}
func validateFirst(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
var error: NSError? = nil;
if let first = value.memory as? String {
if first == "" {
let errorType = UserErrorType.InvalidFirst
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The first name cannot be empty." ] )
}
} else {
let errorType = UserErrorType.InvalidFirst
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The first name cannot be blank." ] )
}
if let error = error {
throw error
}
}
}
验证方法接受类型为AutoreleasingUnsafeMutablePointer<AnyObject?>
一个参数。 那是什么? 不要让参数的类型吓到你。 如类型名称所示, AutoreleasingUnsafeMutablePointer
结构是可变的指针。 它指向对象引用。 我们可以通过其memory
属性访问指针指向的值。
正如我刚才提到的, validateFirst(_:)
方法正在抛出。 如果传递给我们的值无效,则会引发错误。 通过抛出错误,我们通知Core Data托管对象无效。
在下一个示例中,我为User
类的email属性实现一种验证方法。 我们使用正则表达式来验证email
属性的值。
func validateEmail(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
var error: NSError? = nil
if let email = value.memory as? String {
let regex = "^.+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$"
let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
if !predicate.evaluateWithObject(email) {
let errorType = UserErrorType.InvalidEmail
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The email address is invalid." ] )
}
} else {
let errorType = UserErrorType.InvalidEmail
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The email address is invalid." ] )
}
if let error = error {
throw error
}
}
即使您可以使用验证方法修改属性的值,Apple还是强烈建议您这样做。 如果您修改通过验证方法传递给您的值,则内存管理可能会陷入困境。 考虑到这一点,数据验证流程变得非常简单。 验证属性的值,如果无效,则引发错误。 就这么简单。
修改application(_:didFinishLaunchingWithOptions)
中的属性值,然后在模拟器中运行该应用程序。 如果输入的值无效,则将引发错误。
4.对象验证
NSManagedObject
类提供了三个附加的hook子类,可以重写它们来进行数据验证:
-
validateForInsert()
-
validateForUpdate()
-
validateForDelete()
在插入,更新或删除持久性存储中的相应记录之前,Core Data将在托管对象上调用这些方法。 这些方法中的每一个都抛出。 如果抛出错误,则中止相应的插入,更新或删除。
尽管这些挂钩相对于我们之前讨论的属性验证方法所带来的好处可能不是立即显而易见的,但在某些情况下它们是无价的。 想象一下,只要一个用户记录具有一个或多个与之相关联的便笺记录,便无法删除。 在这种情况下,您可以覆盖User
类中的validateForDelete()
方法。
override func validateForDelete() throws {
try super.validateForDelete()
var error: NSError? = nil
if let notes = notes {
if notes.count > 0 {
let errorType = UserErrorType.OneOrMoreNotes
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "A user with notes cannot be deleted.." ] )
}
}
if let error = error {
throw error
}
}
注意,我们使用override
关键字,并在顶部调用超类的validateForDelete()
实现。 如果用户记录与一个或多个便笺记录相关联,则会引发错误,从而阻止用户记录被删除。
5.您应该使用哪个选项?
数据验证是每个处理数据的应用程序的重要方面。 核心数据为开发人员提供了几种用于实施数据验证的API。 但是您可能想知道在应用程序中使用哪个选项。
这取决于您的偏好和项目要求。 对于具有公共约束的简单数据模型,数据模型提供的选项可能就足够了。 也就是说,某些开发人员更喜欢将验证逻辑保留在模型类中,即在NSManagedObject
子类中。 优点是特定模型类的逻辑位于一个位置。
对于更复杂的验证逻辑,建议使用针对属性或validateForInsert()
, validateForUpdate()
和validateForDelete()
挂钩的自定义验证方法。 它们为对象验证增加了功能和灵活性,并且您还受益于模型层包含验证逻辑。
重要的是要理解数据验证包括两个方面,数据验证的逻辑和何时执行数据验证。 模型层负责数据验证。 控制器负责确定何时应执行数据验证,例如,用户何时单击按钮以创建帐户。 这是一个微妙但重要的区别。
最后但并非最不重要的一点是,避免将数据验证逻辑放在控制器层中。 它不必要地弄乱了项目的控制器,通常会导致代码重复。
结论
核心数据使数据验证变得简单而直接。 数据模型可以帮助您解决常见的约束,但是该框架还提供了一些用于自定义数据验证的更高级的API。
翻译自: https://code.tutsplus.com/tutorials/data-validation-with-core-data-advanced-constraints--cms-26623