I have a NSFetchedResultsController combined with a UITableViewDiffableDataSource.
There is a bool attribute "marked" in my entity which I want to change by clicking on the table view cell. To display the status, I change the background color of the cell. However, the reload is not working as expected. The background color of the cell won't change.
I first tried UITableViewDiffableDataSource<String, NSManagedObjectID>, but obviously that won't work for changes, because the NSManagedObjectID doesn't change. I then updated it to UITableViewDiffableDataSource<Int, Test>, but that also didn't work as expected.
Any ideas how to solve this? Or is it just not (yet) possible?
import UIKit
import CoreData
class MainViewController: UITableViewController {
lazy var fetchedResultController: NSFetchedResultsController<Test> = {
let fetchRequest: NSFetchRequest<Test> = Test.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
let fetchedResultController = NSFetchedResultsController<Test>(fetchRequest: fetchRequest, managedObjectContext: self.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultController.delegate = self
return fetchedResultController
}()
var persistentContainer: NSPersistentContainer { get { return (UIApplication.shared.delegate as! AppDelegate).persistentContainer } }
var viewContext: NSManagedObjectContext { get { return self.persistentContainer.viewContext } }
lazy var dataSource: UITableViewDiffableDataSource<Int, Test> = {
return UITableViewDiffableDataSource<Int, Test>(tableView: self.tableView) { (tableView, indexPath, cdObject) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(cdObject.name!) (\(cdObject.id))"
cell.detailTextLabel?.text = DateFormatter.localizedString(from: cdObject.timestamp!, dateStyle: .short, timeStyle: .short)
cell.backgroundColor = cdObject.clicked ? UIColor.systemOrange : UIColor.systemBackground
return cell
}
}()
@IBAction func barButtonPressed_insert(_ sender: UIBarButtonItem) {
for i in 0 ..< 7 {
let newCDEntry = Test(context: self.viewContext)
newCDEntry.id = Int16(i)
newCDEntry.name = "Name \(i)"
newCDEntry.timestamp = Date()
}
self.viewContext.transactionAuthor = "regular insert"
defer { self.viewContext.transactionAuthor = nil }
do {
try self.viewContext.save()
} catch {
let nserror = error as NSError
if let conflicts = nserror.userInfo["conflictList"] as? [NSConstraintConflict] {
for conflict in conflicts {
for conflictObject in conflict.conflictingObjects {
self.viewContext.delete(conflictObject)
}
}
}
do {
try self.viewContext.save()
} catch {
print("##### error saving insert")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
do {
try self.fetchedResultController.performFetch()
} catch {
print("could not fetch data")
}
self.applyFetchedDataToDataSource(animated: false)
}
private func applyFetchedDataToDataSource(animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Int, Test>()
snapshot.appendSections([0])
snapshot.appendItems(self.fetchedResultController.fetchedObjects ?? [])
self.dataSource.apply(snapshot, animatingDifferences: animated)
}
@objc private func managedObjectContextObjectDidSave(notification: Notification) {
print("received save notification")
self.viewContext.mergeChanges(fromContextDidSave: notification)
}
// MARK: - TableView
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let cdObject = self.fetchedResultController.object(at: indexPath)
cdObject.clicked = !cdObject.clicked
do {
self.viewContext.transactionAuthor = "click toggled"
defer { self.viewContext.transactionAuthor = nil }
try self.viewContext.save()
} catch {
print("could not save clicked toggle")
}
}
}
extension MainViewController: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.applyFetchedDataToDataSource(animated: true)
}
}