API para recuperación asíncrona remota utilizando Apple Combine



Combine es un marco funcional Swift reactivo que se ha implementado recientemente para todas Apple plataformas de Apple , incluido Xcode 11 . Con Combine, es muy fácil procesar secuencias de valores que aparecen de forma asincrónica a lo largo del tiempo. También simplifica el código asincrónico al abandonar la delegación y las devoluciones de llamada complejas anidadas.

Pero estudiar Combinar en sí mismo al principio puede no parecer tan simple. El hecho es que los principales "jugadores" de Combine son conceptos tan abstractos como "editores" Editores , "suscriptores" Suscriptores y operadores Operadores , sin los cuales no será posible avanzar en la comprensión de la lógica de funcionamiento de Combine . Sin embargo, debido al hecho de que Apple proporciona a los desarrolladores "editores", "suscriptores" y operadores ya preparados, el código escrito con Combine es muy compacto y fácil de leer.

Verá esto en el ejemplo de una aplicación relacionada con la recuperación asincrónica de información de películas de la muy popular base de datos TMDb ahora . Crearemos dos aplicaciones diferentes: UIKit y SwiftUI , y mostraremos cómo funciona Combine con ellas.



Espero que este artículo te facilite aprender Combinar . El código para todas las aplicaciones desarrolladas en este artículo se puede encontrar en Github .

Combine tiene varios componentes principales:

Editor Editor .




Editores Los editores son TIPOS que entregan valores a todos los que se preocupan. El concepto de "editor" del editor se implementa en Combine como un protocolo , no como un TIPO específico. El protocolo del editor tiene TIPOS genéricos asociados para el valor de salida y el error de falla .
Un "editor" que nunca publica un error utiliza el error TIPO Nunca para un error.





Apple proporciona a los desarrolladores implementaciones específicas de "editores" ya preparados: Just , Future , Empty , Deferred , Sequence , @Published , etc. Los "editores" también se agregan a las clases de Foundation : URLSession , < NotificationCenter , Timer .

"Suscriptor" Suscriptor .




También es un protocolo de protocolo que proporciona una interfaz para "suscribirse" a los valores de un "editor". Ha asociado TIPOS genéricos para errores de entrada y falla . Obviamente, los TIPOS del publicador editor y suscriptor deben coincidir.



Para cualquier editor de editor, hay dos suscriptores integrados: hundir y asignar :



El sumidero "Suscriptor" se basa en dos cierres: un cierre, recibirValor , se ejecuta cuando obtiene los valores , el segundo cierre, recibirCompleción , se ejecuta cuando se completa la "publicación" (normalmente o con un error).



La asignación "Suscriptor", avanza cada valor recibido, hacia la key Path especificada.

Suscripción "Suscripción".




Primero, el Editor "editor" crea y entrega la Suscripción " Suscripción " al Suscriptor " Suscriptor " a través de su método de recepción (suscripción :) :



Después de eso, la Suscripción puede enviar sus valores a los "suscriptores" de los Suscriptores utilizando dos métodos:



Si ha terminado de trabajar con la Suscripción Suscripción , puede llamar a su método cancel () :



Asunto "Asunto".




Este es un protocolo de protocolo que proporciona una interfaz para ambos clientes, tanto para el "editor" como para el "suscriptor". Esencialmente, el Asunto "sujeto" es el Editor "editor", que puede aceptar el valor de entrada Entrada y que puede usar para "inyectar" los valores en la secuencia llamando al método send () . Esto puede ser útil al adaptar el código imperativo existente en Combine Models.

Operador Operador .




Con el operador, puede crear un nuevo "editor" de editor a partir de otro "editor" de editor convirtiendo, filtrando e incluso combinando los valores de los muchos publicadores anteriores.



Aquí puede ver muchos nombres de operadores conocidos: compactMap , map , filter , dropFirst , append .

Editores de la Fundación Editores integrados en la Fundación .


