Attilio
Attilio

Reputation: 583

Swift - Fetch method with Generics

the Idea is to fetch "Typed" data using Coredata.

Updated

class func retrieve<T: AnyObject>(entityName:T.Type, sortBy:String? = nil, isAscending:Bool? = true, predicate:NSPredicate? = nil) -> AnyObject[]    {

    println("\(entityName)")
    let request = NSFetchRequest(entityName: "Users")
    request.returnsObjectsAsFaults = false
    if predicate != nil {
        request.predicate = predicate
    }

    if (sortBy != nil){
        var sorter: NSSortDescriptor = NSSortDescriptor(key: sortBy , ascending: isAscending!)
        request.sortDescriptors = [sorter]
    }


    var error: NSError? = nil
    var fetchedResult = myDataModel.managedObjectContext.executeFetchRequest(request, error: &error)
    if !error {
        println("errore: \(error)")
    }


    println("retrieved \(fetchedResult.count) elements for \(entityName)")
    return fetchedResult

}

now, what I wrote above is syntactically correct but I believe that there is also another way to make the same, maybe more 'easily'. I think the propose is clear :) I was trying to get Class name from entityName input param

an to call this function in the way below or something similar :

 var xx = myDataModel.retrieve(Users.self)

where

 Users: NSManagedeObject

do you have any suggestion?

Upvotes: 2

Views: 3116

Answers (4)

ibrahimtasdmr
ibrahimtasdmr

Reputation: 11

This useful generic method

lazy var persistentContainer: NSPersistentContainer = {
    let persistent = NSPersistentContainer(name: "SuperModelName")
    persistent.loadPersistentStores { _, error in
        print(error?.localizedDescription ?? "")
    }
    return persistent
}()

var moc: NSManagedObjectContext {
    persistentContainer.viewContext
} 

func fetch<T: NSManagedObject>(_ type: T.Type) -> [T] {
    do {
        let fetchRequest = NSFetchRequest<T>(entityName: type.description())
        let fetchItem = try moc.fetch(fetchRequest)
        return fetchItem
    } catch {
        print(error.localizedDescription)
        return []
    }
}

var beverageData: [Beverage] {
    fetch(Beverage.self)
}

Upvotes: 0

sgib
sgib

Reputation: 1040

I couldn't seem to get the @objc annotations to work for me, so instead I used:

let fetchRequest = NSFetchRequest(entityName: NSStringFromClass(entityClass).componentsSeparatedByString(".").last!)

which drills down to the entity name.

Upvotes: 0

Jean Le Moignan
Jean Le Moignan

Reputation: 22236

If, as @David pointed out, you add @objc(ModelClassName) to your Model classes, here's a simplified version:

func retrieve<T: NSManagedObject>(entityClass:T.Type, sortBy:String? = nil, isAscending:Bool = true, predicate:NSPredicate? = nil) -> T[] {
    let entityName = NSStringFromClass(entityClass)
    let request    = NSFetchRequest(entityName: entityName)

    request.returnsObjectsAsFaults = false
    request.predicate = predicate

    if (sortBy != nil) {
        var sorter = NSSortDescriptor(key:sortBy , ascending:isAscending)
        request.sortDescriptors = [sorter]
    }

    var error: NSError? = nil
    var fetchedResult = myDataModel.managedObjectContext.executeFetchRequest(request, error: &error)
    if !error {
        println("errore: \(error)")
    }

    println("retrieved \(fetchedResult.count) elements for \(entityName)")
    return fetchedResult
}

One would use it this way:

@objc(Client)
class Client : NSManagedObject {
    ...    
}

retrieve(Client)  // This would get all clients in the database.

Upvotes: 3

David Berry
David Berry

Reputation: 41226

You can use NSStringFromClass as long as you make sure that your model classes have non-mangled names by using @objc(ModelClassName)

let request = NSFetchRequest(entityName:NSStringFromClass(entityClass))

Another option, which is more work for you might be something like:

protocol EntityWithName {
    class func entityName() -> String
}

extension MyModelObject : EntityWithName {
    class func entityName() -> String {
        return "MyModelObject"
    }
}

extension NSManagedObjectContext {

    // Create an instance of T and insert it into the Context (this is normal)
    func insert<T:NSManagedObject where T:EntityWithName>(entityClass:T.Type) -> T? {
        let entityDescription = NSEntityDescription.entityForName(entityClass.entityName(), inManagedObjectContext: self)

        if !entityDescription {
            return nil
        }

        return NSManagedObject(entity: entityDescription, insertIntoManagedObjectContext: self) as? T
    }
}

let myModelObject = myContext.insert(MyModelObject.Self)

However, since your model objects are required to have NSManagedObject as a superclass, trying to avoid "binding to objective-c" is really kind of pointless.

One other observation, if you're using mogenerator with it's new swift generation options, then the EntityWithName approach becomes much easier, you can just add:

extension MyModelObject : EntityWithName {}

To each of your user modifiable class files (not the _ one) and it should be fine since mogenerator generates an entityName class method.

Upvotes: 2

Related Questions