API pour l'extraction asynchrone à distance à l'aide d'Apple Combine



Combine est un framework Swift réactif fonctionnel qui a été récemment implémenté pour toutes Apple plateformes Apple , y compris Xcode 11 . Avec Combine, il est très facile de traiter des séquences de valeurs qui apparaissent de manière asynchrone dans le temps. Il simplifie également le code asynchrone en abandonnant la délégation et les rappels imbriqués complexes.

Mais étudier Combiner lui-même au début peut ne pas sembler si simple. Le fait est que les principaux «acteurs» de Combine sont des concepts abstraits tels que «éditeurs» Éditeurs , «abonnés» Abonnés et opérateurs Opérateurs , sans lesquels il ne sera pas possible d'avancer dans la compréhension de la logique de fonctionnement de Combine . Cependant, étant donné Apple fournit aux développeurs des "éditeurs", des "abonnés" et des opérateurs prêts à l'emploi, le code écrit avec Combine est très compact et bien lisible.

Vous le verrez dans l'exemple d'une application liée à la récupération asynchrone des informations sur les films de la très populaire base de données TMDb . Nous allons créer deux applications différentes: UIKit et SwiftUI , et montrer comment Combine fonctionne avec elles.



J'espère que cet article vous facilitera l'apprentissage du Combine . Le code de toutes les applications développées dans cet article se trouve sur Github .

La moissonneuse-batteuse comprend plusieurs composants principaux:

Publisher Publisher .




Éditeurs Les éditeurs sont des TYPES qui apportent des valeurs à tous ceux qui se soucient. Le concept «éditeur» de l'éditeur est implémenté dans Combine en tant que protocole , et non en tant que TYPE spécifique. Le protocole Publisher a associé des TYPES génériques pour la valeur de sortie et l'erreur d' échec .
Un «éditeur» qui ne publie jamais d'erreur utilise l'erreur TYPE Never for a Failure .





Apple fournit aux développeurs des implémentations spécifiques de «éditeurs» prêts à l'emploi: Just , Future , Empty , Deferred , Sequence , @Published , etc. Des "éditeurs" sont également ajoutés aux classes Foundation : URLSession , < NotificationCenter , Timer .

Abonné "Abonné".




C'est également un protocole protocolaire qui fournit une interface pour «s'abonner» aux valeurs d'un «éditeur». Il a associé des TYPES génériques pour les erreurs d' entrée et d' échec . Évidemment, les TYPES de Publisher Publisher et Subscriber Subscriber doivent correspondre.



Pour tout éditeur Publisher, il existe deux abonnés intégrés: regrouper et attribuer :



Le récepteur «Subscriber» est basé sur deux fermetures: une fermeture, receiveValue , est exécutée lorsque vous obtenez les valeurs , la deuxième fermeture, receiveCompletion , est exécutée lorsque la «publication» est terminée (normalement ou avec une erreur).



L' affectation «Abonné» avance chaque valeur reçue vers le key Path spécifié.

Abonnement "Abonnement".




Premièrement, l'éditeur «éditeur» crée et livre l'abonnement «Abonnement» à l' abonné « Abonné » via sa méthode de réception (abonnement :) :



Après cela, l' abonnement peut envoyer ses valeurs aux «abonnés» des abonnés en utilisant deux méthodes:



Si vous avez fini de travailler avec l' abonnement Abonnement , vous pouvez appeler sa méthode cancel () :



Sujet "Sujet".




Il s'agit d'un protocole de protocole qui fournit une interface pour les deux clients, à la fois pour «l'éditeur» et pour «l'abonné». Essentiellement, le sujet « subject » est le «publisher» Publisher , qui peut accepter la valeur d' entrée Input et que vous pouvez utiliser pour «injecter» les valeurs dans le flux en appelant la méthode send () . Cela peut être utile lors de l'adaptation du code impératif existant dans les modèles combinés .

Opérateur Opérateur .




À l'aide de l'opérateur, vous pouvez créer un nouvel «éditeur» d'éditeur à partir d'un autre «éditeur» d'éditeur en convertissant, en filtrant et même en combinant les valeurs des nombreux éditeurs en upstream précédents.



Vous voyez ici beaucoup de noms d'opérateurs familiers: compactMap , map , filter , dropFirst , append .

