Refactoring existing Core Data entity classes?


Steve Christensen
 

I have inherited some code where Core Data entity classes look something like this:

@interface Entity1 : NSManagedObject
Something* common1;
Something* common2;
Something* common3;

SomethingElse* entity1Specific;
...
@end

@interface Entity2 : NSManagedObject
Something* common1;
Something* common2;
Something* common3;

SomethingElse* entity2Specific;
...
@end


I’d like to refactor the classes into:

@interface Common : NSManagedObject
Something* common1;
Something* common2;
Something* common3;
@end

@interface Entity1 : Common
SomethingElse* entity1Specific;
...
@end

@interface Entity2 : Common
SomethingElse* entity2Specific;
...
@end


Is this possible using just a new model version and/or mapping model, or does it require something more involved?

Thanks,
Steve


Chris Hanson
 

On Sep 21, 2018, at 9:26 PM, Steve Christensen via Groups.Io <punster@...> wrote:

Is this possible using just a new model version and/or mapping model, or does it require something more involved?

In fact you don’t even need a new model version, you can just refactor the code.

We made sure that Core Data’s concept of entity inheritance is independent of class hierarchy, so you don’t actually need to create a parent entity that corresponds to the Common class if you don’t want to be able to have a query return instances of both Entity1 and Entity2.

This only works if you’re not using code generation. Xcode’s Core Data code generation (whether invoked automatically or manually) does assume entity and class hierarchies are the same, and will only use NSManagedObject as a parent class for base entities, not some other class you specify. This is one reason we allow category/extension-only code generation: It lets you maintain the class hierarchy you want, while still letting Xcode generate the accessor declarations.

  -- Chris

PS - I’m still using SuperClock on my vintage Macs. :)


Steve Christensen
 

Hi Chris,

Thanks, that’s good to know for the easy path. What about the case where I want to update the model to reflect the class hierarchy? Based on what the Common superclass will handle, I can see that it would be more convenient to request instances of Common to perform certain operations rather than iterating over all the subclasses.

Steve

PS: Regarding SuperClock!, I hope you’re not waiting on any updates. ;)


On Sep 22, 2018, at 11:45 AM, Chris Hanson <cmhanson@...> wrote:

On Sep 21, 2018, at 9:26 PM, Steve Christensen via Groups.Io <punster@...> wrote:

Is this possible using just a new model version and/or mapping model, or does it require something more involved?

In fact you don’t even need a new model version, you can just refactor the code.

We made sure that Core Data’s concept of entity inheritance is independent of class hierarchy, so you don’t actually need to create a parent entity that corresponds to the Common class if you don’t want to be able to have a query return instances of both Entity1 and Entity2.

This only works if you’re not using code generation. Xcode’s Core Data code generation (whether invoked automatically or manually) does assume entity and class hierarchies are the same, and will only use NSManagedObject as a parent class for base entities, not some other class you specify. This is one reason we allow category/extension-only code generation: It lets you maintain the class hierarchy you want, while still letting Xcode generate the accessor declarations.

  -- Chris

PS - I’m still using SuperClock on my vintage Macs. :)



Chris Hanson
 

If you put the entities themselves in an inheritance relationship, Xcode will generate code reflecting that.

I would only do this for “strongly” related entities though since entity inheritance implies table sharing. (Unlike EOF, Core Data doesn’t let you choose whether or not to share storage when using inheritance.)

For example, if I were modeling a blog, I might give both BlogEntry and BlogComment entities a timestamp attribute, but I wouldn’t have a BlogObject parent entity for both. However, I might have a BlogText parent entity with BlogEntryText and BlogCommentText subentities, since each subentity is likely to be virtually identical except for the relationship between it and a BlogEntry or BlogComment. Make sense?

  -- Chris

On Sep 22, 2018, at 6:34 PM, Steve Christensen <punster@...> wrote:

Hi Chris,

Thanks, that’s good to know for the easy path. What about the case where I want to update the model to reflect the class hierarchy? Based on what the Common superclass will handle, I can see that it would be more convenient to request instances of Common to perform certain operations rather than iterating over all the subclasses.

