Tommy B.
Tommy B.

Reputation: 3639

Sorting fetched results controller data, using two properties, using sort descriptors

I have a simple reminder app for plants. I'm trying to sort the data that I get via Core Data's NSFetchedResultsController. My problem is: the data that I'm trying to sort relies on the manipulation of 2 properties.

I have a Plant object with these properties: waterCycle and lastWater.

waterCycle is the number (Double) of days in-between that plant requires water and lastWater is the date (NSDate) when the plant was last watered. I have a function that calculates, if the plant is due, how many days late since it required water. (i.e. Needed water 3 days ago!)

After some research, I believe I cannot sort based on computed properties or do anything too funky with fetch requests. I'm trying to achieve this with NSFetchRequest and NSSortDescriptor but nothing seems to work. Any suggestions are welcome on how to tackle this challenge.

One alternative I had in mind was that I could keep this data within the object record, but that would imply that the app would need to recompute and save all objects each day in order only to bump that late day number which is pretty cumbersome in my opinion.

Here's some code, do not hesitate to ask for more info or code. Thanks!


NSFetchedResultsController

private var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest()
    fetchRequest.entity = NSEntityDescription.entityForName("Plant", inManagedObjectContext: managedObjectContext!)

    // this should sort the list so plant that needs water are on top
    let waterCycleSortDescriptor = NSSortDescriptor(key: "waterCycle", ascending: true)
    let lastWaterSortDescriptor = NSSortDescriptor(key: "lastWater", ascending: true)

    fetchRequest.sortDescriptors = [waterCycleSortDescriptor, lastWaterSortDescriptor]

    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
                                                               managedObjectContext: managedObjectContext!,
                                                               sectionNameKeyPath: nil, cacheName: PlantRepository.getCacheName())
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    do {
        try _fetchedResultsController!.performFetch()
    } catch {
        fatalError("\(error)")
    }

    return _fetchedResultsController!
}

I calculate the number of late days with a function similar to this (simplified for post):

static func getLateDays(lastWater: NSDate, waterCycle: Double) -> Int {
    // add waterCycle days to lastWater
    let toDate = lastWater.dateByAddingTimeInterval(NSTimeInterval(60*60*24*waterCycle))
    // calc late days after due date
    let lateDays = NSCalendar.currentCalendar().components(NSCalendarUnit.Day, fromDate: toDate, toDate: NSDate(), options: NSCalendarOptions(rawValue: 0))
    return lateDays.day
}

Upvotes: 0

Views: 873

Answers (1)

MathewS
MathewS

Reputation: 2307

I've struggled with looking for ways to sort NSFetchedResultsController from a computed property/transient attribute as well and after attempting several hacks found no good option.

If I was tacking this specific problem, I would add an attribute that holds the target date for the plant to be watered, and use this for sorting.

You won't need to calculate this every day, but instead updated this only when the user updates the value for lastWater - this will be accurate for making sure that results are ordered, and in the view you can calculate on the fly how many days ahead (or behind) you are for the watering schedule.

Upvotes: 3

Related Questions