4

EDIT: Specifically using Swift Generics

I want a countAll() function for all of my entities and I'm hoping to achieve this by writing one generic function.

The following handles an entity called 'Event', but I want to handle an entity named 'T'.

I'm not sure how to write a generic function like this. Could someone help please?

func countAll() -> Int {
    let request: NSFetchRequest<Event> = Event.fetchRequest()
    do {
      return try persistentContainer.viewContext.count(for: request)
    } catch {
      XCTAssert(false, "Core Data failed to fetch with error: " + error.localizedDescription)
      return 0
    }
}

This is the closest I can get before I hit an error:

func count<T: NSFetchRequestResult>(entityName: String) -> Int {
    let request = NSFetchRequest<T>(entityName: entityName)
    do {
      return try persistentContainer.viewContext.count(for: request)
    } catch {
      XCTAssert(false, "Core Data failed to fetch with error: " + error.localizedDescription)
      return 0
    }
}
  • https://stackoverflow.com/questions/3184050/getting-a-count-of-records-in-a-core-data-entity – Renuka Pandey Jun 05 '17 at 08:50
  • Hi @Ren. I am actually doing this in the code above, if you look closer. Also, it doesn't answer my question at all. –  Jun 05 '17 at 08:51
  • Please go through below link: https://stackoverflow.com/questions/1134289/cocoa-core-data-efficient-way-to-count-entities It will surely help you. – Renuka Pandey Jun 05 '17 at 08:55
  • @Ren, that link is to objective-c code !!! My question is regarding Swift generics! –  Jun 05 '17 at 08:57
  • Please go through, swift codes are also explained in chain – Renuka Pandey Jun 05 '17 at 09:07

2 Answers2

6

You can pass the managed object class instead of NSFetchRequestResult to the generic function:

func countAll<T: NSManagedObject>(entity: T.Type) -> Int {
    let entityName = String(describing: entity)
    let request = NSFetchRequest<T>(entityName: entityName)
    do {
        return try thePersistentContainer.viewContext.count(for: request)
    } catch {
        print(error.localizedDescription)
        return 0
    }
}

let count = countAll(entity: YourEntity.self)

An alternative is to define an extension method on NSManagedObject (similarly as in How can I create instances of managed object subclasses in a NSManagedObject Swift extension?):

extension NSManagedObject {

    class func countAll() -> Int {
        let eName = String(describing: self)
        let request = NSFetchRequest<NSFetchRequestResult>(entityName: eName)
        do {
            return try thePersistentContainer.viewContext.count(for: request)
        } catch {
            print(error.localizedDescription)
            return 0
        }
    }
}

let count = YourEntity.countAll()
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you. T.Type and String(describing: self) evaded me. I thought adopting Swift, over Objective-C was only going to take a few weeks. It has taken a few years yet! –  Jun 05 '17 at 09:20
3

A suitable alternative to a generic function is a protocol extension and class methods

protocol Fetchable
{
    associatedtype FetchableType: NSManagedObject = Self

    static var entityName : String { get }
    static func countAll(in persistentContainer: NSPersistentContainer) throws -> Int

}

extension Fetchable where Self : NSManagedObject, FetchableType == Self
{
    static var entityName : String {
        return NSStringFromClass(self).components(separatedBy: ".").last!
    }

    static func countAll(in persistentContainer: NSPersistentContainer) throws -> Int
    {
        let request = NSFetchRequest<FetchableType>(entityName: entityName)
        return try persistentContainer.viewContext.count(for: request)
    }
}

The benefit is you can call the method on the class, just make your NSManagedObject subclasses adopt the protocol.

do {   
    try Event.countAll(in: persistentContainer)
} catch { print(error) }
vadian
  • 274,689
  • 30
  • 353
  • 361
  • This is great. I can't accept it because it doesn't quite answer the question. Nevertheless, it is pretty awesome. –  Jun 05 '17 at 09:11