Showing posts with label NSManagedObjectContext. Show all posts
Showing posts with label NSManagedObjectContext. Show all posts

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.


Friday, July 21, 2017

Swift 3 - CoreData - CRUD Operations (Tutorial-1)

CoreData is a native iOS framework which works as a wrapper b/w iOS app and sqlite.

Though CoreData also stores data in a sqlite file, It is a powerful API which makes our CRUD operations on internal storage data very easy and handy by proving below features in the API.

  • Data models creations
  • Relationships creation
  • Delete rules/actions 
  • Version handling

In this tutorial I will explain the necessary stuff for simple CRUD operations by taking an example.

You can checkout the example from here.

Below things are must known for these CRUD operations

  • NSPersistentContainer
  • NSManagedObjectContext
  • NSEntityDescription
  • NSManagedObject

I am not going to explain what are those and the importance of those in this post. Please read out the comments in the code in the example link I have given. 

What we are going to save is, We are going to take person details(name and grade) from user and going to store, display, edit and delete using CoreData.





I have created Person entity and added attributes.











Please checkout the code to see how data is getting pushed to managedObjectContent and saving it will actually get stored in the sqlite file.

Grade is something we are saving in the entity called VIP which is a subclass of Person class just to show the subclassing of managed objects.
















  @objc(Person)
   public class Person: NSManagedObject {    
   }

  @objc(VIP)
   public class VIP: Person {
   }



With my example, we can edit and delete also the saved user’s data.







One important point that I want to discuss here is, Whatever data changes(add/edit/delete) we make to our model objects, We need to save the changes like shown below.


Editing Person data,



      func edit(person: NSManagedObject, with name: String, 
                 onCompletion: @escaping (_ person:NSManagedObject) -> Void, 
                 onFailure: @escaping (_ error: NSError) -> Void) {
    
        let managedContext = self.persistentContainer.viewContext
        
        (person as! Person).name = name
        (person as! Person).lastUpdated = Date() as NSDate?
        
        do {
        try managedContext.save()
            onCompletion(person)
        } catch let error as NSError {
            onFailure(error as NSError)
        }
    }




Deleting(soft/hard) Person data,



      func delete(person: NSManagedObject, onCompletion: 
                                              @escaping (_ status:Bool) -> Void) {
        let managedContext =     
               CoreDataManager.sharedInstance.persistentContainer.viewContext

        (person as! Person).active = false

        (person as! Person).lastUpdated = Date() as NSDate?

        do {
            try managedContext.save()
            onCompletion(true)
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }

    }

    

In the next tutorial Let’s see some data validation stuff :)

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