Mastering In CoreData (Part 7 Core Data Relationships Delete Rules)
As we discussed in part5 and part6 when we created Relationship between Entities. Every Entity NSManagedObject is associated with an entity description (an instance of NSEntityDescription) that provides metadata about the object. The object metadata includes the name of the entity that the object represents and the name of its attributes and relationships. There are a number of things you should consider when you create a relationship and delete rule is one of them.
Delete rule
Delete rules are one of the conveniences that make working with Core Data great. Every relationship has a delete rule. It defines what happens when the record that owns the relationship is deleted.
OR
A relationship’s delete rule specifies what should happen if an attempt is made to delete the source object.
In this part we will used our previous Entities to understand the deletion rule. Download the Todo application. First we will do some theory part then we will test it using actual coding
Core Data supports four delete rules → Cascade, Deny, Nullify, No Action.
Cascade
Delete the objects/object at the destination of the relationship when you delete the source. If we delete the user all tasks associated with the user will also be deleted
To add relationship between Entities see Part 5 and Part 6. Go to managed object model file and make the User → Task relationship cascade as shown in Figure 1
Let’s create User having two todo tasks as shown in Figure 2. If you don’t understand this code please see part 5 and 6 where I discussed this code step by step. This code will create a user having two todo tasks associated to it and saved it to the disk
Managed Object Context Visual Mapping is shown in Figure 3
By calling delete code to the user only. As the User → Task deletion rule is cascade by deleting User object it will delete all tasks associated to it. As you can see on the console no task object is remaining.
Visual Mapping of Managed Object Context after deletion operation is shown in Figure 4. As you can see User and two tasks associated to it are also deleted from the context and by calling save() method this will be deleted from the persistent store as well
Nullify
If the delete rule of a relationship is set to Nullify, the destination of the relationship is nullified when the record is deleted but destination object still persist only the relationship between source and destination is deleted
First change the cascade relationship to Nullify from drop down as shown in Figure 5
Let’s create User having two todo tasks as shown above in Figure 2. If you don’t understand this code please see part 5 and 6 where I discussed this code step by step. This code will create a user having two todo tasks associated to it and saved it to the disk
Visual Mapping of Managed Object Context will look like as shown in Figure 6
By deleting User object it will not delete tasks associated to the user as you can see two tasks that we added still in the Managed Object Context and printed in the console as well as shown in the Figure 7
Visual Mapping of Managed Object Context will look like as shown in Figure 8. As you can see the since User → Task has inverse relationship before delete code apply task also have a reference to User object.After deleted User object this relationship was set to nil as shown below
Deny
If there is at least one object at the relationship destination (Tasks), do not delete the source object (User).
If a rule is Deny, then before you delete an object you must remove the destination object or objects from the relationship, otherwise you will get a validation error (Could not save. Error Domain=NSCocoaErrorDomain Code=1600) when you save.
Before go the this section first delete the application and add one task to the user object as shown in Figure 9
Visual Mapping of Managed Object Context will look like as shown in Figure 10
When we call save() method these changes will persist into the disk. Visual Mapping of Persistent Store will look like as shown in Figure 11
By deleting User object it will not delete User since one task object still associated to the User object. It will delete the User from managed object context (memory) but when we will call save() method to push changes to persistent store Exception raised as shown in the console in Figure 12
Visual Mapping of Managed Object Context will look like as shown in Figure 13 after delete operation
Visual Mapping of Persistent store will look like as shown in Figure 14. In physical storage it will have their previous state since our save() method throw an Error because of the Deny rule.
Now rerun application and print User objects. Since our application will launch first time it will first dump data from the persistent store to NSManagedObjectContext Cache. Since User object was not deleted from the persistent store as shown in Figure 15 User object was printed on the console since previously it was only deleted from memory
To work with Deny rule we need to delete all tasks associated tothe user first then delete User . The code looks like this as shown in Figure 16 and worked with no error. You can use validateForDelete on NSManagedObject to check if it can be deleted or not. Now after running this code Persistent store and Managed object context both have the same state contains no User object.
No Action
Do nothing to the object at the destination of the relationship.
For example, if you delete a User, leave all the tasks as they are, even if they still believe they belong to that user.
Story
In the application that I worked almost four years one of our developers who was new to the iOS made one of the relationship NoAction and after we encrypted our Core Data using Core Data encryption. Our QA performed application update scenario and it was working fine and when the application was live it started crashing on update on some users. It took three complete days and night to figured out the crash and the culprit was NoAction. It was crashing on migration of persistent store method because of the inconsistent state of the object graph. Now in our application we don’t have NoAction delete rule.
No Action rule might be of use, because if you use it, it is possible to leave the object graph in an inconsistent state (tasks having a relationship to a deleted user). Let’s do some coding and understand it with visual diagram
First Go to to the CrudOperationCoreData.xcdatamodeld → Tap on User Entity → Tap on tasks relationship → On the Data Model Inspector right side of the window → select Delete rule No Action as shown in Figure 17
First step to add User with two tasks and save it to the persistent store as shown in Figure 18
Visual Mapping of Managed Object Context will look like as shown in Figure 19
Now when we delete the User object task object using delete() method of NSManagedObjectContext. As you can see we didn’t call save method yet as shown in figure 20
Visual Mapping of Managed Object Context will look like as shown in Figure 21. As you can see User object still present in memory you can access objects using their reference as shown in Figure 22 by using expr lldb command. Also context track deleted objects in deleted Objects array and when you call the delete() method on context User reference will store on deleted objects array.
Now calling save() method and it will make the User objects faulty and tasks objects still referencing to the User faulty objects which is no longer exists
Visual Mapping of Managed Object Context and Persistent store will look like as shown in Figure 23 after calling save() method
Summary
In this part 7 we looked the delete rule in depth with the help of coding and diagrams. we discussed four delete rule Core data provides and 90% od the case Cascade and nullify worked.
What Next?
In the next part we will look how to validate NSManagedObject.
Useful Links
https://cocoacasts.com/core-data-relationships-and-delete-rules
https://iosdose.com/wp/2018/03/26/swift-core-data/
https://stackoverflow.com/questions/11990576/core-data-deny-delete-rule-causing-errors