Éditeurs de la Fondation Éditeurs intégrés à la Fondation .


Apple fournit également aux développeurs plusieurs des fonctionnalités Combine déjà intégrées dans le framework Foundation <, c'est-à-dire les éditeurs des éditeurs, pour des tâches telles que la récupération de données à l'aide d' URLSession , l'utilisation de notifications à l'aide de Notification , Timer et les propriétés de surveillance basées sur KVO . Cette compatibilité intégrée nous aidera vraiment à intégrer le framework Combine dans notre projet actuel.
Pour en savoir plus à ce sujet, consultez l'article «Le didacticiel ultime du framework Combine dans Swift» .

Qu'apprenons-nous à faire avec Combine ?


Dans cet article, nous allons apprendre à utiliser le framework Combine pour récupérer des données de film à partir du site Web TMDb . Voici ce que nous étudierons ensemble:

  • Utiliser le futur "éditeur" pour créer une fermeture avec Promise pour une seule valeur: valeur ou erreur.
  • Utilisation de l' URL " session " de l'éditeur " datataskPublisher " pour "s'abonner" aux données publiées par un UR L. donné
  • Utilisation de l'opérateur tryMap pour convertir des données de données à l' aide d'un autre éditeur Publisher.
  • Utilisation de l'opérateur de décodage pour convertir les données de données en un objet décodable et les publier pour transmission aux éléments suivants de la chaîne.
  • Utiliser l'opérateur récepteur pour «s'abonner» à un «éditeur» d'éditeur à l'aide de fermetures.
  • Utilisez l'instruction assign pour vous «abonner» à l'éditeur «éditeur» et attribuer la valeur qu'il fournit au key Path donné.

Projet initial


Avant de commencer, nous devons nous enregistrer pour recevoir la clé API sur le site Web TMDb . Vous devez également télécharger le projet initial à partir du référentiel GitHub .
Assurez-vous de placer votre clé API dans la classe MovieStore de classe dans let apiKey constant.



