Fermer

juillet 5, 2022

Couche réseau avec Combine | AU NOUVEAU Blog

Couche réseau avec Combine |  AU NOUVEAU Blog


Qu’est-ce que Combiner ?

La Combiner Cadre – Un cadre annulé en 2019 qui fonctionne sur le fondement du paradigme de programmation réactive fonctionnelle (FRP) similaire à RxSwift et ReactiveSwift. Il est développé par Apple et peut être considéré comme une alternative de première partie à RxSwift et ReactiveSwift.

Lisons dans la documentation :

Personnalisez la gestion des événements asynchrones en combinant des opérateurs de traitement d’événements.

Cet article prend la liberté en supposant que nous avons la compréhension de base des trois pièces mobiles clés de Combine qui sont éditeurs, les opérateurs, les abonnés, et comment nous pouvons créer un Combiner le pipeline.

Exemple de pipeline combinéExemple de pipeline combiné

Dans cet article, nous parlerons de l’implémentation de la couche réseau à l’aide de Combiner. Pendant la mise en œuvre, nous aborderons certains sujets tels que Protocoles, Génériques, Protocoles Associatifs, et bien sûr combiner la saveur de URLSession

Avant de nous lancer dans la rédaction du Couche réseau utilisant Combine, revoyons le « Rappeler » type de couche réseau que nous avons généralement implémenté dans notre code.

Dans cette couche réseau, nous avons défini quelques protocoles qui définissent notre abstraction API. Pour mettre en évidence la La manière de combiner la couche réseauCe sera plus lié si nous refactorisons le « Rappeler » couche réseau de type (fermeture). Dans une aventure de refactoring comme celle-ci, c’est une bonne idée de commencer par changer les protocoles d’abord et de mettre à jour les implémentations plus tard.

Lien vers le protocole de demande de type de rappel

Avant de mettre à jour le protocole, passons un peu de temps à comprendre ces protocoles et leur objectif. Comme mentionné, nous allons approfondir quelques sujets rapides avancés tels que Protocoles, Génériques et protocoles associatifs.

Comme nous le voyons dans le code ci-dessus, il existe de nombreux protocoles, alors en avons-nous vraiment besoin ?? Eh bien, honnêtement, non, nous ne le faisons pas, mais nous devrions le faire si nous voulons que notre application et l’API soient découplées, ce qui nous permettra de nous en moquer facilement pour les tests.

Permettez-moi de le dire à haute voix : Protocoles sommes IMPRESSIONNANT.

Analysons maintenant les protocoles en détail :

Demande :

// Un protocole AssociatedType : type de requête à implémenter par des requêtes concrètes

Lien vers le protocole de demande de type de rappel

Ce type de demande est un protocole générique de type associé qui définit deux types associés sur lesquels opérer.

  1. Type de réponse contrainte d’être décodable
  2. Analyseur de réponse est contraint d’être de ResponseParserType où ResponseParserType est un autre type ATP sur réponse. De plus, nous contraignons le type ResponseParser pour pouvoir garantir que Parser correspond au type de réponse correct.
  3. Nous avons défini quelques propriétés requises pour la requête.
  4. Ensuite, nous avons un errorParser de ErrorParserTypeErrorParserType Type de protocole qui permet au type de demande de mise en œuvre d’utiliser un analyseur d’erreur personnalisé par rapport à celui par défaut.

Cela décrit très bien le Protocole de demande qui sera utilisé par notre code d’application pour déclarer chaque type de requête.

Mais, ce protocole reste tel quel. Il n’utilise pas le rappel, nous n’avons donc pas à le convertir pour utiliser la combinaison.

Prochain, RequestExecutor :

Ce protocole déclare l’abstraction pour l’implémentation de l’exécuteur de requête. Cela nous aidera à basculer sur l’API (Framework) en faisant des requêtes.

Marquez l’utilisation du type générique, mais permettez-moi de préciser qu’il s’agit simplement d’un protocole avec une fonction qui accepte les requêtes génériques. C’est une implémentation de effacement de type concept qui permet l’utilisation du protocole générique comme type.

Et Ceci n’est pas un ATP (protocole de type associé)

Cela fera à peu près notre API de mise en réseau. Voyons juste la mise en œuvre de RequestExecutor.

Le code est explicite car il ne fait qu’implémenter le RequestExecutor protocole.

