Как правильно использовать один-ко-многим объектам базовых данных с DiffableDataSource? - PullRequest
0 голосов
/ 25 апреля 2020

У меня были проблемы с получением объектов Core Data с отношениями «один ко многим» для правильной работы с DiffableDataSource, и я мог использовать некоторые указатели.

У меня есть табличное представление, в котором отображаются Task объекты, извлеченные из базовых данных. Я использую комбинацию DiffableDataSource и NSFetchedResultsController. Объект «Задача» моделируется таким образом, что он имеет отношение «один-ко-многим» подзадач к другим объектам «Задачи». В табличном представлении должны отображаться все задачи родительского уровня. При касании родительской задачи ее подзадачи должны быть вставлены или удалены из табличного представления ниже ее позиции.

В моем методе didChangeContentWith, описанном ниже, я рассмотрел несколько различных решений, но не достиг результатов Я хочу. В настоящее время мое табличное представление, кажется, все забито; выбор одной строки вставляет дубликаты или неправильные задачи.

enter image description here

Сначала я думал, что мои клетки не были должным образом подготовлены к повторному использованию, но, похоже, это не так. Я также пробовал только извлекать родительские Задачи и использовать их ссылки на подзадачи для вставки, добавлять все задачи и удалять подзадачи, когда они не развернуты, и пару других методов.

Когда я смотрю на начальный снимок и окончательный diff, в моем методе didChangeContentWith начальный снимок содержит все Задачи (и подзадачи), а в diff содержатся только нужные Задачи. Я не уверен, что происходит между моим diff и его применением в табличном представлении. У меня такое ощущение, что это связано со ссылкой на Задачу о большем количестве Задач, которые плохо играют с Diffable, но я, вероятно, просто сумасшедший.

Пример данных и логика c выглядит следующим образом:

  {
      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"
      ]
  }

Модель задачи:

enter image description here

Использование 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()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...