Steve

PS: Regarding SuperClock!, I hope you’re not waiting on any updates. ;)


On Sep 22, 2018, at 11:45 AM, Chris Hanson <cmhanson@...> wrote:

On Sep 21, 2018, at 9:26 PM, Steve Christensen via Groups.Io <punster@...> wrote:

Is this possible using just a new model version and/or mapping model, or does it require something more involved?

In fact you don’t even need a new model version, you can just refactor the code.

We made sure that Core Data’s concept of entity inheritance is independent of class hierarchy, so you don’t actually need to create a parent entity that corresponds to the Common class if you don’t want to be able to have a query return instances of both Entity1 and Entity2.

This only works if you’re not using code generation. Xcode’s Core Data code generation (whether invoked automatically or manually) does assume entity and class hierarchies are the same, and will only use NSManagedObject as a parent class for base entities, not some other class you specify. This is one reason we allow category/extension-only code generation: It lets you maintain the class hierarchy you want, while still letting Xcode generate the accessor declarations.

  -- Chris

PS - I’m still using SuperClock on my vintage Macs. :)




Steve Christensen
 

So, a little background. The model on the server has a number of types that inherit from a common class that contains a number of common properties. The original authors of the iOS app created a bunch of Core Data entities that flatten the hierarchy since the json includes both Common and specific properties. This becomes a pain in some cases because I can’t say, “fetch all the Common class instances” so I can perform operations on the common properties.

At this point I am expecting to manually edit the class files to separate out the Common properties (the CD model file is set for manual class file maintenance). I want to figure out what I need to do to migrate the existing store so that the Common properties get associated with the Common class in the model and any “moving around” within the store happens correctly.

If I can just create a new model version, create a Common entity containing its properties, change all the subclasses to inherit from Common and remove the shared properties from those entities in the model and Core Data will take care of the details at runtime then that’s the best case. Being an occasional pessimist I’m not expecting the best case which is why I was asking for details. I haven’t run across a discussion of somebody doing this sort of thing before.

Steve

On Sep 22, 2018, at 6:54 PM, Chris Hanson <cmhanson@eschatologist.net> wrote:

If you put the entities themselves in an inheritance relationship, Xcode will generate code reflecting that.

I would only do this for “strongly” related entities though since entity inheritance implies table sharing. (Unlike EOF, Core Data doesn’t let you choose whether or not to share storage when using inheritance.)

For example, if I were modeling a blog, I might give both BlogEntry and BlogComment entities a timestamp attribute, but I wouldn’t have a BlogObject parent entity for both. However, I might have a BlogText parent entity with BlogEntryText and BlogCommentText subentities, since each subentity is likely to be virtually identical except for the relationship between it and a BlogEntry or BlogComment. Make sense?

-- Chris

On Sep 22, 2018, at 6:34 PM, Steve Christensen <punster@mac.com> wrote:

Hi Chris,

Thanks, that’s good to know for the easy path. What about the case where I want to update the model to reflect the class hierarchy? Based on what the Common superclass will handle, I can see that it would be more convenient to request instances of Common to perform certain operations rather than iterating over all the subclasses.

Steve

PS: Regarding SuperClock!, I hope you’re not waiting on any updates. ;)


On Sep 22, 2018, at 11:45 AM, Chris Hanson <cmhanson@eschatologist.net> wrote:

On Sep 21, 2018, at 9:26 PM, Steve Christensen via Groups.Io <punster=mac.com@groups.io> wrote:

Is this possible using just a new model version and/or mapping model, or does it require something more involved?
In fact you don’t even need a new model version, you can just refactor the code.

We made sure that Core Data’s concept of entity inheritance is independent of class hierarchy, so you don’t actually need to create a parent entity that corresponds to the Common class if you don’t want to be able to have a query return instances of both Entity1 and Entity2.

