2017-07-27 46 views
1

我已经搜索了一个没有太多运气的答案。这个问题几乎是相同的,但答案不是很清楚(至少对我来说!): Which it is the place for NSFetchedResultsController in VIPER architecture?在Clean Architecture中使用NSFetchedResultsController

的NSFetchedResultsController似乎是iOS应用程序非常有用的方法,但所有的例子我见过这个地方非常多的ViewController层 - 至少,VC成为一个委托。在Clean Architecture/Viper中,模型层与View层非常分离,我无法弄清楚在这样的体系结构中如何使用NSFRC。对上述问题的回答意味着Interactor应该是一个代表,但这没有意义 - 管理对象随后将呈现给Interactor,而不是PONSO。或许我还没有很好地理解它,但是(a)它在干净的建筑中是否有一席之地;和(b)如果它确实需要正确的Swift实现模式?

回答

1

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

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

我发现它更容易,就像使用DataSource委托作为ViewController一样(当Table View是实现的一部分时) - 我将它作为ViewController的单独类实现。

该方法保持标准的VIP循环,并且不需要视图层中的模型知识。

处理委托调用有点棘手。 NFRC通常绑定到View层以处理表视图数据委托请求:NFRC通知插入,删除,移动,更新更改,委托对其进行适当处理。然而,在一个不能发生的VIP体系结构中,因为NFRC无法附加到视图 - 它生活在模型层,需要留在那里。

我在存储实例实例化此并把存储实例的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) 
     } 
    } 

基本上,我初始化空数组(开始更新),整理所有的通知作为事件对象(PO​​NSOS)插入该数组(I,D,M,U),然后在完成时运行完成处理程序(结束更新)。完成处理程序作为fetch()操作的一部分传入并存储以供将来使用 - 即用于何时需要通知MO更改。 完成处理是通过从交互器过去了,看起来像:

func processFetchResults(itemChangeEvents: [ItemChangeEvent]) { 
    let response = ListItems.FetchItems.Response(itemEvents: itemChangeEvents) 
    presenter?.presentFetchedItems(response: response) 
} 

所以它传递的所有事件,其传递到数据源代表可以处理他们的演示。

但是这还不够。为了提高效率,Data Source委托人确实需要与NSFRC进行交互,以便将表视图行映射到正确索引路径中的数据行,处理部分信息等。因此,我所做的是创建一个名为DynamicDataSource的协议,实施由Interactor初始化的“包装”NSFRC并代理其方法。虽然模型技术上交给了View层,但它实际上被封装在一个协议后面,实现将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开发非常陌生,所以这可能不是世界上最好的代码,并且可能有更好的方法来实现它!我始终乐于接受反馈和改进建议。

相关问题