API para busca assíncrona remota usando o Apple Combine



Combine é uma estrutura Swift reativa funcional que foi implementada recentemente para todas as plataformas da Apple , incluindo o Xcode 11 . Com o Combine, é muito fácil processar seqüências de valores que aparecem de forma assíncrona ao longo do tempo. Ele também simplifica o código assíncrono abandonando a delegação e retornos de chamada aninhados complexos.

Mas estudar o Combinar a princípio pode não parecer tão simples. O fato é que os principais "participantes" do Combine são conceitos abstratos como "editores", editores , "assinantes" Assinantes e operadores Operadores , sem os quais não será possível avançar na compreensão da lógica do funcionamento do Combine . No entanto, devido ao fato de a Apple fornecer aos desenvolvedores "editores", "assinantes" e operadores prontos, o código escrito com o Combine é muito compacto e bem legível.

Você verá isso no exemplo de um aplicativo relacionado à recuperação assíncrona de informações de filmes do banco de dados TMDb muito popular agora . Criaremos dois aplicativos diferentes: UIKit e SwiftUI e mostraremos como o Combine funciona com eles.



Espero que este artigo facilite o aprendizado da combinação . O código para todos os aplicativos desenvolvidos neste artigo pode ser encontrado no Github .

Combine tem vários componentes principais:

Publisher Publisher .




Editores Editores são TIPOS que entregam valores a todos que se importam. O conceito de "editor" do editor é implementado no Combine como um protocolo , não como um TYPE específico. O protocolo do Publisher associou TYPES genérico para valor de saída e erro de falha .
Um "editor" que nunca publica um erro usa o erro TIPO Nunca para uma falha .





Apple fornece aos desenvolvedores implementações específicas de "editores" prontos: Justo , Futuro , Vazio , Adiado , Sequência , @Published , etc. "Editores" também são adicionados às classes da Fundação : URLSession , < NotificationCenter , Timer .

Assinante "Assinante".




É também um protocolo de protocolo que fornece uma interface para "assinar" valores de um "editor". Ele associou TYPES genérico para erros de entrada e falha . Obviamente, os TIPOS do Publisher Publisher e Subscriber Subscriber devem corresponder.



Para qualquer editor, existem dois Assinantes internos : afundar e atribuir :



O coletor "Assinante" é baseado em dois fechamentos: um fechamento, receiveValue , é executado quando você obtém os valores , o segundo fechamento, receiveCompletion , é executado quando a "publicação" é concluída (normalmente ou com erro).



A atribuição "Assinante" avança cada valor recebido, em direção ao key Path especificado.

"Assinatura".




Primeiro, o Publicador "publicador" cria e entrega a Assinatura " Assinatura " ao Assinante " Assinante " por meio de seu método de recebimento (assinatura :) :



Depois disso, a Assinatura pode enviar seus valores para os "assinantes" dos Assinantes usando dois métodos:



Se você terminou de trabalhar com a Assinatura , pode chamar o método cancel () :



Assunto "Assunto".




Este é um protocolo de protocolo que fornece uma interface para ambos os clientes, tanto para o "publicador" quanto para o "assinante". Essencialmente, o Assunto "assunto" é o Editor "publisher", que pode aceitar o valor de entrada Input e que você pode usar para "injetar" os valores no fluxo chamando o método send () . Isso pode ser útil ao adaptar o código imperativo existente nos modelos de combinação .

Operador Operador .




Usando o operador, você pode criar um novo "editor" do Publisher a partir de outro "editor" do Publisher , convertendo, filtrando e até combinando os valores dos vários editores anteriores do upstream .



Você vê aqui muitos nomes conhecidos de operadores: compactMap , mapear , filtrar , dropFirst , anexar .

Editores da Fundação Editores incorporados à Fundação .


Apple também fornece aos desenvolvedores vários dos recursos Combine já incorporados na estrutura Foundation <, ou seja, editores dos editores, para tarefas como buscar dados usando o URLSession , trabalhar com notificações usando Notification , Timer e monitorar propriedades baseadas no KVO . Essa compatibilidade interna realmente nos ajudará a integrar a estrutura Combine em nosso projeto atual.
Para saber mais sobre isso, consulte o artigo “O melhor tutorial da estrutura Combine no Swift” .

