1

J'ai cherché une réponse à ceci sans beaucoup de chance. Cette question est à peu près le même, mais la réponse est très claire (au moins pour moi!): Which it is the place for NSFetchedResultsController in VIPER architecture?Utilisation de NSFetchedResultsController dans l'architecture propre

Le NSFetchedResultsController semble une approche très utile pour les applications iOS, mais tous les exemples que j'ai vu ce lieu beaucoup à la couche ViewController - au moins, le VC devient un délégué. Dans une architecture propre/Viper, la couche modèle est très déconnectée de la couche View et je ne peux pas comprendre comment la NSFRC est utilisée dans une telle architecture. La réponse à la question ci-dessus implique que l'Interacteur devrait être un délégué, mais cela n'a pas de sens - les Objets gérés seraient alors adressés à l'Interacteur, plutôt qu'aux PONSO. Peut-être que je ne le comprends pas encore assez bien, mais (a) at-il une place dans une architecture propre; et (b) si c'est le cas, alors veut le bon modèle de mise en œuvre Swift?

Répondre

1

C'est ce que j'ai fait à la fin. NSFetchedResultsController (NFRC) doit être abordé de deux manières: extraction des données, c'est-à-dire exécution de la requête, et notifications des modifications apportées à l'ensemble ManagedObject (MO) via des appels de délégation.

L'extraction de données ne déclenche pas d'appels de délégation. Ainsi, vous devez normalement renvoyer les résultats de l'exécution de l'extraction, c'est-à-dire anNFRC.fetchedObjects(), ré-empaqueter en tant que PONSOS dans le worker ou l'interacteur et les transmettre au Presenter pour les transmettre à ViewController.

J'ai trouvé plus facile et tout aussi conforme d'utiliser le DataSource Delegate comme ViewController (lorsqu'une Table View fait partie de l'implémentation) - Je l'implémente en tant que classe distincte de ViewController. Cette approche maintient le cycle VIP standard et ne nécessite aucune connaissance du modèle dans la couche View.

La gestion des appels de délégués est un peu plus complexe. Le NFRC est généralement lié à la couche View pour gérer les demandes de délégués de données Table View: le NFRC notifie les modifications Insert, Delete, Move, Update et le délégué le gère de manière appropriée. Cependant, dans une architecture VIP qui ne peut pas se produire car le NFRC ne peut pas être attaché à la vue - il vit dans la couche Modèle et doit y rester.

J'instancié cela dans l'instance magasin et fait l'instance de magasin un délégué NFRC et mis en œuvre les méthodes déléguées comme:

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

Fondamentalement, j'initialiser un tableau vide (Begin mise à jour), rassembler toutes les notifications objets d'événement (PONSOS) dans ce tableau (I, D, M, U), puis exécutez un gestionnaire d'achèvement lorsque vous avez terminé (Fin mise à jour). Le gestionnaire de complétion est transmis dans le cadre de l'opération fetch() et stocké pour une utilisation future, c'est-à-dire lorsque des modifications de MO doivent être notifiées. Le gestionnaire d'achèvement est passé à travers de la Interactor et ressemble à:

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

Il passe tous les événements au présentateur qui passe au délégué Source des données qui peuvent les traiter.

Cependant, ce n'est pas suffisant. Pour être efficace, le délégué de la source de données doit interagir avec NSFRC pour mapper une ligne de vue tabulaire sur une ligne de données au bon chemin d'index, gérer les infos de section, etc. J'ai donc créé un protocole appelé DynamicDataSource et un la mise en œuvre de ce qui est initialisé par l'Interactor pour «envelopper» la NSFRC et en extraire les méthodes. Alors que le modèle est techniquement transmis à la couche View, il est en fait enveloppé derrière un protocole et l'implémentation convertit les MO en PONSOS. Donc, je le vois comme une extension (pas une extension Swift!) De la couche Presenter.

protocol ListItemsDynamicDataSource: AnyObject { 
    // MARK: - Helper methods 
    func numberOfSections() -> Int 
    func rowsInSection(_ section: Int) -> Int 
    func getItem(index: IndexPath) -> ListItems.FetchItems.ViewModel.DisplayedItem 
} 

Si un magasin de persistance a été changé pour, disons, une couche Memory Store ou JSON, puis la mise en œuvre Source de données dynamiques pourraient gérer cela de façon appropriée sans affecter la vue. Il s'agit clairement d'une façon complexe d'utiliser le NFRC, mais je pense que c'est une classe utile à utiliser. Pour une application simple, c'est probablement trop. Cependant, cela fonctionne, et je pense que c'est un bon compromis conforme. Il vaut la peine d'ajouter que je suis assez nouveau dans le développement de Swift et d'IOS, donc ce n'est peut-être pas le meilleur code du monde et il pourrait y avoir de meilleures façons de le faire! Je suis toujours ouvert aux commentaires et suggestions d'amélioration.