Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
351 views
in Technique[技术] by (71.8m points)

ios - Swift - Coredata Migration - Set new attribute value according to old attribute value

One of my current core data entities - Entity1 - has a Boolean attribute called isSaved.

In the new core data model, I am planning to remove isSaved attribute and add a new Int attribute called type. And for all saved Entity1 objects, I'd like to set the value of type according to the value of isSaved in old core data model. (e.g. if isSaved is true, then type is 1, else type is 2).

I've read some articles about light weight core data migration, but none of them seems helpful.

Just wondering if there is any way that can make my planned migration work?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Lightweight migration can't do this. You'll have to create a mapping model and a subclass of NSEntityMigrationPolicy. It's not difficult but it's unfamiliar territory for most iOS developers. The steps run like this:

  1. Create the mapping model. In Xcode, File --> New --> Mapping Model. When you click "Next", Xcode will ask for the source (old) and destination (new) model files for this mapping.
  2. The model file will infer mappings where possible. Everything else will be blank. With your type and some other properties, it'll look something like the following. Entries like $source.timestamp mean to copy the existing value from before the migration.

Initial mapping

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

  2. Add a method to the subclass to convert the value. Something like the following. The method name doesn't matter but it's good if you choose something descriptive. You need to use ObjC types here-- e.g. NSNumber instead of Int and Bool.

     @objc 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 its custom mapping policy. That's in the inspector on the right under "custom policy". Be sure to include the module name and class name.

Custom Policy

  1. Tell the mapping model to use that function you created earlier to get values for the type property from the old isSaved property. The following says to call a function on the custom policy class named typeForIsSaved: (the : is important) with one argument, and that the argument should be the isSaved value on $source (the old managed object).

Custom attribute mapping

Migration should now work. You don't have to tell Core Data to use the mapping model-- it'll figure out that migration is needed and look for a model that matches the old and new model versions.

A couple of notes:

  • If you crash with an error that's something like Couldn't create mapping policy for class named... then you forgot the module name above in step 5 (or got it wrong).
  • If you get a crash with an unrecognized selector error then the method signature in step 4 doesn't match what you entered in step 6. This can also happen if you forget to include @objc in the function declaration.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...