O que aprendemos a fazer com o Combine ?


Neste artigo, aprenderemos como usar a estrutura Combine para buscar dados de filmes no site do TMDb . Aqui está o que estudaremos juntos:

  • Usando o futuro "editor" para criar um fechamento com o Promise para um único valor: valor ou erro.
  • Usando o URLSession.datataskPublisher "publisher" para "assinar" os dados publicados por um determinado UR L.
  • Usando o operador tryMap para converter dados de dados usando outro editor do Publisher.
  • Usando o operador de decodificação para converter dados em um objeto Decodable e publique-os para transmissão aos elementos subsequentes da cadeia.
  • Usando o operador coletor para "assinar" um "editor" do Publisher usando fechamentos.
  • Use a instrução de atribuição para "assinar" o "editor" do Publicador e atribua o valor que ele fornece ao key Path fornecido.

Projeto inicial


Antes de começarmos, precisamos nos registrar para receber a chave API no site do TMDb . Você também precisa baixar o projeto inicial do repositório do GitHub .
Certifique-se de colocar sua chave de API na classe MovieStore, em deixe apiKey constante.



Aqui estão os principais blocos de construção dos quais criaremos nosso projeto:

  • 1. Dentro do arquivo Movie.swift estão os modelos que usaremos em nosso projeto. A estrutura raiz do struct MoviesResponse implementa o protocolo Decodable , e o usaremos ao decodificar dados JSON em um Model. A estrutura MoviesResponse possui uma propriedade de resultados , que também implementa o protocolo Decodable e é uma coleção de filmes [Movie] . É ela quem nos interessa:



  • 2. A enumeração de enumeração MovieStoreAPIError implementa o protocolo Error . Nossa API usará essa enumeração para representar vários tipos de erros: erros de recuperação de URL urlError , erros de decodificação de decodingError e erros de busca de dados responseError .



  • 3. Nossa API possui um protocolo MovieService com um único método, fetchMovies (do endpoint: Endpoint) , que seleciona filmes [Movie] com base no parâmetro endpoint . O próprio ponto de extremidade é uma enumeração de enum que representa um ponto de extremidade para acessar o banco de dados TMDb para buscar filmes como nowPlaying (mais recente), popular (popular), topRated (superior) e futuro (em breve na tela).



  • 4. A classe MovieStore é uma classe específica que implementa o protocolo MovieService para buscar dados no site TMDb . Dentro desta classe, implementamos o método fetchMovies (...) usando Combine .



  • 5. A classe MovieListViewController é a principal classe ViewController na qual usamos o método coletor para "assinar" o método de busca de filme fetchMovies (...) , que retorna o "publicador" do Futuro e atualiza a tabela TableView com novos dados do filme filmes usando a nova API DiffableDataSourceSnapshot .

Antes de começarmos, vamos ver alguns dos componentes básicos do Combine que usaremos para API recuperação remota de dados.

"Inscreva-se" no "editor" usando o coletor e seus fechamentos.


A maneira mais fácil de "assinar" o "editor" do Publicador é usar coletor com seus fechamentos, um dos quais será executado sempre que obtivermos um novo valor e o outro quando o "publicador" concluir a entrega dos valores .



Lembre-se de que em Combine, cada "assinatura" retorna um cancelável , que será excluído assim que sairmos do nosso contexto. Para manter uma "assinatura" por mais tempo, por exemplo, para obter valores de forma assíncrona, precisamos salvar a "assinatura" na propriedade subscription1 . Isso nos permitiu obter consistentemente todos os valores (7,8,3,4) .

O futuro publica assincronamente um único valor: valor ou erro de falha .


Na estrutura Combinar , o futuro do "editor" pode ser usado para obter de forma assíncrona um único TIPO de Resultado usando um fechamento. O fechamento possui um parâmetro - Promise , que é uma função de TYPE (Result <Output, Failure>) -> Void .

