Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reorder TableView Cells With NSFetchedResultsController Core Data - Swift 3

I am using an NSFetchedResultsController. I can't find any straightforward tutorials that walk through it for Swift 3.

So here is what I have done so far. I have successfully populated my table using an NSFetchedResultsController that fetches the inserted data from core data. I created an attribute in my core data model called, orderPosition that is declared as Int32. I haven't done anything with this attribute in regards to adding and saving data to core data upon insert.

In my fetch func where I initialize the NSFetchedResultsController, I have modified my sort descriptor to include the following code:

let sortDescriptor = NSSortDescriptor(key: "orderPosition", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]

Then I implemented the tableView func:

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

    attemptFetch()
    var objects = self.controller.fetchedObjects! 
    self.controller.delegate = nil

    let object = objects[sourceIndexPath.row]
    objects.remove(at: sourceIndexPath.row)
    objects.insert(object, at: destinationIndexPath.row)

    var i = 0
    for object in objects {
        object.setValue(i += 1, forKey: "orderPosition")
    }

    appdel.saveContext()
    self.controller.delegate = self

}

In my implementation of the didChange anObject func, I included the switch-case for .insert, .delete, .update, and .move. My .move case looks like the following:

case.move:
        if let indexPath = indexPath {
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
        if let indexPath = newIndexPath {
            tableView.insertRows(at: [indexPath], with: .fade)
        }
        break

Please help me figure this out. I have been spending weeks trying to understand this. I have gone through many if not all stack overflow questions on this. I implemented some of the loop ideas from one because I had a similar thought process to approach this but it didn't work. There are no Swift 3 core data tutorials/videos that I have come across that will really help me solve and understand this well. I don't want to give up on this.

like image 678
DevSpace Avatar asked Feb 25 '17 22:02

DevSpace


2 Answers

Just implemented similar functionality in my project.Turned out to be easier than it seemed.

Here is an update for your moveRowAt method

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

    // NOT NEEDED, SINCE I ASSUME THAT YOU ALREADY FETCHED ON INITIAL LOAD
    // attemptFetch()

    var objects = self.controller.fetchedObjects! 

    // NOT NEEDED
    //self.controller.delegate = nil

    let object = objects[sourceIndexPath.row]
    objects.remove(at: sourceIndexPath.row)
    objects.insert(object, at: destinationIndexPath.row)

    // REWRITEN BELOW
    //var i = 0
    //for object in objects {
    //    object.setValue(i += 1, forKey: "orderPosition")
    //}

    for (index, item) in items.enumerated() {
        item.orderPosition = index
    }

    appdel.saveContext()
    //self.controller.delegate = self

}

In didChange technically you don't need any code. The table is updated in place, when you move the item in it. And your data in coreData is updated when you save the context. So next time the data is fetched, based on the sortDescriptor you have, it will come in sorted by orderPosition.

Worked for me. Let me know, if you have any questions.

like image 187
Alexander.Kazakov Avatar answered Sep 18 '22 22:09

Alexander.Kazakov


Swift deviates from C in that the =, += and other assignment-like operations returns Void. So your line object.setValue(i += 1, forKey: "orderPosition") is setting i to be one more but is always setting the position to be 0. Try instead:

var i = 0
for object in objects {
    i += 1
    object.setValue(i, forKey: "orderPosition")
}

or if you want to simplify your code a bit you can do

for (i, object) in objects.enumerated() {
    object.setValue(i, forKey: "orderPosition")
}
like image 35
Jon Rose Avatar answered Sep 20 '22 22:09

Jon Rose