Apple también proporciona a los desarrolladores varias de las funciones combinadas ya integradas en el marco Foundation <, es decir, los editores de los editores, para tareas como obtener datos usando URLSession , trabajar con notificaciones usando Notification , Timer y monitorear propiedades basadas en KVO . Esta compatibilidad incorporada realmente nos ayudará a integrar el marco Combine en nuestro proyecto actual.
Para obtener más información sobre esto, consulte el artículo "El último tutorial de marco de combinación en Swift" .

¿Qué aprendemos a hacer con Combine ?


En este artículo, aprenderemos cómo usar el marco Combinar para obtener datos de películas del sitio web TMDb . Esto es lo que estudiaremos juntos:

  • Uso del futuro "editor" para crear un cierre con Promise para un solo valor: valor o error.
  • Uso de la URLSession.datataskPublisher de "editor" para "suscribirse" a los datos de datos publicados por un UR L.
  • Usando el operador tryMap para convertir datos de datos usando otro editor Publisher.
  • Usar el operador de decodificación para convertir datos de datos en un objeto decodificable y publicarlos para su transmisión a elementos posteriores de la cadena.
  • Uso del operador de sumidero para "suscribirse" a un editor "editor" mediante cierres.
  • Utilice la instrucción de asignación para "suscribirse" al editor "publicador" y asigne el valor que proporciona a la key Path dada.

Proyecto inicial


Antes de comenzar, debemos registrarnos para recibir la clave API en el sitio web TMDb . También debe descargar el proyecto inicial desde el repositorio de GitHub .
Asegúrese de colocar su clave API en la clase MovieStore clase en let apiKey constante.



Estos son los principales bloques de construcción a partir de los cuales crearemos nuestro proyecto:

  • 1. Dentro del archivo Movie.swift hay modelos que utilizaremos en nuestro proyecto. La estructura raíz de struct MoviesResponse implementa el protocolo Decodable , y lo usaremos cuando decodifiquemos datos JSON en un Modelo. La estructura MoviesResponse tiene una propiedad de resultados , que también implementa el protocolo Decodable y es una colección de películas [Movie] . Es ella quien nos interesa:



  • 2. Enumeración de enumeración MovieStoreAPIError implementa el protocolo de error . Nuestra API utilizará esta enumeración para representar varios tipos de errores: errores de recuperación de URL urlError , errores de decodificación de decodificación de error y errores de recuperación de datos de respuestaError .



  • 3. Nuestra API tiene un protocolo MovieService con un único método, fetchMovies (from endpoint: Endpoint) , que selecciona películas [Movie] en función del parámetro de punto final . El punto final en sí mismo es un enum enum que representa un punto final para acceder a la base de datos TMDb para buscar películas como nowPlaying (más reciente), popular (popular), topRated (arriba) y próximamente (próximamente en la pantalla).



  • 4. La clase MovieStore es una clase específica que implementa el protocolo MovieService para obtener datos del sitio TMDb . Dentro de esta clase, implementamos el método fetchMovies (...) usando Combine .



  • 5. La clase MovieListViewController es la clase principal de ViewController en la que utilizamos el método de sumidero para "suscribirnos" al método fetchMovies (...) película, que devuelve el "publicador" Futuro , y luego actualiza la tabla TableView con nuevos datos de película películas con la nueva API DiffableDataSourceSnapshot .

Antes de comenzar, veamos algunos de los componentes básicos de Combine que usaremos para API recuperación remota de datos.

"Suscríbete" al "editor" usando el sumidero y sus cierres.


La forma más fácil de "suscribirse" al "editor" del editor es utilizar sumidero con sus cierres, uno de los cuales se ejecutará cada vez que obtengamos un nuevo valor , y el otro cuando el "editor" termine de entregar los valores .



Recuerde que en Combine, cada "suscripción" devuelve un Cancelable , que se eliminará tan pronto como abandonemos nuestro contexto. Para mantener una "suscripción" durante más tiempo, por ejemplo, para obtener valores de forma asincrónica, necesitamos guardar la "suscripción" en la propiedad subscribing1 . Esto nos permitió obtener consistentemente todos los valores (7,8,3,4) .

Futuro asincrónicamente "publica" un único valor: valor o error de falla .


