Core Data relies heavily on string-based APIs, which makes it cumbersome to create type safe code. Most developers end up creating factory methods, macros and other conveniences to improve working with Core Data.
I like to use an Objective-C Category method on NSManagedObject to retrieve the entity name of an NSManagedObject subclass instance. That name is used to make it easier to deal with Core Data's string-typed API.
Let's first take a look at some Objective-C code. Then jump over to Swift.
Objective-C Category
@import CoreData;
@interface NSManagedObject (EntityAdditions)
+ (NSString *)hrl_entityName;
@end
#import "NSManagedObject+EntityAdditions.h"
@implementation NSManagedObject (EntityAdditions)
+ (NSString *)hrl_entityName
{
return NSStringFromClass([self class]);
}
@end
Using NSStringFromClass works because the entity and class have the same name. Also, the code presented in this post assumes that the entity is not associated with a module.

Example Objective-C Usage
Let's assume there is a Core Data entity named HRLEngine whose class name is also HRLEngine. Here is how to use the category method to insert a new engine into a managed object context.
NSString *entityName = [HRLEngine hrl_entityName];
HRLEngine *engine = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
This is common Core Data boilerplate code. All we did was make it slightly more type-safe by using the hrl_entityName category method.
For the most part this works well. However, we can do better.
Let's look at two possible solutions in Swift - NSManagedObject convenience initializer - Generified "factory" method on NSManagedObjectContext
Swift API Goal (By Example)
The goal is to use a type-safe API to create a new instance of a Core Data entity in a given managed object context.
Convenience Initializer
var engine: Engine(managedObjectContext: managedObjectContext)
Factory Method On NSManagedObjectContext
var engine: Engine = managedObjectContext.insertObject()
Both solutions insert a new Engine into the the managed object context.
Swift API Goal Solutions
Deriving The Entity Name
Here's a Swift Extension that mirrors the Objective-C Category defined above. We'll use the fact that the entity and class are the same. It may seem strange to use NSStringFromClass in Swift. Perhaps there's a better way to do "dynamic" programming in Swift 2 that I do not yet know about.
extension NSManagedObject {
public class func entityName() -> String {
// NSStringFromClass is available in Swift 2.
// If the data model is in a framework, then
// the module name needs to be stripped off.
//
// Example:
// FooBar.Engine
// Engine
let name = NSStringFromClass(self)
return name.componentsSeparatedByString(".").last!
}
}
Convenience Initializer
extension NSManagedObject {
convenience init(managedObjectContext: NSManagedObjectContext) {
let entityName = self.dynamicType.entityName()
let entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: managedObjectContext)!
self.init(entity: entity, insertIntoManagedObjectContext: managedObjectContext)
}
}
The key to the convenience initializer is self.dynamicType.entityName().
The value returned from dynamicType represents the NSManagedObject subclass type of the managed object being created. In this case Engine.
The entityName() function can then be called because the type "is-a" NSManagedObject.
Therefore the entityName, in this example, is "Engine". The convenience initializer then calls the managed object's designated initializer, which completes the creation and insertion of an Engine into the managed object context.
Examples
var engine = Engine(managedObjectContext)
var turnout = Switch(managedObjectContext)
var whiskerTrack = WhiskerTrack(managedObjectContext)
Factory Method On NSManagedObjectContext
import CoreData
extension NSManagedObjectContext {
public func insertObject<T: NSManagedObject>() -> T {
guard let object = NSEntityDescription.insertNewObjectForEntityForName(T.entityName(), inManagedObjectContext: self) as? T
else { fatalError("Invalid Core Data Model.") }
return object;
}
}
Examples
let engine: Engine = managedObjectContext.insertObject()
let turnout: Switch = managedObjectContext.insertObject()
let whiskerTrack: WhiskerTrack = managedObjectContext.insertObject()
The compiler generates an error:
let foo: NSString = managedObjectContext.insertObject()
// 'NSManagedObject' is not convertible to 'NSString'
This will compile but fail at runtime:
let foo: NSManagedObject = managedObjectContext.insertObject()
We can help the compiler by improving the insertObject method. More on that in a future post.
Wrap Up
The two solutions currently work with Xcode 7 beta 4.
I personally like the convenience initializer solution. First, it is a natural API. Second, it allows for fully realizing an object in a single initialization call. Here's an example.
extension Engine {
convenience init(name: String, roadNumber: String, managedObjectContext: NSManagedObjectContext) {
self.init(managedObjectContext: managedObjectContext)
self.name = name
self.roadNumber = roadNumber
}
}
Example Usage
var engine = HRLEngine(name: "Missouri Pacific SD40", roadNumber:"3007", managedObjectContext: managedObjectContext)
// if you log the engine...
<Engine: 0x7fb1216e8440> (entity: Engine; id: 0x7fb1216d71f0 <x-coredata:///Engine/t16348995-64B1-43E1-8569-56E818D12F384> ; data: {
name = "Missouri Pacific SD40";
roadNumber = 3007;
...
})
There are plenty of existing Objective-C Core Data "helpers" floating around. I am sure there will be just as many in Swift in the years to come.