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-MY
com a interface SwiftUI usando o menu File
-> New
-> Project
e selecionamos o modelo Single View App
na 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.swift
e coloque-o na pasta Model
necessária para interagir com TMDB arquivos MovieStore.swift
, MovieStoreAPIError.swift
e MovieService.swift
, e colocá-los em conformidade, pastas MovieService
e Protocol
:
em SwiftUI exigiu que
foi codificável , se vamos para preencher seus JSON
dados 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 Swift
protocolos.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 Model
os filmes, vamos começar a criar UI
. Adicionar ao arquivo ContentView.swift nossa View Model
como @EnvironmentObject variável var moviesViewModel e substituir Texto ( «Olá, mundo!» ) Notexto ( "\ (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, adicioneUI
Elementos para controlar qual coleção de filmes queremos mostrar. Este é Stepper :
... e Picker :
Ambos usam o "editor" $ moviesViewModel.indexEndpoint nosso View Model
e, 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, URL
que é 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 Model
outra @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 UI
se não for nulo ...
usando o AlertView : Simulamos
esse erro simplesmente removendo a API
chave 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 .iOS
os desenvolvedores esperam há muito tempo por Apple
esse 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 SwiftIntroducing 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 dacombinaçã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 4Visualize Combine Magic with SwiftUI — Part 5Getting Started With the Combine Framework in SwiftTransforming Operators in Swift Combine Framework: Map vs FlatMap vs SwitchToLatestCombine's FutureUsing CombineURLSession and the Combine framework