En el marco Combinar , el futuro "editor" se puede utilizar para obtener de forma asincrónica un TIPO de resultado único mediante un cierre. El cierre tiene un parámetro: Promesa , que es una función de TIPO (Resultado <Salida, Falla>) -> Anulado .

Veamos un ejemplo simple para entender cómo funciona el futuro editor:



Creamos Future con un resultado exitoso de TYPE Int y un error de TYPE Never . Dentro del cierre Future , utilizamos DispatchQueue.main.asyncAfter (...) para retrasar la ejecución del código en 2 segundos, simulando así un comportamiento asincrónico. Dentro del cierre, devolvemos Promise con un resultado exitoso de promesa (.success (...)) en forma de un valor entero aleatorio de Int en el rango entre 0 y 100 . A continuación, usamos dos suscripciones futuras , cancelable y cancelable1 , y ambas dan el mismo resultado, aunque se genera un número aleatorio en el interior.
1. Cabe señalar que el "editor" de Future in Combine tiene algunas características de comportamiento en comparación con otros "editores":

  • El "editor" Futuro siempre "publica" UN valor ( valor o error), y esto completa su trabajo.

  • Future "publisher" es una clase de clase ( reference type ) , a diferencia de otros "publishers", que son principalmente estructuras de estructura ( value type ), y recibe el cierre Promise , que se crea inmediatamente después de la inicialización de la instancia Future "publisher", como parámetro. Es decir, el cierre de Promise se transmite antes de que cualquier suscriptor " suscriptor " se suscriba a la instancia de "editor" del Futuro . El "editor" de Future no requiere un "suscriptor" para su funcionamiento, como requieren todos los demás Editores comunes. Es por eso que el texto "¡Hola desde el futuro!" Se imprime solo una vez en el código anterior.

  • El "editor" futuro es un "editor" eager (impaciente), a diferencia de la mayoría de los "publicadores" perezosos ("publicar" solo si hay una "suscripción"). Solo una vez que el editor del Futuro cierra su Promesa , el resultado se recuerda y luego se entrega a los "suscriptores" actuales y futuros. Del código anterior, vemos que cuando se "suscribe" repetidamente al futuro editor, siempre obtiene el mismo valor "aleatorio" (en este caso 6 , pero puede ser diferente, pero siempre el mismo), aunque se usa en el cierre valor int aleatorio.

Dicha lógica del "editor" de Future permite que se use con éxito para almacenar un resultado calculado asincrónico que consume recursos y no perturbar el "servidor" para las subsiguientes "suscripciones" múltiples.

Si esa lógica del "editor" de Future no le conviene y desea que su Future se llame flojo y cada vez que obtenga nuevos valores Int aleatorios, entonces debe "envolver" Future en diferido :



Usaremos Future de una manera clásica, como se sugiere en Combine , es decir, como un "editor" asincrónico computarizado "compartido".

Nota 2. Necesitamos hacer un comentario más con respecto a la "suscripción" que usa sumidero para el "Editor" asíncrono. El método de hundimiento devuelve AnyCancellable , que no recordamos constantemente. Esto significa que Swift destruirá AnyCancellable cuando abandone el contexto dado, que es lo que sucede en el main thread . Por lo tanto, resulta que AnyCancellable se destruye antes de que el cierre con Promise pueda comenzar en el main thread . Cuando se destruye AnyCancellable , se llama a su método de cancelación , que en este caso cancela la "suscripción". Es por eso que recordamos nuestras "suscripciones" de sumidero para el futuro en las variables cancelable <y cancelable1 o en Set <AnyCancellable> () .


Uso de Combinar para buscar películas del sitio web TMDb .


Para comenzar, abra el proyecto de inicio y vaya al archivo MovieStore.swift y al método fetchMovies con una implementación vacía:



Usando el método fetchMovies, podemos seleccionar varias películas estableciendo valores específicos para el parámetro de entrada de punto final de TYPE Endpoint . TYPE Endpoint es una enumeración de enumeración que toma los valores nowPlaying (actual), próxima (próximamente en la pantalla), popular (popular), topRated (superior):



