我在没有太大运气的情况下一直在寻找答案。这个问题几乎相同,但是答案不是很清楚(至少对我来说是这样!):
Which it is the place for NSFetchedResultsController in VIPER architecture?

NSFetchedResultsController对于iOS应用程序来说似乎是一种非常有用的方法,但是我所看到的所有示例都将其放在ViewController层上-至少,VC成为了委托(delegate)。在Clean Architecture/Viper中,模型层与View层之间的连接非常断开,我无法弄清楚在这种体系结构中如何使用NSFRC。对上述问题的回答意味着,交互者应该是一个委托(delegate),但这没有任何意义-被管理对象随后将浮现于交互者,而不是PONSO。也许我还不太了解它,但是(a)它在Clean Architecture中是否占有一席之地; (b)如果可以,那么是否需要正确的Swift实现模式?

最佳答案

这就是我最后所做的。需要通过两种方式访问​​NSFetchedResultsController(NFRC)-获取数据(即执行查询)以及通过委托(delegate)调用通知ManagedObject(MO)集的更改。

提取数据不会触发委托(delegate)调用。因此,通常您将返回运行提取的结果,即anNFRC.fetchedObjects(),在工作器或交互器中重新打包为PONSOS并将其传递给Presenter以传递给ViewController。

我发现使用DataSource Delegate作为ViewController更加容易且一致(当表 View 是实现的一部分时)-我将其实现为ViewController的单独类。

这种方法保持了标准的VIP周期,并且在View Layer中不需要模型知识。

处理委托(delegate)人的电话有些棘手。 NFRC通常绑定(bind)到View层,以处理Table View数据委托(delegate)请求:NFRC通知插入,删除,移动,更新更改,并且委托(delegate)适本地处理它。但是,在VIP架构中,这是不可能的,因为NFRC无法附加到 View 上-它位于模型层中并且需要保留在该层中。

我在Store实例中实例化了此实例,并使Store实例成为NFRC委托(delegate),并将委托(delegate)方法实现为:

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

基本上,我初始化一个空数组(开始更新),将所有通知作为事件对象(PO​​NSOS)整理到该数组(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。

但是,这还不够。为了提高效率,数据源委托(delegate)确实需要与NSFRC进行交互,以将表 View 行映射到正确索引路径处的数据行,处理节信息等。因此,我要做的是创建一个称为DynamicDataSource的协议(protocol),并创建一个由Interactor初始化以“包装” NSFRC并代理其方法的实现。从技术上讲,该模型已移交给View层,但实际上它被包装在协议(protocol)后面,并且实现将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
}

如果将持久性存储更改为例如Memory Store或JSON层,则动态数据源实现可以在不影响View的情况下适本地进行处理。显然,这是使用NFRC的一种复杂方法,但是我认为这是一个有用的类。对于一个简单的应用程序来说,它可能会过分杀伤力。但是,它可行,我认为这是一个很好的,一致的折衷方案。

值得补充的是,我是Swift和IOS开发的新手,因此这可能不是世界上最好的代码,并且可能会有更好的方法!我随时欢迎反馈和改进建议。

10-07 19:48
查看更多