Search code examples
iosswiftcore-datansfetchedresultscontroller

CoreData FetchRequest problems


I'm teaching myself to programme and have thought up this project for myself to learn. I'm having trouble with my code, I was able to save it correctly and load the first state population TVC. However, I'm having problems with the state and number of animals per state TVC. I want to total it per a state. So I would be adding the dogs and cats population together and get the total per a state, but it brings Alabama separately with two different population, can someone help me with this please.

the model below shows how I want it, I'm able to output to State Population correctly but now the second one.

enter image description here

What my code is doing for the second one is that it's getting the data from coredata but I'm using sort descriptor because I don't know any other way to pull the data.

var totalEntries : Int = 0
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()

func fetchRequest() -> NSFetchRequest {
    let fetchRequest = NSFetchRequest(entityName: "Animals")
    let sortDescriptor = NSSortDescriptor(key: "state", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]
    return fetchRequest
}

func getFRC() -> NSFetchedResultsController {
    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
    return frc
}

override func viewDidLoad() {
    super.viewDidLoad()

    frc = getFRC()
    frc.delegate = self

    do {
        try frc.performFetch()
    } catch {
        print("Failed to fetch data")
        return
    }
    totalEntries = moc.countForFetchRequest(fetchRequest(), error: nil) as Int!
    self.tableView.reloadData()


}

override func viewDidAppear(animated: Bool) {

    frc = getFRC()
    frc.delegate = self

    do {
        try frc.performFetch()
    } catch {
        print("Failed to fetch data")
        return
    }
    totalEntries = moc.countForFetchRequest(fetchRequest(), error: nil) as Int!
    self.tableView.reloadData()

}

Solution

  • Your problem is the fetched results controllers aren't designed to show aggregated fetch results like you desire, hence you see all the underlying data instead of the aggregate.

    You could use the FRC if you cheat... Set the section name of the FRC to the state name, then you will have one section in the table per state. In the table delegate return 1 for the number of rows in each section. When configuring the cell use KVC to @sum the populations of all of the animals in that state (the rows for that section as understood by the FRC).

    This is a memory and runtime inefficient solution... It could be improved by caching the calculated sums, but you're adding logic on top of bad design then.

    The correct approach would be to abandon the FRC and use a simple array of dictionaries. This would be generated by changing your fetch request to return dictionary type and configuring it to calculate the sums for you. That's done with an expression something like:

    NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
    expressionDescription.name = @"sumOfPopulations";
    expressionDescription.expression = [NSExpression expressionForKeyPath:@"@sum.population"];
    expressionDescription.expressionResultType = NSDecimalAttributeType;