Comencemos inicializando Future con un cierre de callback . Recibido Futuro , luego lo reembolsaremos.



Dentro del cierre de callback , generamos la URL para el valor correspondiente del parámetro de entrada de punto final utilizando la función generateURL (con punto final: Punto final) :



Si no se pudo generar la URL correcta, entonces devolvemos un error usando la promesa (.failure (.urlError (...)) , de lo contrario seguimos adelante e implementamos la URLSession.dataTaskPublisher "publicador".

Para "suscribirse" a los datos de una determinada URL podemos utilizar el método datataskPublisher integrado en la clase URLSession , que toma la URL como parámetro y devuelve el "editor" del editor con los datos de salida de la tupla (datos: datos, respuesta: URLResponse) y un error URLError .



Para convertir un editor de Publisher a otro editor de Publisher, use el operador tryMap . En comparación con el mapa , el operador tryMap puede arrojar un error Error de lanzamiento dentro de un cierre, lo que nos devuelve el nuevo editor Publisher.

En el siguiente paso, utilizaremos el operador tryMap para verificar el código http código de estado de la respuesta de respuesta para asegurarnos de que su valor esté entre 200 y 300 . Si no, entonces arrojamos arroja el valor de error de enumeración responseError enum MovieStoreAPIError . De lo contrario (cuando no hay errores), simplemente devolvemos los datos de datos recibidos al siguiente editor de la cadena "editor".



En el siguiente paso, utilizaremos el operador de decodificación , que decodifica los datos JSON salida del tryMap del "editor" anterior en el MovieResponse <Modelo usando JSONDecoder .



... jsonDecoder está configurado para un formato de fecha específico:



Para que el procesamiento se realice en el main thread , utilizaremos el operador de recepción (on :) y pasaremos RunLoop.main como su parámetro de entrada. Esto permitirá que el "suscriptor" obtenga el valor del valor en el hilo main .



Finalmente, llegamos al final de nuestra cadena de transformación, y allí usamos sumidero para obtener una suscripción de " suscripción " a la "cadena" formada de "editores" de Editores. Para inicializar una instancia de la clase Sink , necesitamos dos cosas, aunque una de ellas es opcional:

  • Cierre de recibir Valor :. Se llamará siempre que una suscripción de "suscripción" reciba un nuevo valor del editor "editor".

  • Recibo Cierre de finalización: (Opcional). Se llamará después de que el editor "editor" haya terminado de publicar el valor , se le dará la enumeración de finalización , que podemos usar para verificar si la "publicación" de los valores se completó realmente o si la finalización se debió a un error.

Dentro del cierre de recibenValor , simplemente invocamos una promesa con una opción .success y un valor de $ 0.resultados , que en nuestro caso es una variedad de películas . Dentro del cierre de complete la recepción, verificamos si la finalización tiene un error de error , luego pasamos el error de promesa correspondiente con la opción .failure .



Tenga en cuenta que aquí recopilamos todos los errores "descartados" en las etapas anteriores de la "cadena de editores".

A continuación, memorizamos la suscripción de "suscripción" en la propiedad Set <AnyCancellable> () .
El hecho es que la suscripción de "suscripción" es Cancelable , es un protocolo que destruye y borra todo después de completar la función fetchMovies . Para garantizar la preservación de la suscripción de "suscripción" incluso después de completar esta función, debemos recordar la suscripción de " suscripción " en la variable externa a la función fetchMovies . En nuestro caso, utilizamos la propiedad de suscripciones , que tiene el tipo Set <AnyCancellable> () y utilizamos el método .store (en: & self.subscriptions) , que garantiza la usabilidad de la "suscripción" después de que la función fetchMovies finalice su trabajo.



Esto concluye la formación del método fetchMovies para seleccionar películas de la base de datos TMDb usando el marco Combinar . El método fetchMovies , como parámetro de entrada de, toma el valor de la enumeración Endpoint de enumeración, es decir, qué películas específicas nos interesan:

.nowPlaying : películas que están actualmente en pantalla,
.upcoming : películas que llegarán pronto,
.popular - películas populares,
.topRated - mejores películas, es decir, con una calificación muy alta.
Intentemos aplicar esta API al diseño de la aplicación con las interfaces de usuario UIKit habituales en forma de un Table View Controller :



y para una aplicación cuya interfaz de usuario se crea utilizando el nuevo marco declarativo SwiftUI :



"Suscribirse" a películas de las películas habituales de View Controller .


Nos movemos al archivo MovieListViewController.swift y en el método viewDidLoad llamamos al método fetchMovies .



Dentro de nuestro método fetchMovies, utilizamos el movieAPI desarrollado anteriormente y su método fetchMovies con el parámetro .nowPlaying como punto final del parámetro de entrada from . Es decir, elegiremos las películas que se encuentran actualmente en las pantallas de los cines.



El método movieAPI.fetchMovies (de: .nowPlaying) devuelve Future "publisher", al que nos "suscribimos" mediante sumidero , y le suministramos dos cierres. En el cierre de complete la recepción , verificamos si hay un error de error y mostramos una advertencia de emergencia a la alerta del usuario con el mensaje de error mostrado.



En el cierre de recibenValor, llamamos al método generateSnapshot y le pasamos las películas seleccionadas.



La función generateSnapshot genera un nuevo NSDiffableDataSourceSnapshot usando nuestras películas y aplica la instantánea resultante al diffableDataSource de nuestra tabla.

Lanzamos la aplicación y observamos cómo funciona UIKit con los "editores" y los "suscriptores" desde el marco Combine . Esta es una aplicación muy simple que no le permite sintonizar varias colecciones de películas, que ahora se muestran en la pantalla, populares, de alta calificación o las que aparecerán en la pantalla en el futuro cercano. Solo vemos las películas que van a aparecer en la pantalla ( próximamente ). Por supuesto, puede hacer esto agregando cualquier elemento de la UI para establecer los valores de la enumeración de Endpoint , por ejemplo, usando Stepper o Segmented Control , y luego actualizar la interfaz de usuario. Esto es bien conocido, pero no lo haremos en una aplicación basada en UIKit , sino que lo dejaremos en el nuevo marco declarativo SwiftUI .
El código para una aplicación basada en UIKit se puede encontrar en Github en la CombineFetchAPICompleted-UIKit .

Use SwiftUI para mostrar películas

.
Creamos una nueva aplicación CombineFetchAPI-MYcon la interfaz SwiftUI usando el menú File-> New-> Projecty seleccionamos la plantilla Single View Appen la sección iOS:



Luego especifique el nombre del proyecto y el método de creación UI- SwiftUI :



Luego, especifique la ubicación del proyecto y copie el archivo Modelo al nuevo proyecto Movie.swifty colóquelo en la carpeta Modelnecesaria para la interacción con TMDB archivos MovieStore.swift, MovieStoreAPIError.swifty MovieService.swift, en consecuencia y colocarlos en carpetas MovieServicey Protocol:



en SwiftUI requería que era codificable , si nos vamos a llenar sus JSONdatos yIdentificable , si queremos facilitar la visualización de la lista de películas [película] en forma de una lista de una lista . Los modelos en SwiftUI no necesitan ser Equatable y Hashable , como lo requiere UIKit API para UITableViewDiffableDataSource en la aplicación UIKit anterior . Por lo tanto, eliminamos de la estructura < struct Movie todos los métodos asociados con los protocolos Equatable y Hashable :


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


Hay un gran artículo identificable que muestra la diferencia y las similitudes entre los Swiftprotocolos.Identificable , Hashable y Equatable .

La interfaz de usuario que crearemos con SwiftUI se basa en el hecho de que cambiamos el punto final al obtener datos y obtenemos la colección de películas que necesitamos, que se presenta en forma de una lista:



así como en el caso de UIKit , los datos se muestrean utilizando la función movieAPI .fetchMovies (desde el punto final: Punto final) , que obtiene el punto final deseado y devuelve el "publicador" Futuro <[Movie, MovieStoreAPIError]> . Si echamos un vistazo a la enumeración de Endpoint , veremos que podemos inicializar la opción deseadacaso en la enumeración de Endpoint y, en consecuencia, la colección de películas deseada utilizando el índice de índice :



Por lo tanto, para obtener la colección de películas que necesitamos para películas , simplemente establezca el índice indexEndpoint correspondiente de la enumeración de Endpoint . Hagámoslo View Model, que en nuestro caso será la clase MoviesViewModel que implementa el protocolo ObservableObject . Agregue un nuevo archivo MoviesViewModel.swift para el nuestro en nuestro proyecto View Model:



en esta clase muy simple, tenemos dos propiedades @Published : una @Published var indexEndpoint: Int- entrada, otras películas Var publicadas: [Película] - salida. Una vez que ponemos @Published propiedad frente indexEndpoint , podemos empezar a utilizarlo como una simple propiedad indexEndpoint , y como editor $ indexEndpoint .
Al inicializar una instancia de nuestra clase MoviesViewModel tenemos que estirar la cadena de entrada "editor" $ indexEndpoint de salida de tipo "editor" AnyPublisher <[Película], para Never> , lo que obtenemos mediante el uso de las funciones ya conocidas movieAPI.fetchMovies (de: Punto final (índice : indexPoint)) y el operador flatMap .



A continuación, nos "suscribimos" a este "editor" recién recibido utilizando un "suscriptor" muy simple que asigna (a: \ .movies, on: self) y asignamos el valor recibido del "editor" a la matriz de salida de películas . Podemos aplicar la evaluación de "suscripción" (a: \ .movies, on: self) solo si el "editor" no arroja un error, es decir, tiene un TIPO de error Nunca . ¿Cómo lograr esto? Usando el operador replaceError (con: []) , que reemplaza cualquier error con una matriz de películas vacía .

Es decir, la primera versión más simple de nuestra aplicación SwiftUI no mostrará información sobre posibles errores al usuario.

Ahora que tenemos View Modelpara nuestras películas, comencemos a crear UI. Añadir al archivo ContentView.swift nuestra View Modelforma @EnvironmentObject variables var moviesViewModel y reemplazar texto ( «¡Hola, mundo!» ) En
el texto ( "\ (moviesViewModel.indexEndpoint)") , que simplemente muestra el índice indexEndpoint colección variante de película.



De manera predeterminada, en nuestro View Modelíndice de colección indexEndpoint = 2 , es decir, al iniciar la aplicación, deberíamos ver películas que aparecerán en la pantalla ( Próximamente ) en un futuro próximo :



luego agregueUIElementos para controlar qué colección de películas queremos mostrar. Este es Stepper :



... y Picker :



ambos usan el "editor" $ moviesViewModel.indexEndpoint nuestro View Model, y con uno de ellos (de todos modos, cuál) podemos seleccionar la colección de películas que necesitamos:



Luego agregamos la lista de películas recibidas usando List y ForEach y atributos mínimos película en sí misma película :



Lista de películas mostradas moviesViewModel.movies que también tomamos de las nuestras View Model:



NO utilizamos $ publisher $ moviesViewModel.movies con el signo $, porque no vamos a editar nada en esta lista de películas. Usamos la propiedad usual moviesViewModel.movies .

Puede hacer que la lista de películas sea más interesante mostrando en cada línea de la lista el póster de la película correspondiente, URLque se presenta en la estructura de la película :



Tomamos esta oportunidad de Thomas Ricouard de su hermoso proyecto MovieSwiftUI .

Como en el caso de cargar películas , para la imagen UIImage , tenemos el servicio ImageService , que implementa el método fetchImage con Combinedevolviendo el "editor" AnyPublisher <UIImage?, Never> :



... y la clase final ImageLoader: ObservableObject que implementa el protocolo ObservableObject con la imagen de propiedad @Published : UIImage? :



El único requisito que establece el protocolo ObservableObject es la existencia de la propiedad objectWillChange . SwiftUI usa esta propiedad para comprender que algo ha cambiado en la instancia de esta clase, y tan pronto como esto sucede, actualiza todas las Vistas que dependen de la instancia de esta clase. Normalmente, el compilador crea automáticamente una propiedad objectWillChange , y todas las propiedades @Published también lo notifican automáticamente. En el caso de algunas situaciones exóticas, puede crear manualmente un objectWillChange y notificarle los cambios. Tenemos tal caso.
En la clase ImageLoader @Published var image:UIImage? . , ImageLoader , «» $image «» loadImage() , poster size @Published var image:UIImage? .Notificaremos a objectWillChange de estos cambios .
Podemos tener muchas de esas imágenes en la tabla, lo que conlleva costos de tiempo significativos, por lo que utilizamos el almacenamiento en caché de las instancias de imageLoader de la clase ImageLoader :



tenemos una vista especial para reproducir el póster de la película MoviePosterImage :



... y la usaremos al mostrar una lista de películas en nuestro ContentView principal :





El código para la aplicación basada en SwiftUI sin visualización de errores se puede encontrar en Github en la carpeta CombineFetchAPI-NOError.

Mostrar errores de recuperación remota de películas asincrónicas.


Hasta el momento, no hemos usado ni mostrado errores que ocurran durante la selección remota asíncrona de películas desde el sitio web TMDb . Aunque la función movieAPI.fetchMovies (from endpoint: Endpoint) que utilizamos nos permite hacer esto, ya que devuelve el "editor" Future <[Movie, MovieStoreAPIError]> .

Con el fin de permitir que los errores, añadir a nuestra View Modelotra @Published propiedad moviesError: MovieStoreAPIError? eso representa el error. Esta es una propiedad opcional , su valor inicial es nulo , que corresponde a la ausencia de un error:



para obtener este error moviesError, tendremos que cambiar ligeramente la inicialización de la clase MoviesViewModel y usar un suscriptor de sumidero más complicado : el



error moviesError se puede mostrar UIsi no es nulo ...



usando AlertView : Simulamos



este error simplemente quitando la APIclave correcta :



Código para una aplicación basada en SwiftUI con La visualización de errores se puede encontrar en Github en la carpeta CombineFetchAPI-Error .

Si inicialmente planeó no manejar los errores, puede hacerlo sin Future <[Movie], MovieStoreAPIError> y devolver lo habitualAnyPublisher <[Película], Nunca> en el método fetchMoviesLight :



La ausencia de errores ( Nunca ) nos permite usar una asignación de "suscriptor" muy simple (a: \ .movies, on: self) :



Todo funcionará como antes:



Conclusión


Usar el marco Combinar para procesar una secuencia de valores que aparecen asíncronamente en el tiempo es muy simple y fácil. Los operadores que ofrece Combine son potentes y flexibles. Combinar nos permite evitar escribir código asincrónico complejo utilizando la cadena ascendente de editores Editores , operadores y suscriptores integrados . Combine se construye en un nivel más bajo que Foundation , en muchos casos no necesita Foundation y tiene un rendimiento sorprendente.



SwiftUI también está fuertemente vinculado a Combine<gracias a su @ObservableObject , @Binding y @EnvironmentObject .
iOSLos desarrolladores llevan mucho tiempo esperando Appleeste tipo de marco oficial, y finalmente este año sucedió.

Enlaces:

Obtención de la API con el asíncrono marco del Apple Remote Combinar
el intento! Swift NYC 2019 - Comenzando con Combine
"El último tutorial de framework Combine en Swift".

Combine: Programación asincrónica con Swift

Introducing Combine - WWDC 2019 - Videos - Apple Developer. session 722
(sinopsis de la sesión 722 "Introducción a la combinación" en ruso)

Combine in Practice - WWDC 2019 - Videos - Apple Developer. sesión 721
(Sinopsis de la sesión 721 "Uso práctico de Combine" en ruso)

SwiftUI & Combine: Together is better. Por qué SwiftUI y Combine te ayudan a crear mejores aplicaciones.

MovieSwiftUI .

Visualice Combine Magic con SwiftUI Parte 1 ()
Visualice Combine Magic con SwiftUI - Parte 2 (Operadores, suscribirse y cancelar en 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/476678/


All Articles