Core Data Code Generation

When working with Core Data it is common to create subclasses of NSManagedObject for each entity in the data model for easy property access and other helper methods. Starting with Xcode 8 the default for new entities is for Xcode to automatically create these files for you when the model changes. Is it worth the trouble? Here are my notes on how to use or disable the feature.

Model Code Generation - A Recap

For example, suppose I have a model named World with a single Country entity:

World Data Model

Xcode has long had the ability to create subclass files for Core Data entities for you. Choose any entity in the data model and then from the Editor menu use Create NSManagedObject Subclass....

Xcode sets the language used for the code generation when you create the project. Use the file inspector on the data model to change it if you need to:

Code Generation Language

Xcode adds a subclass named after the entity and a category/extension on the subclass for the properties.

For a Swift project Xcode adds two files for each entity to the project:

  • Country+CoreDataClass.swift
  • Country+CoreDataProperties.swift

For an Objective-C project we get a header and implementation file for each entity:

  • Country+CoreDataClass.h
  • Country+CoreDataClass.m
  • Country+CoreDataProperties.h
  • Country+CoreDataProperties.m

Here is how the subclass looks (some boilerplate removed for brevity) using Xcode 8 and Swift:

// Country+CoreDataClass.swift
@objc(Country)
public class Country: NSManagedObject {
}

Here is the Objective-C version:

// Country+CoreDataClass.h
@interface Country : NSManagedObject
@end
#import "Country+CoreDataProperties.h"

// Country+CoreDataClass.m
#import "Country+CoreDataClass.h"
@implementation Country
@end

A Swift extension on the entity subclass adds the properties and a class method to create a fetch request:

// Country+CoreDataProperties.swift
extension Country {
  @nonobjc public class func fetchRequest() -> NSFetchRequest<Country> {
    return NSFetchRequest<Country>(entityName: "Country");
  }
  @NSManaged public var name: String?
  @NSManaged public var capital: String?
}

The Objective-C version using a category on the subclass:

// Country+CoreDataProperties.h
#import "Country+CoreDataClass.h"
@interface Country (CoreDataProperties)
+ (NSFetchRequest<Country *> *)fetchRequest;
@property (nullable, nonatomic, copy) NSString *name;
@property (nullable, nonatomic, copy) NSString *capital;
@end

// Country+CoreDataProperties.m
#import "Country+CoreDataProperties.h"
@implementation Country (CoreDataProperties)
+ (NSFetchRequest<Country *> *)fetchRequest {
  return [[NSFetchRequest alloc] initWithEntityName:@"Country"];
}
@dynamic name;
@dynamic capital;
@end

Note that for Swift you add @NSManaged to each property, in Objective-C you use @dynamic. Either way you are telling the compiler that the Core Data framework will provide the implementation at runtime.

If you want to make other changes it is highly recommended to do so in your own category/extension. That way you do not risk losing your customizations should you ever regenerate the subclass and property files with Xcode.

Automatic Model Generation - New in Xcode 8

Starting with Xcode 8 you can go an extra step and have Xcode automatically create the subclass and property files for the Core Data model entities. Xcode updates the files automatically to stay in sync with the model.

Codegen setting

The setting is per-entity and has three possible settings:

Manual/None

This is the pre-Xcode 8 behaviour as described above where you need to manually create the subclass or use the Create NSManagedObject Subclass.... If you open an older Core Data project in Xcode 8 it sets the code generation to Manual/None.

Class Definition

The subclass and properties extension/category are created as above with one big difference. Xcode creates the files as part of the Xcode derived data for the project (see below). They are not added to your project directory and do not appear in the Xcode project navigator.

For Objective-C, Xcode adds two extra files:

  • ModelName+CoreDataModel.h
  • ModelName+CoreDataModel.m

where ModelName is the name of the Core Data model (World in my example). The header file includes the other CoreDataClass header files, the implementation file is empty.

For Swift the file <ModelName>+CoreDataModel.swift is also created but it does nothing other than import the Foundation and Core Data frameworks.

This is the default for new entities with Xcode 8 so remember to set to Manual/None each time you create a new entity if you prefer to manage the subclasses yourself.

Category/Extension:

Xcode assumes that you want to manage the NSManagedObject subclass yourself and only creates the properties category/extension code.

Where does Xcode create the files?

It can be a little tricky to find the files as they are not added to the Xcode project navigator. Xcode adds the files to its derived data directory with the actual sub-directory depending on the build target.

To find the files first find the Xcode Derived Data directory. You can change this in the Xcode preferences but is usually under your home directory:

~/Library/Developer/Xcode/DerivedData

From the DerivedData directory you need to dive down into the project specific build directory until you find the DerivedSources and then CoreDataGenerated sub-directories. At that point there should be a directory named after the model (World in my example). Here is a screenshot for the build directory of a Swift project using a debug build for the simulator:

Swift DerivedSources

Is It Worth It?

Not having to regenerate the code when you change the model is a nice feature but it is not without problems. Every now and again I seem to get stuck with Xcode complaining about an undeclared type. This seems to mostly be a problem with modules but sometimes just needs a clean rebuild of the project.

I am also uneasy about having Xcode automatically create the files for me. I want to know if a future version of Xcode decides to change something.

For now I think I will stick to manually creating the files. I like to have the code checked into version control and not have to explore the derived data directory to see what is happening.

Further Reading