Gergely Gergely - 1 month ago 15
iOS Question

TableView section update depend on CoreData transient property change

I'm trying to develop a timetable app.

I have TableViewController which shows the classes on current day. In viewDidLoad( ) I fetch the classes with NSFetchedResultsController:

let fetchRequest = NSFetchRequest(entityName: "Lessons")
let predicate = NSPredicate(format: "startsday = '\(dateFormatter.stringFromDate(NSDate()))'", NSDate())
fetchRequest.predicate = predicate
let sortDescriptorStarts = NSSortDescriptor(key: "starts", ascending: true)
let sortDescriptorTitle = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptorStarts, sortDescriptorTitle]
fetchRequest.fetchBatchSize = 30
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: "sectionIdentifier", cacheName: nil)
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(nil)


And when I set sectionNameKeyPath: "sectionIdentifier"(which is a transient property) they are sorted into three groups "Past", "Now", "Next" depend on current time. It is working. I have a screenshot here.

My problem is: As time goes by the sections aren't updated

The rows should move out and move in other sections and eventually they should all go into the "Past" section, and the "Now", "Next" sections should be deleted.

I can update the sectionIdentifier transient property, but my fetchedResultController doesn't want to notice the changes...

I don't want to use tableView.reloadData( ) because i want animations.

(There is a similar question, but the end of it is different. I can't get my answer.)

Thanks!

Answer

I have found a solution for this problem. It is definitely not the best but it's working nicely. (Swift 2.3)

First, I have a transient property called "sectionIdentifier".This could be "NOW", "NEXT" and "PAST" depend on the current time.I added a new persistent property next to this transient called "sectionID". This property's purpose is to tell if the lesson status (NOW, NEXT, PAST) changed. I changed my original fetch to this:

let fetchRequest = NSFetchRequest(entityName: "Lessons")
let predicate = NSPredicate(format: "startsday = '\(dateFormatter.stringFromDate(NSDate()))'", NSDate())
fetchRequest.predicate = predicate
let sortDescriptorStarts = NSSortDescriptor(key: "starts", ascending: true)
let sortDescriptorTitle = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptorStarts, sortDescriptorTitle]
fetchRequest.fetchBatchSize = 30
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: "sectionIdentifier", cacheName: nil)
fetchedResultsController.delegate = self
do {
    try fetchedResultsController.performFetch()
} catch _ {
   //Handle error
}
if let lessons = fetchedResultsController.fetchedObjects as? [Lessons] {
    for lesson in lessons {
        if lesson.sectionID != lesson.sectionIdentifier! {
            lesson.setValue(lesson.sectionIdentifier!, forKey: "sectionID")
            moc.refreshObject(lesson, mergeChanges: true)
        }
    }
}

After the fetch I set this persistent property to the transient's value. (last 8 line above)

In viewDidLoad() I added this line.

timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(TodayViewController.update), userInfo: nil, repeats: true)

I update my transient property every second. My update method looks like this:

func update() {
    for lesson in fetchedResultsController.fetchedObjects as! [Lessons] {
        if lesson.sectionID != lesson.sectionIdentifier! {
            lesson.setValue(lesson.sectionIdentifier!, forKey: "sectionID")
            moc.refreshObject(lesson, mergeChanges: true)
            do {
                try moc.save()
            }
            catch _ {
                //Handle error
            }
        }
    }
}

So my transient property will always give the lessons current status and the persistent one will alway give the tableviewcell's status. If there is difference I put my transient value into the persistent property and with moc.refreshobject() i will get a the proper FetchedResultControllerDelegate method and i can handle the tableview changes there.