2017-07-27 107 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開發非常陌生,所以這可能不是世界上最好的代碼,並且可能有更好的方法來實現它!我始終樂於接受反饋和改進建議。

相關問題