 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 URL.
- 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 Pathfornecido.
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.swiftestão os modelos que usaremos em nosso projeto. A estrutura raiz do struct MoviesResponse implementa o protocolo Decodable , e o usaremos ao decodificar dadosJSONem 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 APIusará essa enumeração para representar vários tipos de erros: erros de recuperação deURLurlError , erros de decodificação de decodingError e erros de busca de dados responseError .

- 3. Nossa APIpossui 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 APIDiffableDataSourceSnapshot .
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
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
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
em SwiftUI exigiu que 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
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 :
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
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 .
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
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!» ) Notexto ( "\ (moviesViewModel.indexEndpoint)") , que simplesmente exibe o índice indexEndpoint coleção de filmes variante. Por padrão, em nosso
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, adicione
Em seguida, adicioneUIElementos para controlar qual coleção de filmes queremos mostrar. Este é Stepper : ... e Picker :
... e Picker : Ambos usam o "editor" $ moviesViewModel.indexEndpoint nosso
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 :
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
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,
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> :
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?
... 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?
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 :
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 :
... 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
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
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
erro moviesError pode ser exibido UIse não for nulo ... usando o AlertView : Simulamos
usando o AlertView : Simulamos esse erro simplesmente removendo a
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 :
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) :
A ausência de erros ( Never ) nos permite usar uma atribuição muito simples de "assinante" (para: \ .movies, on: self) : tudo funcionará como antes:
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 .
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 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