1

私は多くの運が無かったので、これに対する答えを探しました。この質問はほとんど同じですが、答えは非常に明確ではありません(少なくとも私に!):NSFetchedResultsControllerは、iOSアプリケーションのための非常に有用なアプローチのように思える Which it is the place for NSFetchedResultsController in VIPER architecture?クリーンアーキテクチャーでのNSFetchedResultsControllerの使用

が、すべての例では、私は場所にこれを見てきましたViewControllerレイヤーでは、少なくともVCはデリゲートになります。 Clean Architecture/Viperでは、モデルレイヤーはViewレイヤーから非常に切り離されています。このようなアーキテクチャでNSFRCがどのように使われているのか分かりません。上記の質問に対する答えは、インタラクタが代理人でなければならないが、それは意味をなさないことを意味し、管理対象オブジェクトはPONSOではなくインタラクタに表示されます。多分私はそれを十分に理解していないかもしれませんが、(a)それはきれいな建築の場所を持っていますか? (b)そうであれば、適切なSwiftの実装パターンが必要ですか?

答えて

1

これは私が最後にしたものです。 NSFetchedResultsController(NFRC)には、データを取得すること、つまりクエリを実行すること、および代理コールを介してManagedObject(MO)に変更を通知することの2つの方法でアプローチする必要があります。

データを取得しても、代理コールが発生しません。そのため、通常はフェッチを実行した結果、つまりNFRC.fetchedObjects()、PONSOSを作業者またはインタラクタに再パッケージし、これらをPresenterに渡してViewControllerに渡します。

DataSource DelegateをViewController(テーブルビューが実装の一部である場合)として使用するのがより簡単で、それと同様に、ViewControllerの別のクラスとして実装しました。

このアプローチでは、標準VIPサイクルが維持され、ビューレイヤにモデルに関する知識は必要ありません。

デリゲートコールの処理はやや難解です。 NFRCは、挿入、削除、移動、更新の変更を通知し、デリゲートがそれを適切に処理することをNFRCが通知するため、通常、Viewレイヤに関連付けられています。しかし、NFRCをViewにアタッチすることができないVIPアーキテクチャでは、Modelレイヤーに存在し、そこにとどまる必要があります。

私は、ストアインスタンスでこれをインスタンス化などの店舗インスタンスに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変更を通知する必要があるときに格納されます。 完了ハンドラは、インターアクターから通過してのように見えるされています

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

だから、それらを処理することができ、データソースデリゲートに渡すプレゼンターにすべてのイベントを渡します。

しかし、これでは不十分です。効率を上げるために、データソースデリゲートは実際にNSFRCと対話して、テーブルビュー行を正しいインデックスパスのデータ行にマッピングし、セクション情報などを処理する必要があります。したがって、私はDynamicDataSourceと呼ばれるプロトコルを作成しました。インタラクタがNSFRCを「ラップ」し、そのメソッドをプロキシするために初期化されたものの実装。モデルは技術的にViewレイヤーに渡されますが、実際にはプロトコルの後ろにラップされ、実装はMOをPONSOSに変換します。プレゼンターレイヤーの拡張子(スウィフト拡張子ではありません)を参照してください。永続ストアに変更された場合は

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の開発にかなり新しいので、これは世界でも最高のコードではないかもしれないし、それを行う良い方法があるかもしれません。私は常に改善のためのフィードバックや提案をしています。

関連する問題