This only works if you’re not using code generation. Xcode’s Core Data code generation (whether invoked automatically or manually) does assume entity and class hierarchies are the same, and will only use NSManagedObject as a parent class for base entities, not some other class you specify. This is one reason we allow category/extension-only code generation: It lets you maintain the class hierarchy you want, while still letting Xcode generate the accessor declarations.

-- Chris

PS - I’m still using SuperClock on my vintage Macs. :)


Chris Hanson
 

That should be reasonable, I don’t recall whether you’ll need to create a mapping model or not to actually migrate the existing store.

It does mean that all of the instances will wind up shoved into a single table in the underlying database, that’s a tradeoff you have to decide on for your particular case; it depends how disjoint the subentities really are. If they have a sufficiently strong is-a relationship with each other that you want to be able to iterate over all the objects in the database to update them then (1) you probably don’t have all that many instances :) and (2) they probably aren’t all that disjoint.

  -- Chris

On Sep 22, 2018, at 7:09 PM, Steve Christensen <punster@...> wrote:

So, a little background. The model on the server has a number of types that inherit from a common class that contains a number of common properties. The original authors of the iOS app created a bunch of Core Data entities that flatten the hierarchy since the json includes both Common and specific properties. This becomes a pain in some cases because I can’t say, “fetch all the Common class instances” so I can perform operations on the common properties.

At this point I am expecting to manually edit the class files to separate out the Common properties (the CD model file is set for manual class file maintenance). I want to figure out what I need to do to migrate the existing store so that the Common properties get associated with the Common class in the model and any “moving around” within the store happens correctly.

If I can just create a new model version, create a Common entity containing its properties, change all the subclasses to inherit from Common and remove the shared properties from those entities in the model and Core Data will take care of the details at runtime then that’s the best case. Being an occasional pessimist I’m not expecting the best case which is why I was asking for details. I haven’t run across a discussion of somebody doing this sort of thing before.

Steve


On Sep 22, 2018, at 6:54 PM, Chris Hanson <cmhanson@...> wrote:

If you put the entities themselves in an inheritance relationship, Xcode will generate code reflecting that.

I would only do this for “strongly” related entities though since entity inheritance implies table sharing. (Unlike EOF, Core Data doesn’t let you choose whether or not to share storage when using inheritance.)

For example, if I were modeling a blog, I might give both BlogEntry and BlogComment entities a timestamp attribute, but I wouldn’t have a BlogObject parent entity for both. However, I might have a BlogText parent entity with BlogEntryText and BlogCommentText subentities, since each subentity is likely to be virtually identical except for the relationship between it and a BlogEntry or BlogComment. Make sense?

 -- Chris

On Sep 22, 2018, at 6:34 PM, Steve Christensen <punster@...> wrote:

Hi Chris,

Thanks, that’s good to know for the easy path. What about the case where I want to update the model to reflect the class hierarchy? Based on what the Common superclass will handle, I can see that it would be more convenient to request instances of Common to perform certain operations rather than iterating over all the subclasses.

Steve

PS: Regarding SuperClock!, I hope you’re not waiting on any updates. ;)


On Sep 22, 2018, at 11:45 AM, Chris Hanson <cmhanson@...> wrote:

On Sep 21, 2018, at 9:26 PM, Steve Christensen via Groups.Io <punster@...> wrote:

Is this possible using just a new model version and/or mapping model, or does it require something more involved?

In fact you don’t even need a new model version, you can just refactor the code.

We made sure that Core Data’s concept of entity inheritance is independent of class hierarchy, so you don’t actually need to create a parent entity that corresponds to the Common class if you don’t want to be able to have a query return instances of both Entity1 and Entity2.

This only works if you’re not using code generation. Xcode’s Core Data code generation (whether invoked automatically or manually) does assume entity and class hierarchies are the same, and will only use NSManagedObject as a parent class for base entities, not some other class you specify. This is one reason we allow category/extension-only code generation: It lets you maintain the class hierarchy you want, while still letting Xcode generate the accessor declarations.

 -- Chris

PS - I’m still using SuperClock on my vintage Macs. :)