Generic CoreData


CoreData is nice framework, it does a lot of things automatically and there are definitely a lot of reasons to use it. But CoreData is Objective-C framework and when it comes to Swift, some things aren’t so nice. One of those things is type safety.

One of nice Swift features are generics. It allows us to write code that operates on different data types while keeping type safety. Lets take a look at NSManagedObjectContext method:

- (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error

Which is translated into this when calling from Swift:

func executeFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> [AnyObject]?

While this code allows us to operate on different data types, it isn’t type safe.

Ideally, it would be great to have something like this in Swift:

func executeFetchRequest(request: NSFetchRequest<T>, error: NSErrorPointer) -> [T]?

This isn’t currently possible because NSFetchRequest is Objective-C object and because of that it can’t use generics.

One possible improvement would be something like this:

extension NSManagedObjectContext {
    func executeFetchRequest<T: NSManagedObject>(request: NSFetchRequest, error: NSErrorPointer, type: T.Type) -> [T]? {
        return self.executeFetchRequest(request, error: error) as? [T]
    }
}

This makes some things easier. We don’t have to cast every time when request is executed, but we still have to construct NSFetchRequest with entity name which is of type String. We can use another trick to avoid that:

extension NSManagedObjectContext {
    func executeFetchRequest<T: NSManagedObject>(error: NSErrorPointer, type: T.Type, builder: ((NSFetchRequest) -> ())?) -> [T]? {
        let request = NSFetchRequest(entityName: NSStringFromClass(T.self))
        builder?(request)
        return self.executeFetchRequest(request, error: error) as? [T]
    }
}

We create NSFetchRequest in our wrapper method and pass it to closure so that caller can customize it in his will if he wants. This is how we can use our little wrapper:

let items = persistenceController.context.executeFetchRequest(nil, type: Note.self, builder: nil)
let items = persistenceController.context.executeFetchRequest(nil, type: Note.self, builder: { (request) -> () in
		request.predicate = NSPredicate(format: "...")
})

It is little improvement but it makes code much nicer. This is of course not limited to only this use case. You can do similar thing to other APIs as well.