Monday, July 24, 2017

Swift 3 - CoreData - Validations (Tutorial-2)

In Tutorial-1, We have seen the basic CRUD operations with CoreData.
In this tutorial, Let’s see some data validations.

Please checkout the code from here

Let’s suppose we need a 10 character validation on name attribute in Person model.
We can set these validations in the HitList.xcdatamodeld it self as shown below.






After we set these min and max limit validations, If we try to save a name of more than 10 characters, CoreData reports the error like shown below.



  CoreDataManager.sharedInstance.savePerson(id: self.people.count + 1, name: 
              nameToSave, lastUpdated: Date(), 
              grade: personGrade, onCompletion: { (person: NSManagedObject) in
                self.people.append(person)
                self.tableView.reloadData()
            }, onFailure: { (err: NSError) in
                print("Insert Error :- \(err)")
            })



   Insert Error :- Error Domain=NSCocoaErrorDomain Code=1660 
   "The operation couldn’t be completed. (Cocoa error  1660.)" UserInfo=    
   {NSValidationErrorObject=
  (entity: Person; id: 0x60800002d380
   ///Person/t2DCF2680-674D-4CBA-B50B-A741BE0667482> ; data: {
    active = 1;
    id = 1;
    lastUpdated = "2017-07-24 10:07:12 +0000";
    name = "sfdfgdsgdfghgj ghjghj";
  }), NSValidationErrorValue=sfdfgdsgdfghgj ghjghj, NSValidationErrorKey=name, 
  NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1660.)}




From this error, We can know like there is some validation missing in name attribute.
But this error is not clearly giving info like whether it’s a min validation or 
max validation until unless we check name string length.

The interesting part here is, NSManagedObject has methods which will get called upon CRUD.



  open func validateValue(_ value: AutoreleasingUnsafeMutablePointer
                                                                forKey key: String) throws // KVC

  open func validateForDelete() throws

  open func validateForInsert() throws

  open func validateForUpdate() throws



We can override these methods in our NSManagedObject object sub classes and
can write our own validation logics.

Please check Person subclass of NSManagedObject for these validations.



      public override func validateForInsert() throws {
        if let personName = self.name {
            if personName.isEmpty {
                throw NSError(domain: Person.PersonNameErrorDomain, code: 
                    Person.errorCodes.minLimitNotReached.rawValue, userInfo: ["message" : 
                    Person.PersonNameMinLimit])
            }
            else if personName.characters.count > 10 {
                throw NSError(domain: Person.PersonNameErrorDomain, code:    
                    Person.errorCodes.maxLimitExceeded.rawValue, userInfo: ["message" : 
                    Person.PersonNameMaxLimit])
            }
        }
        
        if !self.isValidGrade() {
            throw NSError(domain: Person.PersonGradeErrorDomain, code: 100, userInfo: 
                                                 ["message" : "Grade should be one of [A, SA, M]"])
        }
    }
    
    public override func validateForUpdate() throws {
        if let personName = self.name {
            if personName.isEmpty {
                throw NSError(domain: Person.PersonNameErrorDomain, code: 
                      Person.errorCodes.minLimitNotReached.rawValue, 
                          userInfo: ["message" : Person.PersonNameMinLimit])                      
            }
            else if personName.characters.count > 10 {
                throw NSError(domain: Person.PersonNameErrorDomain, code: 
                      Person.errorCodes.maxLimitExceeded.rawValue, userInfo: ["message" : 
                      Person.PersonNameMaxLimit])
            }
        }
    }




Hope this post is useful. Feel free to comment incase of any queries.