【问题标题】:MVVM with CoreData and NSFetchedResultsController带有 CoreData 和 NSFetchedResultsController 的 MVVM
【发布时间】:2020-07-13 20:16:34
【问题描述】:

我正在尝试将 MVVM 与 Swift 一起使用。我有一个使用 NSFetchedResultsController 的 UITableViewController,以及一个在 CoreData db 中添加/编辑数据的后台进程。

我不知道在哪里处理 fetchedResultsControllerDelegate 方法。 viewModel 还是 viewController?

因为 UITableView 会在 CoreData 中的数据更新时自动更新,所以在我看来,就像在每个 NSManagedObject 实例周围制作一个包装视图模型对象,并将其绑定到表视图,不是这样做的正确方法。这种方式有点违背了使用 NSFetchedResultsController 的目的。

这是我正在使用的简单 MVC 代码,MVVM 化的最佳方法是什么?

// 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
  }
}

【问题讨论】:

  • 在我看来,没有简单的方法可以将 MVVM 与 CoreData 结合起来。如果必须,您的 ViewModel 可以管理您的 FRC,同时为您的 ViewController 提供回调,这些回调映射到您的 FRC 委托方法。另一方面,如果您要使用 Realm,我发现它比 CoreData 更适合 MVVM。但我不确定你的要求是什么。

标签: ios swift uitableview core-data mvvm


【解决方案1】:

Apple 本身有时会使用我非常喜欢的非常好的Data Provider 模式。

你可以在这里找到一个例子:https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud

这种模式将 FetchedResultsController 分解为一个 Provider 类,您可以将 Viewcontroller(使用 weak var)作为 FetchedResultsControllerDelegate 传递给这个 Provider 类。

我希望你会发现看到这个很有帮助。

【讨论】:

    【解决方案2】:

    你可以让它更 MVVM,但你为什么要这样做?如果你只是在寻找更明确的分离,好的,但请记住,你将不得不做很多柯里化。要回答您的原始问题,您将创建一个新对象(必须是对象,因为它将是 FRC 委托),它也是您的表视图数据源/委托。这个新的视图模型将订阅 FRC 更改相关的委托方法,将托管对象转换为您的单元视图模型等,然后向视图控制器发出信号以重新加载数据等。或者您可以创建稍微转换的委托函数(或闭包)让视图控制器订阅,以便它知道何时重新加载表视图并查询视图模型的计数、索引处的对象等。

    但事实上,这已经是 FRC 的功能,而与之对抗似乎是不必要的工作,并且可能更容易出错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 1970-01-01
      相关资源
      最近更新 更多