Как правильно использовать объекты Core Data типа "один ко многим" с DiffableDataSource?
У меня возникли проблемы с тем, чтобы мои объекты Core Data с отношениями "один ко многим" правильно работали с DiffableDataSource, и я мог использовать некоторые указатели.
У меня есть табличное представление, в котором отображается Task
объекты, полученные из Core Data. Я использую комбинациюDiffableDataSource
а также NSFetchedResultsController
. Объект Task смоделирован таким образом, что он имеет отношение "один ко многим" "подзадачи" к большему количеству объектов Task. В табличном представлении должны отображаться все задачи родительского уровня. При нажатии на родительскую задачу ее подзадачи следует вставлять или удалять из табличного представления под ее положением.
Я попробовал несколько разных решений в своем didChangeContentWith
ниже, но не достигли желаемых результатов. В настоящее время все мое табличное представление, похоже, зашифровано; выбор одной строки вставляет повторяющиеся или неправильные задачи.
Сначала я подумал, что мои клетки не были должным образом подготовлены для повторного использования, но, похоже, это не так. Я также пробовал получать только родительские задачи и использовать их ссылки на подзадачи для вставки, добавлять все задачи и удалять подзадачи, когда они не раскрыты, и пару других методов.
Когда я смотрю на исходный снимок и окончательную разницу, в моем didChangeContentWith
метод, исходный снимок содержит все Задачи (и подзадачи), разница содержит только желаемые Задачи. Я не уверен, что происходит между моим diff и его приложением к табличному представлению. У меня такое чувство, что это связано с отсылкой к Задаче на большее количество Задач, которые не очень хорошо работают с Diffable, но я, вероятно, просто сумасшедший.
Данные и логика примера следующие:
{
title: "Test"
subtasks: [
title: "1",
title: "2",
title: "3"
],
title: "Foo"
subtasks: [
title: "bod",
title: "bat",
title: "bar"
],
title: "Bloop"
subtasks: [
title: "Blah1",
title: "Blah2",
title: "Blah3"
]
}
Модель задачи:
Создание NSFetchedResultsController
:
lazy var fetchedResultsController: NSFetchedResultsController<Task> = {
let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
let nameSort = NSSortDescriptor(key: #keyPath(Task.title), ascending: true)
let dueDate = NSSortDescriptor(key: #keyPath(Task.dueDate), ascending: false)
fetchRequest.sortDescriptors = [dueDate, nameSort]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: nil, cacheName: "taskMinder")
fetchedResultsController.delegate = self
return fetchedResultsController
}()
Конфигурация источника данных:
func configure(cell: UITableViewCell, for indexPath: IndexPath) {
guard let cell = cell as? TaskTableViewCell else { return }
let task = fetchedResultsController.object(at: indexPath)
cell.set(task: task)
}
func configureDataSource() -> UITableViewDiffableDataSource<String, Task> {
return UITableViewDiffableDataSource(tableView: tableView) { [unowned self] (tableView, indexPath, task) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: TaskTableViewCell.reuseID, for: indexPath)
self.configure(cell: cell, for: indexPath)
return cell
}
}
И, наконец, у меня есть следующие методы делегата:
extension TasksViewController: NSFetchedResultsControllerDelegate {
func controller( _ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
// Create diff and go through sections
var diff = NSDiffableDataSourceSnapshot<String, Task>()
snapshot.sectionIdentifiers.compactMap { $0 as? String }.forEach { section in
diff.appendSections([section])
let tasks = snapshot.itemIdentifiersInSection(withIdentifier: section).compactMap { $0 as?
NSManagedObjectID }.compactMap {
controller.managedObjectContext.object(with: $0) as? Task
}
tasks.forEach { task in
if let parent = task.parentTask, parent.isExpanded {
diff.insertItems([task], afterItem: parent)
} else if task.parentTask == nil {
diff.appendItems([task], toSection: section)
}
}
}
// Apply the diff
dataSource?.apply(diff)
}
}
extension TasksViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let task = dataSource?.itemIdentifier(for: indexPath) else { return }
tableView.deselectRow(at: indexPath, animated: true)
// Hide/show subtasks
if task.subtasks != nil {
task.isExpanded.toggle()
if let cell = tableView.cellForRow(at: indexPath) as? TaskTableViewCell {
UIView.animate(withDuration: 0.2) {
cell.isExpanded = task.isExpanded
}
}
} else {
// toggle completion status
}
self.coreDataStack.saveContext()
}
}