Alfonso Tesauro
Alfonso Tesauro

Reputation: 1792

Custom validation method for Core Data is not called on insert

I am developing a iOS app in swift that uses Core Data. I am trying to implement some Core Data validation. I don't have any custom logic yet, so I am using the "Class definition" setting in the model to make Xcode create the NSManagedObject subclasses automatically. In this case, according to the documentation, I can put my validation logic in an extension. My code looks like this:

extension Person {

   func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
        var error: NSError? = nil

    if let email = value.pointee as? String {
        let regex = "^.+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)

        if !predicate.evaluate(with: 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
    }
}

Unfortunately, my validation code does never get called. I know it gets called only when actually saving the context, but it is never called. I am also trying:

let entity = NSEntityDescription.entity(forEntityName: "Person", in: self.persistentContainer.viewContext)

let person = NSManagedObject(entity: entity!, insertInto: self.persistentContainer.viewContext) as! Person

person.age = 16
person.hasDrivingLicense = true
person.email = "novalidemail"

do {
    try person.validateForInsert()

   } catch {
      let validationError = error as NSError

        print(validationError)
    }

But my validation method does never get called. Another test I made was to override validateValue like this in the extension:

public override func validateValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws {
    print("\(key)")
}

This last method instead gets called for all keys, including the "email" key I am after.

Upvotes: 1

Views: 300

Answers (1)

Alfonso Tesauro
Alfonso Tesauro

Reputation: 1792

I finally found the issue. Prefixing @objc to the validation function made it work, so it gets called. So, instead of:

func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {}

Just:

@objc func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {}

I had not tried this yet because, in this project coming from a tutorial, the validation functions do not have an @objc prefix and still they are called. I would like to understand the difference and discover why I need that prefix in my project. Thanks

Upvotes: 2

Related Questions