La fonction exécuterRequête C’est là que toute la magie opère. Il prend le type de requête et le convertit en URLRequest via DemandeConvertible pour l’utiliser avec la dataTask nouvellement créée. Vous remarquerez à quel point les validations de l’utilisation du bon analyseur et du bon type de réponse sont conservées avec Type de demande et le seul travail de l’exécuteur de requête est de passer des appels réseau.

Et c’est ce dont une couche réseau évolutive aura besoin pour fonctionner.

Mais attendez, où est Combiner ?

Oui, il est temps de recommencer et Combiner.

Nous commencerons par refactoriser RequestExecutor protocole, car c’était le seul protocole de notre abstraction d’API qui utilise Rappeler (fermetures).

Lien vers les protocoles de couche réseau de Combine

Lien vers les protocoles de couche réseau de Combine

C’est quoi RequestExecutor refactor est tout ce qu’il faut pour être utilisé pour Combiner.

Les changements:

  1. Nous avons supprimé le paramètre de rappel à la place, il existe un type de retour de AnyPublisher Générique sur la réponse et l’erreur de la demande.
  2. Le refactoring a également vu l’utilisation de Clause where générique pour contraindre R taper avec DemandeConvertible. mais c’est juste un changement syntaxique induit pour rafraîchir l’utilisation de clause où.

Notez qu’il y a utilisation de AnyPublisher ce qui est commun dans Combiner avec ses propres avantages. Mais pourquoi ne pas utiliser l’éditeur spécifiquement disponible URLSession.DataTaskPublisher sur DataTask. La raison de ne pas utiliser DataTaskPublisher ou tout autre éditeur spécifique est de ne pas rendre notre exécuterRequête fonction étroitement liée à la mise en œuvre. Nous en reparlerons plus AnyPublisher dans un moment.

C’est tout avec la partie refactorisation des protocoles. La prochaine étape consiste à refactoriser l’implémentation réelle de ces abstractions.

Voyons maintenant l’utilisation simple de URLSession.DataTaskPublisher pour effectuer des requêtes d’API Web, puis nous l’ajouterons à notre Refactoring.

Tâche de données avec combiner :

Tâche de données dans Combiner

Ce pipeline définit le urlSessionPublisher et abonnez-vous en utilisant évier abonné. C’est la forme la plus basique du pipeline de la moissonneuse-batteuse pour DataTask.

Cela fait le travail de faire des requêtes sur le réseau et de nous récupérer les données, les objets de réponse URL. Mais l’utiliser dans notre implémentation est encore un long chemin.

Faisons évoluer le pipeline actuel en utilisant de superbes les opérateurs.

Cela a beaucoup évolué maintenant, parlons de ces mises à jour de notre pipeline.

  1. Nous utilisons l’opérateur de carte pour transformer la sortie (données : données, réponse : URLResponse) de urlSessionPublisher aux données en ignorant la réponse.
  2. décoder est utilisé pour charger les données et tenter de les analyser. Décoder peut lancer lui-même une erreur si le décodage échoue. Si cela réussit, l’objet transmis dans le pipeline sera la structure des données JSON.
  3. En cas d’erreur, nous la traitons ici en utilisant remplacerErreur, car nous ne voulons pas que notre pipeline (abonnement) se termine par un échec. Nous pouvons également utiliser le attraper opérateur au lieu d’utiliser replaceError. Mais il faut garder à l’esprit que même avec remplacerErreur ou attraper s’il y a une erreur dans le flux, il finira par terminer le pipeline.
  4. Utilisant eraseToAnyPublisher () pour exposer une instance de AnyPublisher à l’abonné en aval, plutôt qu’au type réel de cet éditeur. Cette forme de effacement de type préserve l’abstraction au-delà des limites de l’API, telles que différents modules. Lorsque vous exposez vos éditeurs en tant que type AnyPublisher, vous pouvez modifier l’implémentation sous-jacente au fil du temps sans affecter les clients existants.
  5. Comme nous avons géré l’erreur en utilisant remplacerErreur à l’étape 3, cette fermeture sera invoquée avec un tableau de données Posts ou un tableau vide en cas d’erreur.
  6. Il montre comment nous pouvons annuler un abonnement. (Commentez-le si vous testez le code)

Ceci est vraiment mieux. Mais qu’en est-il si nous ne voulons pas remplacer l’erreur par des données vides. Réparons ça.

Flux de traitement des requêtes Raising Error et Stricter Network :

Ici, nous allons mettre à jour le pipeline de réseau combiné créé ci-dessus pour générer des erreurs et les normaliser en erreurs que notre application comprendra.

