MVVM с CoreData и NSFetchedResultsController - PullRequest
1 голос
/ 02 апреля 2020

Я пытаюсь начать использовать MVVM со Swift. У меня есть UITableViewController, использующий NSFetchedResultsController, и фоновый процесс, который добавляет / редактирует данные в базе данных CoreData.

Я не знаю, где обрабатывать методы fetchedResultsControllerDelegate. ViewModel или viewController?

Поскольку UITableView автоматически обновляется при обновлении данных в CoreData, мне кажется, что создание объекта модели-оболочки вокруг каждого экземпляра NSManagedObject и привязка его к табличному представлению, не правильный способ сделать это. Таким образом, это побеждает цель использования NSFetchedResultsController.

Вот простой код MVC, который я использую, каков лучший способ MVVM-ify это?

// Data model
@objc(Post)
public class Post: NSManagedObject {
  @NSManaged public var userName: String?
  @NSManaged public var createdAt: Date?

  @nonobjc public class func fetchRequest() -> NSFetchRequest<Post> {
    return NSFetchRequest<Post>(entityName: "Post")
  }
}
// View controller
class ViewController :  UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
  var fetchedResultsController: NSFetchedResultsController<Post>
  var tableView:UITableView = {
    return UITableView()
  }()

  override func viewDidLoad(){
    view.addSubview(tableView)
    tableView.delegate = self
    tableView.dataSource = self

    let fetchRequest:NSFetchRequest<Post> = Post.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "foo = %@ and bar = %@", argumentArray: ["baz", "loreum"])
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Post.createdAt), ascending: false)]

    self.fetchedResultController = NSFetchedResultsController(fetchRequest: fetchRequest,
                                                             managedObjectContext: context,
                                                             sectionNameKeyPath: #keyPath(Post.createdAt),
                                                             cacheName: nil)
    self.fetchedResultController.delegate = self

    do {
      try self.fetchedResultController.performFetch()
    } catch let error as NSError {
      fatalError("Error: \(error.localizedDescription)")
    }
  }

  // MARK - NSFetchedResultsControllerDelegate
  func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { /** do stuff **/ }
  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { /** do stuff **/ }
  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    switch type {
    case .insert:
      tableView.insertSections(IndexSet(integer: sectionIndex), with: .left)
    case .delete:
      tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
    default:
      break;
    }
  }
  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { /** do stuff **/ }

  // MARK - UITableViewDataSource
  func numberOfSections(in tableView: UITableView) -> Int {
    return fetchedResultsController.sections?.count ?? 0
  }
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { /** do stuff **/ }
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "my-cell-identifier", for: indexPath) as! CustomUITableViewCell
    configureCell(cell, indexPath: indexPath)
    return cell
  }

  // MARK - Cell configuration
  func configureCell(_ cell:CustomUITableViewCell, indexPath:IndexPath){
    let o = fetchedResultsController.object(at: indexPath)
    cell.textLabel?.text = o.userName
    // configure cell more here
  }
}

Ответы [ 2 ]

0 голосов
/ 04 апреля 2020

Apple иногда использует очень хороший шаблон Data Provider, который мне очень нравится.

Пример вы можете найти здесь: https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud

Факторы этого шаблона из FetchedResultsController в класс Provider, и вы передадите Viewcontroller (используя weak var) в качестве FetchedResultsControllerDelegate этому классу Provider.

Надеюсь, вы найдете это полезным.

0 голосов
/ 02 апреля 2020

Вы можете сделать его более MVVM, но почему вы хотите? Если вы просто ищете более четкое разделение, хорошо, но имейте в виду, вам придется много каррировать. Чтобы ответить на исходный вопрос, вы должны создать новый объект (должен быть объектом, потому что это будет делегат FR C), который также является источником / делегатом данных табличного представления. Эта новая модель представления будет подписываться на методы делегирования, связанные с изменением FR C, преобразовывать управляемые объекты в модели представления вашей ячейки и т. Д. c. и затем подайте сигнал контроллеру представления для перезагрузки данных, et c. Или вы можете создать слегка преобразованные функции делегатов (или замыкания) для контроллера представления, на который будет подписываться, чтобы он знал, когда нужно перезагрузить табличное представление и запросить модель представления для количества, объекта по индексу и т. Д. c.

* 1002. * По правде говоря, это уже функция FR C, и плыть против этого кажется ненужной работой и может быть более подверженной ошибкам.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...