4

I use a general CoreData query method in my project.

func query(table: String, searchPredicate: NSPredicate) -> [AnyObject]
{
    let context = app.managedObjectContext

    let fetchRequest = NSFetchRequest(entityName: table)

    fetchRequest.predicate = searchPredicate

    let results = try! context.fetch(fetchRequest)
    return results
}

In Swift 3 this doesn't work. I found this on Apple's web site:

func findAnimals() 
{
    let request: NSFetchRequest<Animal> = Animal.fetchRequest
    do 
    {
        let searchResults = try context.fetch(request)
        ... use(searchResults) ...
    } 
    catch 
    {
        print("Error with request: \(error)")
    }
}

Using the Apple example, how would I pass Animal in to the method as a parameter to make findAnimals more generic?

iphaaw
  • 6,764
  • 11
  • 58
  • 83

4 Answers4

4

I haven't tried this but I think something like this would work...

func findCoreDataObjects<T: NSManagedObject>() -> [T] {
    let request = T.fetchRequest
    do 
    {
        let searchResults = try context.fetch(request)
        ... use(searchResults) ...
    } 
    catch 
    {
        print("Error with request: \(error)")
    }
}

You have to make the entire function generic and so you have to tell it what type T is when calling it.

someObject.findCoreDataObjects<Animal>()

I think that should do the job. Not entirely certain though as I'm new to generics myself :D

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • Actually if I'd had chance to spend more time with this after I posted the bounty I would have realised that this was actually the correct answer all along. – iphaaw Oct 12 '16 at 06:52
1

How about this.

func query<T: NSManagedObject>(table: String, searchPredicate: NSPredicate) -> [T] {
    let context = app.managedObjectContext
    let fetchRequest: NSFetchRequest<T> = NSFetchRequest(entityName: table)
    fetchRequest.predicate = searchPredicate
    let results = try! context.fetch(fetchRequest)
    return results
}
closetCoder
  • 1,064
  • 10
  • 21
  • Have tried to us use this with func allRecords(table: String, sort: NSSortDescriptor? = nil) -> [T] { return query(table: table, search: nil, sort: sort) } – iphaaw Sep 26 '16 at 16:05
  • Wait what? You get the bounty for pretty much copying my answer? How does that make sense? Lol – Fogmeister Sep 27 '16 at 08:38
  • @Fogmeister - I had to think long and hard about who got the bounty as the answers were similar. I could copy and paste closetCoder's answer into my code so that's what tipped it. Thanks for your help though. – iphaaw Sep 27 '16 at 09:15
  • Glad you found it helpful. – closetCoder Oct 08 '16 at 08:17
1

Here is the final result that may help someone:

import Foundation
import Cocoa

func addRecord<T: NSManagedObject>(_ type : T.Type) -> T
{
    let entityName = T.description()
    let context = app.managedObjectContext
    let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
    let record = T(entity: entity!, insertInto: context)
    return record
}

func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int
{
    let recs = allRecords(T.self)
    return recs.count
}


func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
    let context = app.managedObjectContext
    let request = T.fetchRequest()
    do
    {
        let results = try context.fetch(request)
        return results as! [T]
    }
    catch
    {
        print("Error with request: \(error)")
        return []
    }
}

func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]
{
    let context = app.managedObjectContext
    let request = T.fetchRequest()
    if let predicate = search
    {
        request.predicate = predicate
    }
    if let sortDescriptors = multiSort
    {
        request.sortDescriptors = sortDescriptors
    }
    else if let sortDescriptor = sort
    {
        request.sortDescriptors = [sortDescriptor]
    }

    do
    {
        let results = try context.fetch(request)
        return results as! [T]
    }
    catch
    {
        print("Error with request: \(error)")
        return []
    }

}


func deleteRecord(_ object: NSManagedObject)
{
    let context = app.managedObjectContext
    context.delete(object)
}

func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)
{
    let context = app.managedObjectContext

    let results = query(T.self, search: search)
    for record in results
    {
        context.delete(record)
    }
}

func saveDatabase()
{
    let context = app.managedObjectContext

    do
    {
        try context.save()
    }
    catch
    {
        print("Error saving database: \(error)")
    }
}

Call it with:

let name = "John Appleseed"

let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name

let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %@", name))
for contact in contacts
{
    print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")
}

deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %@", name))

recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")

saveDatabase()
iphaaw
  • 6,764
  • 11
  • 58
  • 83
1

I use that way in my projects:

static func retrieveRecords<T: NSManagedObject>(table: String, sortDescriptorKey: NSSortDescriptor? = nil) -> [T] {
    do {
        let fetchRequest: NSFetchRequest<T> = NSFetchRequest(entityName: table)
        fetchRequest.sortDescriptors = [sortDescriptorKey!]
        let results = try context.fetch(fetchRequest)
        print("\(results)")
        return results
    } catch let error {
        print("Could not fetch \(error.localizedDescription)")

        return []
    }
}

And to call it:

personen = retrieveRecords(table: "Person", sortDescriptorKey: NSSortDescriptor(key: #keyPath(Person.nachname), ascending: true, selector: #selector(NSString.localizedCompare)))
Ing. Ron
  • 2,005
  • 2
  • 17
  • 33