Swift - Coredata Migration - set a new attribute value in accordance with the old attribute value

One of my current main data objects, Entity1, has a Boolean attribute called isSaved .

In the new kernel data model, I plan to remove the isSaved attribute and add a new Int attribute called type . And for all saved Entity1 objects, I would like to set the type value to the isSaved value in the old kernel data model. (for example, if isSaved is true, then the type is 1, otherwise the type is 2).

I have read several articles on the migration of bulk data, but none of them seem useful.

Just wondering if there is a way to make my planned migration?

+5
source share
2 answers

Easy migration cannot do this. You will need to create a mapping model and a subclass of NSEntityMigrationPolicy . It is not difficult, but it is an unfamiliar territory for most iOS developers. The steps are as follows:

  • Create a mapping model. In Xcode, File -> New -> Mapping Model. When you click Next, Xcode will ask for the source (old) and target (new) model files for this mapping.
  • The model file will cause mappings where possible. Everything else will be empty. Using type and some other properties, it will look like this. Entries such as $source.timestamp mean copy the existing value before migration.

Initial mapping

  1. Create a new subclass of NSEntityMigrationPolicy . Give the subclass an obvious name, such as ModelMigration1to2 . This class will tell Core Data how to map the old boolean to the new integer.

  2. Add a method to a subclass to convert the value. Something like the following. The method name does not matter, but it is good if you choose something descriptive. Here you need to use ObjC types - for example. NSNumber instead of Int and Bool .

     func typeFor(isSaved:NSNumber) -> NSNumber { if isSaved.boolValue { return NSNumber(integerLiteral: 1) } else { return NSNumber(integerLiteral: 2) } } 
  3. Go back to the mapping model and tell it to use your subclass as a custom mapping policy. This is in the inspector on the right under "customs policy". Be sure to include the module name and class name.

User policy

  1. Tell the mapping models to use the previously created function to get the values โ€‹โ€‹for the type property from the old isSaved property. The following is about calling a function in a user policy class named typeForIsSaved: (important : with one argument and that the argument must be the isSaved value on $source (old managed object).

Custom attribute mapping

Migration should now work. You do not need to specify Core Data to use the mapping model - it turns out that migration is necessary and find a model that matches the old and new versions of the model.

A few notes:

  • If you encounter an error, something like Couldn't create mapping policy for class named... , then you forgot the module name above in step 5 (or you made a mistake).
  • If you get a failure with an unrecognized selector error, then the method signature in step 4 does not match what you entered in step 6.
+14
source

Using Xcode 9.1 Beta with Swift 4, I found that migration works, but you have to be careful how you specify the name of the conversion method, it also seems you need to mark your functions as @objc.

For example, my Value expression:

 FUNCTION($entityPolicy, "changeDataForData:" , $source.name) 

My conversion policy method name:

 class StudentTransformationPolicy: NSEntityMigrationPolicy { @objc func changeData(forData: Data) -> String { return String(data: forData, encoding: .utf8)! } } 

It was definitely difficult and experimented a lot before I got it to run when I started my application after changing the model. It might be easier to implement "createDestinationInstances" for your policy if all this does not work, but we will leave it for another day ...

+1
source

Source: https://habr.com/ru/post/1259878/


All Articles