Hazards of using mutable types as transformable attributes in Core Data

Rohan Bhale
4 min readFeb 16, 2019

--

Core Data supports entity attributes of ‘Transformable’ type. Transformable type allows us to store custom data types as an object of the declared attribute for a record of an Entity. The only requirement is that our custom type should conform to NSCoding or we need to provide a custom value transformer.

This is a very good feature, but we need to use it carefully. Specially if we use mutable types as the value of the transformable attribute, we could land in trouble. I will demonstrate this to you.

Lets begin by launching Xcode and creating a new Single View App and name it as TransformableAttributes. Now click next and create it at any place you wish.

Once we have our project ready, we create the data model. Right click on the project in the project navigator and select new file. Once the pane for creating new file is displayed, for the iOS tab scroll down to the Core Data section. Select the data model icon.

Then we click next and name the data model file as TransformableModel and click create.

We have our data model file in place now. Now lets setup the Entity.

  • Click the ‘TransformableModel.xcdatamodeld’ file to open up the model editor. Create an Entity called TransformableContainer.
  • In the datamodel inspector for the entity, in the Class section of the Entity we set the Codegen as Manual/None, set Module as Current Product Module, and set Name to TransformableContainer(Yes we manually setup the class later).
  • We also add an attributetransformableAttribute’ to the entity. Make this attribute as transformable selecting the option to do so from the drop down to select attribute type. Now open the data model inspector selecting the attribute and make it as non optional. Our model editor should look like this.

At this point we have our data model setup. Now comes the part where we set up a mutable data type called ‘UnpredicatbleValue’ that conforms to NSCoding. This data type has a variable stored property called ‘value’ of type Int. The implementation is as below:

Now we can create the NSManagedObject subclass for our TransformableContainer entity. This class has the implementation as below:

Now please read the note below carefully.

Note: If you replace the value/object that is referred by a managed object with an entirely new value/object, Core Data will treat the managed object as changed. It is said that the managed object has pending changes. This managed object which now has pending changes will be saved next time when save is called on the context.
However if you have an object that is being pointed to by a managed object and you change the objects variable(change the state of the object), Core Data has no clue that it has been changed. Thus the managed object is not marked as having pending changes. Such object wont be saved next time when save is called on the context.

If you have read the note above we can demonstrate this with a practical test now. Lets open ViewController.swift and write the code below:

As you can see we do the following things in viewDidLoad()

  • Get the viewContext from the appDelegate’s persistentContainer.
  • In the first do block we insert a TransformableContainer into the viewContext. The value is set to 3 and then a save is performed on the viewContext.
  • In the second do block we fetch the TransformableContainer. Change the value to 4 and perform save.
  • In the third do block we fetch the TransformableContainer. Print the value.
  • In the fourth do block we fetch the TransformableContainer. We delete it from the context and perform save.

Now we would think that after we changed value to 4 and performed a save, 4 should be saved as the value. Clearly that is not the case. We are in rude shock when the value is actually 3. We get the following logs in the console.

Value set to 3
Value being changed from 3 to 4
transformableContainer.transformableAttribute.value: 3

It is worth to remember the note above when considering using transformable attributes. This is a hazard that you face if you use mutable types as Transformable attributes.

You can avoid this by using non mutable types for Transformable attributes. You cannot change the value without entirely replacing the object. And if you replace the object CoreData knows it has changed and mark the owning managed object as having pending changes. It would be saved next time save is called on the context. So basically we change the implementation of UnpredicatbleValue as below:

Now we are protected by the compiler. Whenever we change the value, it will complain value can’t be changed. The only option we have is to replace the older object with a newer object with the new value set. As a result Core Data is able to track the change and we are safe from the hazard we faced earlier.

Hope this helps.

Did you enjoy this article? Perhaps try one of these:

--

--