Combine a une famille d’opérateurs préfixés try, qui acceptent une fermeture de lancement d’erreur et renvoient un nouveau éditeur. L’éditeur se termine par un échec au cas où une erreur serait renvoyée à l’intérieur d’une fermeture. Voici quelques-unes de ces méthodes : tryMap(_ :), tryFilter(_ :), tryScan(_:_ 🙂

Erreur personnalisée à utiliser pour la couche réseau

Utilisation de tryMap sur la carte et l’utilisation de carteErreur Les blocs sont la principale mise à jour de notre pipeline de traitement des demandes réseau.

  1. Avec l’opérateur tryMap, la réponse de dataTaskPublisherdataTaskPublisher est immédiatement inspecté et nous lançons une erreur de désir si le code de réponse n’est pas dans la plage.
  2. Identique au modèle ci-dessus, nous décodons la réponse, mais il y a une partie commentée, nous pouvons utiliser la fermeture tryMap avec un décodage normal et lancer l’erreur de notre choix, le cas échéant.
  3. Avec carteErreur nous convertissons tous les types d’erreurs soulevés en un type d’échec commun de APIErreur ou Erreur d’analyse

Avec les modifications ci-dessus apportées à notre pipeline, nous avons atteint la gestion des erreurs attendue.

Wooo !

C’était beaucoup sur le pipeline de traitement de données réseau de Combine.

Avec toutes ces connaissances que nous acquérons dans la dernière partie, il est temps de revenir sur notre refactorisation de la couche réseau et de les appliquer.

Nous reviendrons à notre WebApiClient classer et implémenter le refactorisé RequestExecutor protocole.

Comme je l’ai déjà expliqué, le pipeline de base à un seul coup utilisant DataTaskPublisher , tryMap , mapError . Notre WebApiClient refactorisé se terminera comme ci-dessous.

Lien vers l’implémentation RequestExecutor de Combine

Le code ci-dessus est juste une combinaison de tout ce que nous avons appris dans la gestion des erreurs et l’utilisation du mappage d’erreurs. Mais avant de continuer à expliquer l’ensemble du pipeline de la couche réseau, permettez-moi de vous rappeler ma promesse d’expliquer l’utilisation de AnyPublisher taper dessus Éditeur de tâche de données tout en refactorisant RequestExecutor protocole, le voici !!

  1. Utilisé un Échouer éditeur qui se termine immédiatement avec l’erreur spécifiée. Et je l’efface à n’importe quel éditeur. Ceci est utilisé au cas où try? request.asURLRequest() ne parvient pas à créer la demande et finit par générer une erreur. En utilisant AnyPublisher, nous sommes en mesure de renvoyer n’importe quel type d’éditeur avant même de créer l’instance de notre DataTaskPublisher.
  2. Montre simplement que nous pouvons également utiliser l’éditeur CurrentValueSubject pour émettre une erreur.
  3. Une fois que nous avons la bonne instance de demande, nous devons créer un éditeur qui effectue réellement l’appel de demande. Pour ce faire, nous créons l’instance DataTaskPublisher.
  4. Nous avons peut-être utilisé un opérateur de mappage, mais l’opération de mappage peut générer une erreur, nous utilisons donc tryMap à la place. Faisant usage de la tryMap L’opérateur nous permet de vérifier le code de réponse et si quelque chose ne va pas, nous renvoyons une erreur.
  5. Encore ici, tryMap est utilisé pour utiliser nos anciens analyseurs qui peuvent également générer des erreurs. Nous offre la possibilité d’utiliser notre analyseur personnalisé au lieu d’utiliser le Décoder opérateur que nous avons vu plus tôt.
  6. Il peut y avoir de nombreux types d’erreurs que nous pouvons rencontrer, donc pour en faire un type que notre code d’appel peut facilement gérer, nous utilisons carteErreur comme expliqué ci-dessus.
  7. Un planificateur qui nous permet de revenir à la file d’attente principale.
  8. Enfin, nous effaçons l’éditeur à n’importe quel type.

Cela nous amène à la fin de l’article mais n’hésitez pas à consulter la démo projet pour en savoir plus sur la façon dont nous finissons par utiliser cette couche réseau.

N’oubliez pas d’ajouter votre clé API dans la classe WebApiConstants. Obtenez-en un de ici.

Vérifiez également l’utilisation de Génériques, Protocoles et Injection de dépendance.




Source link