Vejamos um exemplo simples para entender como o editor Future funciona:



Criamos Future com um resultado bem-sucedido de TYPE Int e um erro de TYPE Never . Dentro do fechamento Future , usamos DispatchQueue.main.asyncAfter (...) para atrasar a execução do código em 2 segundos, simulando o comportamento assíncrono. Dentro do fechamento, retornamos Promise com um resultado bem-sucedido de promessa (.success (...)) como um valor inteiro aleatório Int no intervalo entre 0 e 100 . Em seguida, usamos duas assinaturas futuras - cancelável e cancelável1 - e ambas fornecem o mesmo resultado, embora um número aleatório seja gerado dentro.
1. -se que o "editor" do Future in Combine possui alguns recursos comportamentais em comparação com outros "editores":

  • O futuro "editor" sempre publica "UM valor ( valor ou erro), e isso conclui seu trabalho.

  • O futuro "publicador" é uma classe ( reference type ) , diferentemente de outros "editores", que são principalmente estruturas ( value type ), e recebe o fechamento do Promise , criado imediatamente após a inicialização da instância do futuro "publicador", como parâmetro. Ou seja, o encerramento da Promessa é transmitido antes que qualquer assinante " assinante " assine a instância "publicadora" do Futuro . O "publicador" da Future não exige um "assinante" para seu funcionamento, como todos os outros editores comuns exigem. É por isso que o texto "Olá de dentro do futuro!" É impresso apenas uma vez no código acima.

  • O futuro "editor" é um "editor" eager (impaciente), diferentemente da maioria dos outros "editores" preguiçosos ("publicar" somente se houver uma "assinatura"). Somente depois que a editora Future encerra sua promessa , o resultado é lembrado e entregue aos “assinantes” atuais e futuros. A partir do código acima, vemos que, quando você se inscreve repetidamente no coletor para o futuro publicador, sempre obtém o mesmo valor "aleatório" (neste caso, 6 , mas pode ser diferente, mas sempre o mesmo), embora seja usado no fechamento valor int aleatório.

Essa lógica do "publicador" do Future permite que ele seja usado com sucesso para armazenar um resultado calculado que consome recursos assíncronos e para não perturbar o "servidor" de várias "assinaturas" subsequentes.

Se essa lógica do "editor" do Future não combina com você e você deseja que o seu futuro seja chamado de preguiçoso e cada vez que você obtém novos valores aleatórios de Int , você deve "agrupar" o Future em adiado :



Usaremos o Future de maneira clássica, como sugerido em Combine , ou seja, como um "editor" assíncrono computado "compartilhado".

Nota 2. Precisamos fazer mais uma observação sobre a "assinatura" usando coletor para o "Publicador" assíncrono. O método sink retorna AnyCancellable , do qual não lembramos constantemente. Isso significa que o Swift destruirá o AnyCancellable quando você sair do contexto especificado, que é o que acontece no main thread . Portanto, o AnyCancellable é destruído antes que o fechamento com o Promise possa ser iniciado no main thread . Quando AnyCancellable é destruído, seu método de cancelamento é chamado, que neste caso cancela a "assinatura". É por isso que lembramos de nossas “assinaturas” de coletor para o futuro nas variáveis cancellable <e cancellable1 ou em Set <AnyCancellable> () .


Usando Combinar para buscar filmes no site do TMDb .


Para começar, abra o projeto inicial e vá para o arquivo MovieStore.swift e o método fetchMovies com uma implementação vazia:



Usando o método fetchMovies, podemos selecionar vários filmes definindo valores específicos para o parâmetro de entrada do terminal do TYPE Endpoint . TYPE Endpoint é uma enumeração de enumeração que assume os valores nowPlaying (current), coming (coming to the screen soon), popular (popular), topRated (top):



Vamos começar inicializando Future com um fechamento de callback . Futuro recebido, nós reembolsaremos.



Dentro do fechamento de callback , geramos a URL para o valor correspondente do parâmetro de entrada do terminal usando a função generateURL (com endpoint: Endpoint) :



