I'm, trying to reorder the cells in a table view using core data and nsfetchresultscontroller. I found a few ways of doing this using objective C (of which I know nothing) and tried implementing them in swift, here's what I came up with:
var books: Array<AnyObject> = []
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
books = fetchedResultsController.fetchedObjects!
let book = fetchedResultsController.objectAtIndexPath(fromIndexPath)
self.fetchedResultsController.delegate = nil
books.removeAtIndex(fromIndexPath.row)
books.insert(book, atIndex: toIndexPath.row)
var i = 0
for book in books {
book.setValue(i++, forKey: "position")
}
try! context.save()
self.fetchedResultsController.delegate = self
}
The delegate and context references are in an extension in another file. With this code sometimes it works and sometimes it doesn't. I change the order, run the app again and it's in a completely different order that I can't figure out. Especially when I move more than one row at once. And if I print each object's "position" atribute to the console I can see they're really not being updated properly. But why not?
What am I doing wrong? What would be a better option?
Thanks in advance,
Daniel
Edit:
Ok, here's the working code:
func initializeFetchedResultsController() {
let request = NSFetchRequest(entityName: "Book")
let sortDescriptor = NSSortDescriptor(key: "position", ascending: true)
request.sortDescriptors = [sortDescriptor]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {
fatalError("Failed to initialize FetchedResultsController: \(error)")
}
}
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
initializeFetchedResultsController()
var objects = self.fetchedResultsController.fetchedObjects! as! [ObjectClass]
self.fetchedResultsController.delegate = nil
let object = objects[fromIndexPath.row]
objects.removeAtIndex(fromIndexPath.row)
objects.insert(object, atIndex: toIndexPath.row)
var i = 0
for object in objects {
object.position = i++
}
try! context.save()
self.fetchedResultsController.delegate = self
}
The trick is that the initializeFetchedResultsController function, besides being at viewDidLoad, where I had it, must be repeated inside the moveRowAtIndexPath bit. Also, the initializing function and the array declaration must both be before setting the FRC delegate to nil (obviously, but it's easy to miss).
You shouldn't edit books
, it doesn't belong to you and is effectively private data. Instead you should just be updating the objects that are moving to change the position
value. If you do this using array reordering as in your current logic you should create a new mutable copy of the array provided by the FRC. You should also be setting the new values for the position as NSNumber
instances.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With