Nov 25, 2016:  ~ 5 min read

Slightly easier Core Data manipulation

Working with Core Data is getting easier and easier, but there are a couple of improvements I'd like to talk about, and I'd like to start with the auto-generated, generic NSFetchRequest. It's a step in the right direction, but the problem is that trying to use it without explicitly declaring its type won't work:

class ProductModel: NSManagedObject { }

let request = ProductModel.fetchRequest() // <- Ambiguous use of fetchRequest().
let request1: NSFetchRequest<ProductModel> = ProductModel.fetchRequest() // <- Works properly.

Update, Oct 10, 2017: It has been solved, all of this is now redundant. Yay!

I do hope it's just a matter of time until it's solved, but in the meantime, I thought it can be improved a bit, by making use of protocols:

protocol CoreModel {

	associatedType Entity: NSManagedObject

extension CoreModel {
	private static var name: String {
		return String(describing: Entity.self)
	static var request: NSFetchedRequest<Entity> {
		return NSFetchRequest<Entity>(entityName: name)

We have a computed name, and we use that to compute a generic NSFetchRequest, of the required type. We can now make use of this like so:

class ProductModel: CoreModel {

	typealias Entity = ProductModel


let request = ProductModel.request // This is an NSFetchRequest<ProductModel>
let results = context.fetch(request) // This would properly return [PersonModel]

There's one more improvement I'd like to mention, and it's related to the insertion of a model. When we fetch some data over the network we should first verify if it exists locally, and update that instead of trying to create a new one directly:

protocol CoreModel {

	static func create(in context: NSManagedObjectContext = theMainContext, id: String) -> Entity {
		let request = self.request
		request.fetchLimit = 1
		request.predicate = NSPredicate(format: "id == %@", id)
		if let result = (try? context.fetch(request))?.first {
			return result
		let entity = NSEntityDescription.insertNewObject(forEntityName: name,
		                                                 into: context) as! Entity
		entity.setValue(id, forKey: "id")
		return entity


let product = ProductModel.create(id: "1")
// = "1" -> This can be omitted.
product.title = "Bike"


// Save the context.

Here, we make use of the aforementioned generic request, set its limit to 1, since an id should be unique, and we check if it returns a result. If it does, we return it, otherwise we insert a new one, set the value of its id field to the passed in value.

Note: it's usually not a good practice to handle Core Data via setValue(_:forKey:), but use the model's properties instead; here, though, we can be quite sure that we won't misspell id, and we can also save a line of code when calling create.

Let me know @rolandleth if there's anything that can be improved, I'd love to chat about it.

Subscribe to my monthly newsletter.
No spam, unsubscribe at any time.