Se a URL correta não puder ser gerada, retornamos um erro usando promessa (.failure (.urlError (...)) , caso contrário, prosseguimos e implementamos o URLSession.dataTaskPublisher do "editor".

Para "inscrever-se" nos dados de uma determinada URL podemos usar o método datataskPublisher incorporado à classe URLSession , que usa a URL como parâmetro e retorna o Publisher "publisher" com os dados de saída da tupla (dados: Dados, resposta: URLResponse) e um erro de URLError .



Para converter um editor do Publisher em outro editor do Publisher, use o operador tryMap . Comparado ao mapa , o operador tryMap pode gerar um erro de erro de lançamento dentro de um fechamento, o que nos devolve o novo editor do Publisher.

Na próxima etapa, usaremos o operador tryMap para verificar o código http statusCode da resposta de resposta para garantir que seu valor esteja entre 200 e 300 . Caso contrário, lançamos o valor de erro de enumeração responseError enum MovieStoreAPIError . Caso contrário (quando não houver erros), simplesmente retornamos os dados de dados recebidos para o próximo Publicador na cadeia "publisher".



Na próxima etapa, usaremos o operador decode , que decodifica os dados JSON saída do tryMap “publicador” anterior no MovieResponse <Model usando JSONDecoder .



... jsonDecoder está configurado para um formato de data específico:



Para que o processamento seja executado no main thread , usaremos o operador receive (on :) e passaremos RunLoop.main como parâmetro de entrada. Isso permitirá que o "assinante" obtenha o valor do valor no thread main .



Finalmente, chegamos ao final de nossa cadeia de transformação e usamos o coletor para obter uma assinatura de " assinatura " da "cadeia" formada de "editores" de editores. Para inicializar uma instância da classe Sink , precisamos de duas coisas, embora uma delas seja opcional:

  • encerramento receiveValue :. Ele será chamado sempre que uma assinatura de "assinatura" receber um novo valor do Publicador "editor".

  • recebimento de conclusão: (Opcional). Ele será chamado depois que o Publisher "publisher" terminar de publicar o valor , recebe a enumeração de conclusão , que podemos usar para verificar se a "publicação" dos valores está realmente concluída ou se a conclusão ocorreu devido a um erro.

Dentro do fechamento do ReceiveValue , simplesmente chamamos uma promessa com uma opção .success e um valor de $ 0.results , que, no nosso caso, é uma variedade de filmes . Dentro do fechamento receiveCompletion, verificamos se a conclusão possui um erro e passamos o erro de promessa correspondente com a opção .failure .



Observe que aqui coletamos todos os erros "descartados" nas etapas anteriores da "cadeia de editores".

Em seguida, memorizamos a assinatura de "assinatura" na propriedade Set <AnyCancellable> () .
O fato é que a assinatura de “assinatura” é Cancelável , é um protocolo que destrói e limpa tudo após a conclusão da função fetchMovies . Para garantir que a assinatura de “assinatura” seja preservada mesmo após a conclusão dessa função, precisamos lembrar a assinatura de “ assinatura ” na variável externa à função fetchMovies . No nosso caso, usamos a propriedade subscriptions , que possui o tipo Set <AnyCancellable> () e usamos o método .store (in: & self.subscriptions) , que garante a usabilidade da “assinatura” após a função fetchMovies terminar seu trabalho.



Isso conclui a formação do método fetchMovies para selecionar filmes do banco de dados TMDb usando a estrutura Combine . O método fetchMovies , como parâmetro de entrada , assume o valor da enumeração Endpoint , ou seja, quais filmes específicos são interessantes para nós:

.nowPlaying - filmes que estão atualmente na tela,
.upcoming - filmes que estão chegando em breve,
.popular - filmes populares,
.topRated - os melhores filmes, ou seja, com uma classificação muito alta.
Vamos tentar aplicar essa API ao design do aplicativo com as interfaces de usuário usuais do UIKit na forma de um Table View Controller :



e para um aplicativo cuja interface do usuário seja criada usando a nova estrutura declarativa SwiftUI :



"Inscreva-se" nos filmes dos filmes usuais do View Controller .


Passamos para o arquivo MovieListViewController.swift e, no método viewDidLoad , chamamos o método fetchMovies .



Dentro do nosso método fetchMovies, usamos o movieAPI desenvolvido anteriormente e seu método fetchMovies com o parâmetro .nowPlaying como o ponto final do parâmetro from input. Ou seja, escolheremos os filmes que estão atualmente nas telas das salas de cinema.



O método movieAPI.fetchMovies (from: .nowPlaying) retorna Future "publisher", ao qual "assinamos" usando o coletor , e fornecemos dois fechamentos. No fechamento receiveCompletion , verificamos se há um erro de erro e exibimos um aviso de emergência para o alerta do usuário com a mensagem de erro exibida.



No fechamento receiveValue, chamamos o método generateSnapshot e passamos os filmes selecionados para ele .



A função generateSnapshot gera um novo NSDiffableDataSourceSnapshot usando nossos filmes e aplica o instantâneo resultante ao diffableDataSource da nossa tabela.

Iniciamos o aplicativo e observamos como o UIKit funciona com os "publicadores" e "assinantes" da estrutura Combine . Esta é uma aplicação muito simples que não permite que você sintonize várias coleções de filmes - agora exibidos na tela, populares, de alta classificação ou aqueles que aparecerão na tela em um futuro próximo. Vemos apenas os filmes que aparecerão na tela ( .upcoming ). Obviamente, você pode fazer isso adicionando qualquer elemento da UI para definir os valores da enumeração do ponto de extremidade , por exemplo, usando Stepper ou Segmented Control e atualize a interface do usuário. Isso é bem conhecido, mas não faremos isso em um aplicativo baseado no UIKit , mas deixamos isso para a nova estrutura declarativa do SwiftUI .
O código para um aplicativo baseado no UIKit pode ser encontrado no Github na CombineFetchAPICompleted-UIKit .

Use SwiftUI para exibir filmes filmes

.
Criamos um novo aplicativo CombineFetchAPI-MYcom a interface SwiftUI usando o menu File-> New-> Projecte selecionamos o modelo Single View Appna seção iOS:



Em seguida, especifique o nome do projeto e o método de criação UI- SwiftUI :



Em seguida, especifique o local do projeto e copie o arquivo Modelo para o novo projeto Movie.swifte coloque-o na pasta Modelnecessária para interagir com TMDB arquivos MovieStore.swift, MovieStoreAPIError.swifte MovieService.swift, e colocá-los em conformidade, pastas MovieServicee Protocol:



em SwiftUI exigiu que foi codificável , se vamos para preencher seus JSONdados eIdentificável , se queremos facilitar a visualização da lista de filmes [Filme] , na forma de uma lista de uma lista . Os modelos no SwiftUI não precisam ser Equatable e Hashable , conforme exigido pelo UIKit API para o UITableViewDiffableDataSource no aplicativo UIKit anterior . Portanto, removemos da estrutura < struct Movie todos os métodos associados aos protocolos Equatable e Hashable :


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


Há um ótimo artigo identificável que mostra a diferença e as semelhanças entre os Swiftprotocolos.Identificável , Hashable e Equatable .

A interface do usuário que criaremos usando o SwiftUI é baseada no fato de alterarmos o ponto de extremidade ao buscar dados e obter a coleção de filmes necessária, apresentada na forma de uma lista:



Assim como no UIKit , os dados são amostrados usando a função movieAPI .fetchMovies (do terminal: Terminal) , que obtém o terminal desejado e retorna o "publicador" Future <[Movie, MovieStoreAPIError]> . Se dermos uma olhada na enumeração do ponto de extremidade , veremos que podemos inicializar a opção desejadacaso na enumeração Endpoint e, consequentemente, na coleção de filmes desejada usando o índice de índice :



Portanto, para obter a coleção de filmes que precisamos para filmes , apenas defina o índice indexEndpoint correspondente da enumeração de Endpoint . Vamos fazer isso View Model, que no nosso caso será a classe MoviesViewModel que implementa o protocolo ObservableObject . Adicione um novo arquivo MoviesViewModel.swift ao nosso em nosso projeto View Model:



Nesta classe muito simples, temos duas propriedades @Published : uma @Published var indexEndpoint: Int- entrada, outros filmes var. @Published: [Movie] - saída. Uma vez que colocamos @Published propriedade frente indexEndpoint , podemos começar a usá-lo como uma simples propriedade indexEndpoint , e como um editor $ indexEndpoint .
Ao inicializar uma instância da nossa classe MoviesViewModel, devemos esticar a cadeia da entrada "publisher" $ indexEndpoint para a saída "publisher" TYPE AnyPublisher <[Movie], Never> , que obtemos usando a função movieAPI.fetchMovies (from: Endpoint (index ) que já conhecemos : indexPoint)) e o operador flatMap .