Voici les principaux blocs de construction à partir desquels nous allons créer notre projet:

  • 1. À l'intérieur du fichier Movie.swift trouvent des modèles que nous utiliserons dans notre projet. La structure racine de struct MoviesResponse implémente le protocole Decodable , et nous l'utiliserons lors du décodage JSON données JSON dans un modèle. La structure MoviesResponse a une propriété results , qui implémente également le protocole Decodable et est une collection de films [Movie] . C'est elle qui nous intéresse:



  • 2. Énumération énumération MovieStoreAPIError implémente le protocole d' erreur . Notre API utilisera cette énumération pour représenter différents types d'erreurs: erreurs de récupération d' URL urlError , erreurs de décodage decodingError et erreurs d'extraction de données responseError .



  • 3. Notre API possède un protocole MovieService avec une seule méthode, fetchMovies (de endpoint: Endpoint) , qui sélectionne les films [Movie] en fonction du paramètre de endpoint . Le point de terminaison lui-même est un enum enum qui représente un point de terminaison pour accéder à la base de données TMDb pour récupérer des films comme nowPlaying (dernier), populaire (populaire), topRated (haut) et à venir (à venir bientôt à l'écran).



  • 4. La classe MovieStore est une classe spécifique qui implémente le protocole MovieService pour récupérer des données à partir du site TMDb . Dans cette classe, nous implémentons la méthode fetchMovies (...) en utilisant Combine .



  • 5. La classe MovieListViewController est la principale classe ViewController dans laquelle nous utilisons la méthode Sink pour «souscrire» à la méthode de lecture de film fetchMovies (...) , qui renvoie le futur «éditeur», puis mettre à jour la table TableView avec de nouvelles données de film films utilisant la nouvelle API DiffableDataSourceSnapshot .

Avant de commencer, examinons certains des composants de base de Combine que nous utiliserons pour API récupération de données à distance.

«Abonnez-vous» à «l'éditeur» en utilisant évier et ses fermetures.


Le moyen le plus simple de «souscrire» à «l'éditeur» de l'éditeur est d'utiliser le récepteur avec ses fermetures, dont l'une sera exécutée chaque fois que nous obtiendrons une nouvelle valeur , et l'autre lorsque «l'éditeur» aura fini de livrer les valeurs .



N'oubliez pas que dans Combine, chaque «abonnement» renvoie une annulation , qui sera supprimée dès que nous quitterons notre contexte. Afin de maintenir un «abonnement» pendant une période plus longue, par exemple, pour obtenir des valeurs de manière asynchrone, nous devons enregistrer «l'abonnement» dans la propriété subscription1 . Cela nous a permis d'obtenir systématiquement toutes les valeurs (7,8,3,4) .

Future «publie» de façon asynchrone une seule valeur: soit une valeur, soit une erreur d' échec .


Dans le cadre Combine , le futur «éditeur» peut être utilisé pour obtenir de manière asynchrone un seul TYPE de résultat à l' aide d'une fermeture. La fermeture a un paramètre - Promise , qui est une fonction de TYPE (Result <Output, Failure>) -> Void .

Regardons un exemple simple pour comprendre comment fonctionne le futur éditeur:



Nous créons Future avec un résultat réussi de TYPE Int et une erreur de TYPE Never . Dans la fermeture Future , nous utilisons DispatchQueue.main.asyncAfter (...) pour retarder l'exécution du code de 2 secondes, simulant ainsi un comportement asynchrone. À l'intérieur de la fermeture, nous renvoyons Promise avec un résultat de promesse réussi (.success (...)) en tant que valeur entière aléatoire Int comprise entre 0 et 100 . Ensuite, nous utilisons deux abonnements futurs - annulable et annulable1 - et les deux donnent le même résultat, bien qu'un nombre aléatoire soit généré à l'intérieur.
1. Il convient de noter que l '«éditeur» de Future in Combine présente certaines caractéristiques comportementales par rapport aux autres «éditeurs»:

  • Le futur "éditeur" publie toujours "UNE valeur ( valeur ou erreur), et ceci termine son travail.

  • Le futur «éditeur» est une classe ( reference type ), contrairement aux autres «éditeurs», qui sont principalement des structures struct ( value type ), et il est transmis en tant que paramètre une fermeture de promesse , qui est créée immédiatement lors de l'initialisation de l'instance d'éditeur futur . C'est-à-dire que la clôture de la promesse est transmise avant que tout abonné « abonné » ne s'abonne à la future instance «éditeur». L '«éditeur» de Future n'a absolument pas besoin d'un «abonné» pour son fonctionnement, comme tous les autres éditeurs ordinaires l'exigent. C'est pourquoi le texte «Bonjour de l'intérieur du futur!» N'est imprimé qu'une seule fois dans le code ci-dessus.

  • Le futur "éditeur" est un "éditeur" eager (impatient), contrairement à la plupart des autres "éditeurs" paresseux ("publier" uniquement s'il y a un "abonnement"). Ce n'est qu'une fois que le futur éditeur a fermé sa promesse que le résultat est mémorisé et ensuite transmis aux «abonnés» actuels et futurs. D'après le code ci-dessus, nous voyons que lorsque vous vous «abonnez» à plusieurs reprises au futur éditeur, vous obtenez toujours la même valeur «aléatoire» (dans ce cas 6 , mais elle peut être différente, mais toujours la même), bien qu'elle soit utilisée dans la fermeture valeur int aléatoire.

Une telle logique de «l'éditeur» de Future permet de l'utiliser avec succès pour stocker un résultat calculé asynchrone consommateur de ressources et de ne pas perturber le «serveur» pour les multiples «abonnements» ultérieurs.

Si une telle logique de l '«éditeur» de Future ne vous convient pas et que vous voulez que votre Future soit appelé paresseux et chaque fois que vous obtenez de nouvelles valeurs Int aléatoires, alors vous devriez «boucler» Future dans Deferred :



Nous utiliserons Future de manière classique, comme suggéré dans Combine , c'est-à-dire comme un «éditeur» asynchrone calculé «partagé».

Remarque 2. Nous devons faire une dernière remarque concernant l '«abonnement» utilisant le récepteur au «Publisher» asynchrone. La méthode Sink renvoie AnyCancellable , dont nous ne nous souvenons pas constamment. Cela signifie que Swift détruira AnyCancellable au moment où vous quitterez le contexte donné, ce qui se produit sur le main thread . Ainsi, il s'avère que AnyCancellable est détruit avant que la fermeture avec Promise ne puisse démarrer sur le main thread . Lorsque AnyCancellable est détruit, sa méthode d' annulation est appelée, ce qui annule dans ce cas «l'abonnement». C'est pourquoi nous nous souvenons de nos «abonnements» de puits au futur dans les variables annulables <et annulables1 ou dans Set <AnyCancellable> () .


Utilisation de Combine pour récupérer des films sur le site Web de TMDb .


Pour commencer, vous ouvrez le projet de démarrage et accédez au fichier MovieStore.swift et à la méthode fetchMovies avec une implémentation vide:



En utilisant la méthode fetchMovies, nous pouvons sélectionner différents films en définissant des valeurs spécifiques pour le paramètre d' entrée de point final de TYPE Endpoint . TYPE Endpoint est une énumération d' énumération qui prend les valeurs nowPlaying (actuel), à venir (bientôt à l'écran), populaire (populaire), topRated (haut):



Commençons par initialiser Future avec une fermeture de callback . Reçu Future, nous vous rembourserons ensuite.



À l'intérieur de la fermeture de callback , nous générons l' URL de la valeur correspondante du paramètre d' entrée de point final en utilisant la fonction generateURL (avec endpoint: Endpoint) :



Si l' URL correcte n'a pas pu être générée, nous renvoyons une erreur à l'aide de promise (.failure (.urlError (...)) , sinon nous allons de l'avant et implémentons l' URL « session éditeur» URLSession.dataTaskPublisher .

Pour "souscrire" aux données d'une certaine URL nous pouvons utiliser la méthode datataskPublisher intégrée à la classe URLSession , qui prend l' URL comme paramètre et renvoie l'éditeur "publisher" avec les données de sortie du tuple (data: Data, response: URLResponse) et une erreur URLError .



Pour convertir un éditeur Publisher vers un autre éditeur Publisher, utilisez l'opérateur tryMap . Par rapport à la carte , l'opérateur tryMap peut renvoyer une erreur Error throws à l' intérieur d'une fermeture, ce qui nous renvoie le nouvel éditeur Publisher.

Dans l'étape suivante, nous utiliserons l'opérateur tryMap pour vérifier le code http statusCode de la réponse de réponse pour nous assurer que sa valeur est comprise entre 200 et 300 . Sinon, nous lançons la valeur d'erreur d' énumération MovieStoreAPIError enum responseError . Sinon (lorsqu'il n'y a pas d'erreur), nous renvoyons simplement les données reçues au prochain éditeur de la chaîne «éditeur».



Dans l'étape suivante, nous utiliserons l'opérateur de décodage , qui décode les données JSON sortie du tryMap «éditeur» précédent dans MovieResponse <Model à l'aide de JSONDecoder .



... jsonDecoder est configuré pour un format de date spécifique:



Pour que le traitement soit effectué sur le main thread , nous utiliserons l'opérateur receive (on :) et passerons RunLoop.main comme paramètre d'entrée. Cela permettra à "l'abonné" d'obtenir la valeur de valeur sur le thread main .



Enfin, nous sommes arrivés à la fin de notre chaîne de transformation, et là, nous utilisons le puits pour obtenir un abonnement « abonnement » à la «chaîne» formée d'éditeurs «éditeurs». Pour initialiser une instance de la classe Sink , nous avons besoin de deux choses, bien que l'une d'entre elles soit facultative:

  • fermeture recevoirValeur :. Il sera appelé chaque fois qu'un abonnement "abonnement" reçoit une nouvelle valeur de l'éditeur "éditeur".

  • Clôture de la réception: (Facultatif). Il sera appelé une fois que l' éditeur «éditeur» a fini de publier la valeur , il recevra l'énumération d' achèvement , que nous pouvons utiliser pour vérifier si la «publication» des valeurs est vraiment terminée ou si l'achèvement est dû à une erreur.

Dans la fermeture de receiveValue , nous invoquons simplement une promesse avec une option .success et une valeur de 0 $ .résultats , qui dans notre cas est un tableau de films films . Dans la fermeture de receiveCompletion, nous vérifions si l' achèvement comporte une erreur d' erreur , puis transmettons l'erreur de promesse correspondante avec l'option .failure .



Notez que nous collectons ici toutes les erreurs «rejetées» dans les étapes précédentes de la «chaîne des éditeurs».

Ensuite, nous mémorisons l'abonnement «souscription» dans la propriété Set <AnyCancellable> () .
Le fait est que l'abonnement «abonnement» est annulable , c'est un tel protocole qui détruit et efface tout après l'achèvement de la fonction fetchMovies . Pour garantir la conservation de l'abonnement «abonnement» même après l'achèvement de cette fonction, il faut se souvenir de l'abonnement « abonnement » dans la variable externe à la fonction fetchMovies . Dans notre cas, nous utilisons la propriété subscriptions , qui a le type Set <AnyCancellable> () et utilisons la méthode .store (in: & self.subscriptions) , qui garantit la convivialité de l ' «abonnement» une fois que la fonction fetchMovies a terminé son travail.



Ceci conclut la formation de la méthode fetchMovies pour sélectionner des films dans la base de données TMDb en utilisant le framework Combine . La méthode fetchMovies , en tant que paramètre d'entrée de, prend la valeur de l' énumération enum Endpoint , c'est-à-dire quels films spécifiques nous intéressent:

.nowPlaying - films actuellement à l'écran,
.upcoming - films à venir,
.popular - films populaires,
.topRated - meilleurs films, c'est-à-dire avec une cote très élevée.
Essayons d'appliquer cette API à la conception d'applications avec les interfaces utilisateur UIKit habituelles sous la forme d'un Table View Controller :



et à une application dont l'interface utilisateur est construite en utilisant le nouveau framework déclaratif SwiftUI :



"Abonnez-vous" aux films des films View Controller habituels.


Nous passons au fichier MovieListViewController.swift et dans la méthode viewDidLoad appelons la méthode fetchMovies .



Dans notre méthode fetchMovies, nous utilisons le movieAPI développé précédemment et sa méthode fetchMovies avec le paramètre .nowPlaying comme point de terminaison du paramètre d' entrée from . Autrement dit, nous choisirons les films qui sont actuellement sur les écrans des salles de cinéma.



La méthode movieAPI.fetchMovies (from: .nowPlaying) renvoie le futur "éditeur", auquel nous "souscrivons" à l'aide de sink , et lui fournissons deux fermetures. Dans la fermeture de receiveCompletion , nous vérifions s'il y a une erreur d' erreur et affichons un avertissement d'urgence à l'alerte de l'utilisateur avec le message d'erreur affiché.



Dans la fermeture receiveValue, nous appelons la méthode generateSnapshot et lui passons les films sélectionnés.



La fonction generateSnapshot génère un nouveau NSDiffableDataSourceSnapshot à l' aide de nos films et applique l' instantané résultant au diffableDataSource de notre table.

Nous lançons l'application et observons comment UIKit fonctionne avec les «éditeurs» et les «abonnés» du framework Combine . Il s'agit d'une application très simple qui ne vous permet pas de syntoniser diverses collections de films - désormais présentés à l'écran, populaires, très appréciés ou qui vont apparaître à l'écran dans un avenir proche. Nous ne voyons que les films qui vont apparaître à l'écran (. À venir ). Bien sûr, vous pouvez le faire en ajoutant un élément d' UI pour définir les valeurs de l'énumération de point final , par exemple, à l'aide de Stepper ou de Segmented Control , puis mettez à jour l'interface utilisateur. C'est bien connu, mais nous ne le ferons pas dans une application basée sur UIKit , mais laissons cela au nouveau cadre déclaratif SwiftUI .
Le code d'une application basée sur UIKit se trouve sur Github dans le CombineFetchAPICompleted-UIKit .

Utilisez SwiftUI pour afficher des films films

.
CombineFetchAPI-MY SwiftUI File -> New -> Project Single View App iOS :



UISwiftUI :



Movie.swift Model , TMDb MovieStore.swift , MovieStoreAPIError.swift MovieService.swift , MovieService Protocol :



SwiftUI , Codable , JSON , Identifiable , si l' on veut faciliter l'affichage de la liste des films [Film] sous la forme d'une liste d' une liste . Les modèles dans SwiftUI n'ont pas besoin d'être équitables et hashable , comme requis par UIKit API pour UITableViewDiffableDataSource dans l' application UIKit précédente . Par conséquent, nous supprimons de la structure < struct Movie toutes les méthodes associées aux protocoles Equatable et Hashable :


............................


Il existe un excellent article identifiable qui montre la différence et les similitudes entre les Swiftprotocoles.Identifiable , hashable et équitable .

L'interface utilisateur que nous créerons à l'aide de SwiftUI est basée sur le fait que nous changeons le point de terminaison lors de la récupération des données et obtenons la collection de films dont nous avons besoin, qui est présentée sous la forme d'une liste:



ainsi que dans le cas d' UIKit , les données sont échantillonnées à l'aide de la fonction movieAPI .fetchMovies (from endpoint: Endpoint) , qui obtient le point de terminaison souhaité et renvoie le "publisher" Future <[Movie, MovieStoreAPIError]> . Si nous jetons un œil à l'énumération Endpoint , nous verrons que nous pouvons initialiser l'option souhaitéecase Endpoint index :



, movies , indexEndpoint Endpoint . View Model , MoviesViewModel , ObservableObject . MoviesViewModel.swift View Model :



@Published : @Published var indexEndpoint: Int — , @Published var movies: [Movie] - . @Published indexEndpoint , indexEndpoint , $indexEndpoint .
MoviesViewModel «» $indexEndpoint «» AnyPublisher<[Movie], Never> , movieAPI.fetchMovies (from: Endpoint (index: indexPoint)) flatMap .



Ensuite, nous nous «abonnons» à cet «éditeur» nouvellement reçu en utilisant un assing «abonné» très simple (à: \ .movies, on: self) et attribuons la valeur reçue de «l'éditeur» au tableau de sortie des films . Nous pouvons appliquer l' assing «abonnement» (à: \ .movies, on: self) uniquement si «l'éditeur» ne renvoie pas d'erreur, c'est-à-dire qu'il a un TYPE d'erreur Never . Comment y parvenir? Utilisation de l' opérateur replaceError (avec: []) , qui remplace toute erreur par un tableau de films vide de films .

Autrement dit, la première version plus simple de notre application SwiftUI n'affichera pas d'informations sur les erreurs possibles pour l'utilisateur.

, View Model , UI . ContentView.swift View Model @EnvironmentObject var moviesViewModel Text(«Hello, World!»)
Text("\(moviesViewModel.indexEndpoint)") , indexEndpoint .



View Model indexEndpoint = 2 , , ( Upcoming ):



UIÉléments pour contrôler la collection de films que nous voulons montrer. Voici Stepper :



... et Picker : les



deux utilisent le "publisher" $ moviesViewModel.indexEndpoint de la nôtre View Model, et avec l'un d'eux (de toute façon, lequel) nous pouvons sélectionner la collection de films dont nous avons besoin:



Ensuite, nous ajoutons la liste des films reçus en utilisant List et ForEach et des attributs minimaux film lui-même film :



Liste des films affichés filmsViewModel.movies que nous prenons également du nôtre View Model:



Nous N'utilisons PAS le $ publisher $ moviesViewModel.movies avec le signe $, car nous n'allons rien éditer dans cette liste de films. Nous utilisons la propriété habituelle moviesViewModel.movies .

Vous pouvez rendre la liste de films plus intéressante en affichant dans chaque ligne de la liste l'affiche du film correspondant, URLqui est présentée dans la structure du film :



Nous empruntons cette opportunité à Thomas Ricouard de son beau projet MovieSwiftUI .

Comme dans le cas du chargement de films , pour l'image UIImage , nous avons le service ImageService , qui implémente la méthode fetchImage avec CombineDe retour « éditeur» AnyPublisher <?, un UIImage Never> :



... et ImageLoader de classe finale: ObservableObject , met en œuvre le protocole ObservableObject avec @Published propriété d' image: UIImage? :



La seule condition requise par le protocole ObservableObject est l'existence de la propriété objectWillChange . SwiftUI utilise cette propriété pour comprendre que quelque chose a changé dans l'instance de cette classe et dès que cela se produit, met à jour toutes les vues qui dépendent de l'instance de cette classe. En règle générale, le compilateur crée automatiquement une propriété objectWillChange et toutes les propriétés @Published la notifient également automatiquement. Dans le cas de certaines situations exotiques, vous pouvez créer manuellement un objectWillChange et le notifier des modifications. Nous avons juste un tel cas.
Dans la classe ImageLoader @Published var image:UIImage? . , ImageLoader , «» $image «» loadImage() , poster size @Published var image:UIImage? .Nous informerons objectWillChange de ces modifications .
Nous pouvons avoir beaucoup de ces images dans le tableau, ce qui entraîne des coûts de temps importants, nous utilisons donc la mise en cache des instances imageLoader de la classe ImageLoader :



Nous avons une vue spéciale pour lire l' affiche du film MoviePosterImage :



... et nous l'utiliserons lors de l'affichage d'une liste de films dans notre ContentView principal :





Le code de l' application basée sur SwiftUI sans affichage d'erreur peut être trouvé sur Github dans le dossier CombineFetchAPI-NOError.

Afficher les erreurs de récupération de film asynchrone à distance.


Jusqu'à présent, nous n'avons pas utilisé ou affiché d'erreurs qui se produisent lors de la sélection asynchrone à distance de films sur le site Web de TMDb . Bien que la fonction movieAPI.fetchMovies (de endpoint: Endpoint) que nous utilisons nous permette de le faire, car elle renvoie le "publisher" Future <[Movie, MovieStoreAPIError]> .

Afin de permettre des erreurs, ajouter à notre View Modelautre @Published propriété moviesError: MovieStoreAPIError? cela représente l'erreur. Il s'agit d'une propriété facultative , sa valeur initiale est nil , ce qui correspond à l'absence d'erreur:



pour obtenir cette erreur moviesError , MoviesViewModel «» sink :



moviesError UI , nil



AlertView :



, API :



SwiftUI Github CombineFetchAPI-Error .

, Future<[Movie],MovieStoreAPIError> , AnyPublisher <[Movie], Never> dans la méthode fetchMoviesLight :



L'absence d'erreurs ( Never ) nous permet d'utiliser une assignation "abonné" très simple (à: \ .movies, on: self) :



Tout fonctionnera comme avant:



Conclusion


L'utilisation du framework Combine pour traiter une séquence de valeurs apparaissant de manière asynchrone dans le temps est très simple et facile. Les opérateurs proposés par Combine sont puissants et flexibles. Combine nous permet d'éviter d'écrire du code asynchrone complexe en utilisant la chaîne en amont des éditeurs Éditeurs , en utilisant des opérateurs et des abonnés intégrés . La moissonneuse-batteuse est construite à un niveau inférieur à Foundation , dans de nombreux cas n'a pas besoin de Foundation et a des performances incroyables.



SwiftUI est également fortement lié à Combine<grâce à ses @ObservableObject , @Binding et @EnvironmentObject .
iOSles développeurs attendent depuis longtemps Applece genre de cadre officiel, et finalement cette année, c'est arrivé.

Références:

Récupérer l'API Remote Async avec Apple Combine Framework
essayez! Swift NYC 2019 - Premiers pas avec Combine
"Le tutoriel ultime du framework Combine dans Swift".

Combine: Programmation asynchrone avec Swift

Présentation de Combine - WWDC 2019 - Vidéos - Développeur Apple. session 722
(synopsis de la session 722 "Introduction to Combine" en russe)

Combine in Practice - WWDC 2019 - Vidéos - Apple Developer. session 721
(synopsis de la session 721 "Utilisation pratique de Combine" en russe)

SwiftUI & Combine: Together is better. Pourquoi SwiftUI et Combine vous aident à créer de meilleures applications.

MovieSwiftUI .

Visualisez Combine Magic avec SwiftUI Partie 1 ()
Visualize Combine Magic with SwiftUI – Part 2 (Operators, subscribing, and canceling in Combine)
Visualize Combine Magic with SwiftUI Part 3 (See Combine Merge and Append in Action)
Visualize Combine Magic with SwiftUI — Part 4

Visualize Combine Magic with SwiftUI — Part 5

Getting Started With the Combine Framework in Swift
Transforming Operators in Swift Combine Framework: Map vs FlatMap vs SwitchToLatest
Combine's Future
Using Combine
URLSession and the Combine framework

Source: https://habr.com/ru/post/fr476678/


All Articles