这就是我最后所做的。NSFetchedResultsController (NFRC) 需要以两种方式处理 - 获取数据,即执行查询,以及通过委托调用通知 ManagedObject (MO) 集的更改。
获取数据不会触发委托调用。因此,您通常会返回运行 fetch 的结果,即 anNFRC.fetchedObjects(),在 worker 或 interactor 中重新打包为 PONSOS,并将这些传递给 Presenter 以传递给 ViewController。
我发现使用 DataSource Delegate 和 ViewController 一样容易且符合规范(当 Table View 是实现的一部分时)——我将它实现为 ViewController 的单独类。
这种方法保持标准的 VIP 周期,并且不需要视图层中的模型知识。
处理委托调用有点棘手。NFRC 通常与 View 层绑定以处理 Table View 数据委托请求:NFRC 通知插入、删除、移动、更新更改,并且委托适当地处理它。但是,在 VIP 架构中,由于 NFRC 无法附加到视图,因此无法发生 - 它位于模型层并需要留在那里。
我在 Store 实例中对此进行了实例化,并使 Store 实例成为 NFRC 委托,并将委托方法实现为:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
print("item changed")
guard let managedItem = anObject as? ManagedItem else {
return
}
let item = managedItem.toItem()
var eventType: EventType
switch type {
case .insert:
eventType = EventType.insert
case .delete:
eventType = EventType.delete
case .move:
eventType = EventType.move
case .update:
eventType = EventType.update
}
let itemChangeEvent = ItemChangeEvent(eventType: eventType, item: item, index: indexPath, newIndex: newIndexPath)
results.append(itemChangeEvent)
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
results = []
print ("Begin update")
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("End updates")
if let completionHandler = completion {
completionHandler(results)
}
}
基本上,我初始化一个空数组(开始更新),将所有通知作为事件对象(PONSOS)整理到该数组(I,D,M,U)中,然后在完成时运行完成处理程序(结束更新)。完成处理程序作为 fetch() 操作的一部分传入并存储以供将来使用 - 即当需要通知 MO 更改时。完成处理程序从 Interactor 传递过来,如下所示:
func processFetchResults(itemChangeEvents: [ItemChangeEvent]) {
let response = ListItems.FetchItems.Response(itemEvents: itemChangeEvents)
presenter?.presentFetchedItems(response: response)
}
因此,它将所有事件传递给 Presenter,Presenter 传递给可以处理它们的 Data Source Delegate。
然而,这还不够。为了提高效率,数据源委托确实需要与 NSFRC 交互,以将表视图行映射到正确索引路径上的数据行,处理部分信息等。因此,我所做的是创建一个名为 DynamicDataSource 的协议和一个由交互器初始化以“包装” NSFRC 并代理其方法的实现。虽然模型在技术上被交给了视图层,但它实际上被包装在协议后面,并且实现将 MO 转换为 PONSOS。所以我认为它是 Presenter 层的扩展(不是 Swift 扩展!)。
protocol ListItemsDynamicDataSource: AnyObject {
// MARK: - Helper methods
func numberOfSections() -> Int
func rowsInSection(_ section: Int) -> Int
func getItem(index: IndexPath) -> ListItems.FetchItems.ViewModel.DisplayedItem
}
如果将持久性存储更改为内存存储或 JSON 层,则动态数据源实现可以适当地处理它而不影响视图。显然这是使用 NFRC 的一种复杂方式,但我认为这是一个有用的类。对于一个简单的应用程序,这可能是矫枉过正。但是,它可以工作,而且我认为这是一个很好的、符合要求的折衷方案。
值得补充的是,我对 Swift 和 IOS 开发还很陌生,所以这可能不是世界上最好的代码,而且可能有更好的方法!我总是乐于接受反馈和改进建议。