Em seguida, "assinamos" esse "editor" recém-recebido usando uma avaliação muito simples de "assinante" (para: \ .movies, on: self) e atribuímos o valor recebido do "editor" à matriz de saída de filmes . Podemos aplicar a avaliação de "assinatura" (para: \ .movies, on: self) somente se o "editor" não gerar um erro, ou seja, ele tiver um TIPO de erro Nunca . Como conseguir isso? Usando o operador replaceError (with: []) , que substitui quaisquer erros por uma matriz de filmes vazia .

Ou seja, a primeira versão mais simples do nosso aplicativo SwiftUI não exibirá informações sobre possíveis erros para o usuário.

Agora que temos View Modelos filmes, vamos começar a criar UI. Adicionar ao arquivo ContentView.swift nossa View Modelcomo @EnvironmentObject variável var moviesViewModel e substituir Texto ( «Olá, mundo!» ) No
texto ( "\ (moviesViewModel.indexEndpoint)") , que simplesmente exibe o índice indexEndpoint coleção de filmes variante.



Por padrão, em nosso View Modelíndice de coleção indexEndpoint = 2 , ou seja, ao iniciar o aplicativo, devemos ver filmes que aparecerão na tela ( Próximos ) em um futuro próximo :



Em seguida, adicioneUIElementos para controlar qual coleção de filmes queremos mostrar. Este é Stepper :



... e Picker :



Ambos usam o "editor" $ moviesViewModel.indexEndpoint nosso View Modele, com um deles (de qualquer forma, qual), podemos selecionar a coleção de filmes de que precisamos:



Em seguida, adicionamos a lista de filmes recebidos usando List e ForEach e atributos mínimos próprio filme movie :



Lista de filmes exibidos moviesViewModel.movies também tiramos do nosso View Model:



NÃO usamos o $ publisher $ moviesViewModel.movies com o sinal $, porque não vamos editar nada nesta lista de filmes. Usamos a propriedade usual moviesViewModel.movies .

Você pode tornar a lista de filmes mais interessante exibindo em cada linha da lista o pôster do filme correspondente, URLque é apresentado na estrutura do filme :



Aproveitamos essa oportunidade de Thomas Ricouard do seu belo projeto MovieSwiftUI .

Como no caso de carregar filmes , para a imagem UIImage , temos o serviço ImageService , que implementa o método fetchImage com Combineretornando o "publicador" AnyPublisher <UIImage?, Never> :



... e a classe final ImageLoader: ObservableObject que implementa o protocolo ObservableObject com a imagem da propriedade @Published : UIImage? :



O único requisito que o protocolo ObservableObject faz é a existência da propriedade objectWillChange . O SwiftUI usa essa propriedade para entender que algo mudou na instância desta classe e, assim que isso acontece, atualiza todas as Views que dependem da instância dessa classe. Normalmente, o compilador cria automaticamente uma propriedade objectWillChange e todas as propriedades @Published também a notificam automaticamente. No caso de algumas situações exóticas, você pode criar manualmente um objectWillChange e notificá-lo sobre as alterações. Temos exatamente esse caso.
Na classe ImageLoadertemos uma única propriedade @Published var image: UIImage? .É nesta implementação espirituoso é entrada e saída, Na inicialização de instância ImageLoader , usamos o "publisher" $ imagem e "assinatura" para ele chamar o loadImage () , que carrega nos obrigou a imagem cartaz tamanho certo tamanho e atribui a ele @ Publicado na propriedade var image: UIImage? .Notificaremos o objectWillChange dessas alterações .
Na tabela podemos ter um monte de imagens, resultando em significativa demorado, por isso usamos um cache cópias imageLoader classe ImageLoader :



Temos um especial de exibição para jogar Filme Posters MoviePosterImage :



... e vamos usá-lo para exibir a lista de filmes em nosso principal ContentView :





O código para o aplicativo baseado no SwiftUI sem exibição de erro pode ser encontrado no Github na pasta CombineFetchAPI-NOError.

Exibir erros de busca remota de filme assíncrono.


Até o momento, não usamos ou exibimos erros que ocorrem durante a seleção assíncrona remota de filmes no site do TMDb . Embora a função movieAPI.fetchMovies (do ponto de extremidade: Ponto de extremidade) que usamos nos permita fazer isso, uma vez que ela retorna o "publicador" Future <[Movie, MovieStoreAPIError]> .

A fim de permitir erros, acrescentar à nossa View Modeloutra @Published propriedade moviesError: MovieStoreAPIError? isso representa o erro. Esta é uma propriedade Opcional , seu valor inicial é nulo , o que corresponde à ausência de um erro:



Para obter este erro moviesError, teremos que alterar levemente a inicialização da classe MoviesViewModel e usar um assinante de coletor mais complicado : o



erro moviesError pode ser exibido UIse não for nulo ...



usando o AlertView : Simulamos



esse erro simplesmente removendo a APIchave correta :



Código para um aplicativo baseado no SwiftUI com a exibição de erros pode ser encontrada no Github na pasta CombineFetchAPI-Error .

Se você planejou inicialmente não lidar com erros, poderá fazer sem Future <[Movie], MovieStoreAPIError> e retornar o valor usualAnyPublisher <[Movie], Never> no método fetchMoviesLight :



A ausência de erros ( Never ) nos permite usar uma atribuição muito simples de "assinante" (para: \ .movies, on: self) :



tudo funcionará como antes:



Conclusão


Usar a estrutura Combine para processar uma sequência de valores que aparecem de forma assíncrona no tempo é muito simples e fácil. Os operadores oferecidos pela Combine são poderosos e flexíveis. A combinação nos permite evitar a criação de códigos assíncronos complexos usando a cadeia upstream de editores de editores , usando operadores e assinantes internos . A combinação é criada em um nível mais baixo que o Foundation , em muitos casos não precisa do Foundation e tem um desempenho incrível.



O SwiftUI também está fortemente vinculado ao Combine<graças ao seu @ObservableObject , @Binding e @EnvironmentObject .
iOSos desenvolvedores esperam há muito tempo por Appleesse tipo de estrutura oficial e, finalmente, este ano aconteceu.

Links:

Buscando a API assíncrona remota com o Apple Combine Framework,
tente! Swift NYC 2019 - Introdução ao Combine
"O melhor tutorial de estrutura do Combine no Swift".

Combinar: programação assíncrona com Swift

Introducing Combine - WWDC 2019 - Vídeos - Apple Developer. sessão 722
(sinopse da Sessão 722 “Introdução à combinação” em russo)

Combinar na prática - WWDC 2019 - Vídeos - Apple Developer. sessão 721
(Sinopse da Sessão 721 “Uso prático da

combinação ” em russo) SwiftUI & Combine: Juntos é melhor. Por que o SwiftUI e o Combine ajudam a criar aplicativos melhores.

MovieSwiftUI .

Visualize Combine Magic com SwiftUI Parte 1 ()
Visualize Combine Magic com SwiftUI - Parte 2 (operadores, assinando e cancelando em 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/pt476678/


All Articles