Aula 95: Rede com Combine e SwiftUI
Nesta aula, vamos explorar como realizar tarefas de rede usando Combine e SwiftUI. Combine é um framework poderoso que permite trabalhar com eventos assíncronos e fluxos de dados, enquanto o SwiftUI oferece uma maneira moderna de construir interfaces de usuário. Juntos, eles nos permitem lidar com requisições de rede e atualizar a interface de forma reativa.
Configurando o Projeto
Para começar, crie um novo projeto SwiftUI no Xcode. Certifique-se de ter selecionado SwiftUI como a interface e Combine como uma opção nas configurações do projeto.
Gerenciador de Rede
Primeiro, vamos criar um gerenciador de rede simples que usa Combine para buscar dados de uma API REST. Para este exemplo, vamos buscar uma lista de usuários da API JSONPlaceholder.
import Foundation
import Combine
struct Usuario: Codable, Identifiable {
let id: Int
let nome: String
let nomeUsuario: String
let email: String
}
class GerenciadorRede: ObservableObject {
@Published var usuarios: [Usuario] = []
var cancellables = Set<AnyCancellable>()
func buscarUsuarios() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [Usuario].self, decoder: JSONDecoder())
.replaceError(with: [])
.receive(on: DispatchQueue.main)
.assign(to: &$usuarios)
}
}
View do SwiftUI
Em seguida, vamos criar uma view SwiftUI que usa o GerenciadorRede
para exibir a lista de usuários. Usaremos uma List
para mostrar os usuários em um formato rolável.
import SwiftUI
struct ContentView: View {
@StateObject var gerenciadorRede = GerenciadorRede()
var body: some View {
NavigationView {
List(gerenciadorRede.usuarios) { usuario in
VStack(alignment: .leading) {
Text(usuario.nome)
.font(.headline)
Text(usuario.email)
.font(.subheadline)
}
}
.navigationTitle("Usuários")
.onAppear {
gerenciadorRede.buscarUsuarios()
}
}
}
}
Combine e Ciclo de Vida da View
No código acima, utilizamos o modificador onAppear
para acionar o método buscarUsuarios
quando a view aparece. O wrapper de propriedade @StateObject
assegura que nosso GerenciadorRede
seja instanciado apenas uma vez e persista durante todo o ciclo de vida da view.
Executando o App
Agora, quando você executar o app, ele buscará a lista de usuários da API e a exibirá em formato de lista. Isso demonstra um exemplo básico de uso de networking com Combine e SwiftUI.
Tratando Erros
Para melhorar nosso app, devemos também tratar possíveis erros que podem surgir durante a requisição de rede:
import SwiftUI
struct ContentView: View {
@StateObject var gerenciadorRede = GerenciadorRede()
var body: some View {
NavigationView {
List(gerenciadorRede.usuarios) { usuario in
VStack(alignment: .leading) {
Text(usuario.nome)
.font(.headline)
Text(usuario.email)
.font(.subheadline)
}
}
.navigationTitle("Usuários")
.onAppear {
gerenciadorRede.buscarUsuarios()
}
.alert(isPresented: $gerenciadorRede.temErro) {
Alert(title: Text("Erro"),
message: Text(gerenciadorRede.mensagemErro),
dismissButton: .default(Text("OK")))
}
}
}
}
Atualize a classe GerenciadorRede
para incluir o tratamento de erros:
class GerenciadorRede: ObservableObject {
@Published var usuarios: [Usuario] = []
@Published var temErro: Bool = false
var mensagemErro: String = ""
var cancellables = Set<AnyCancellable>()
func buscarUsuarios() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [Usuario].self, decoder: JSONDecoder())
.replaceError(with: [])
.handleEvents(receiveCompletion: { completion in
switch completion {
case .failure(let error):
self.temErro = true
self.mensagemErro = error.localizedDescription
case .finished:
break
}
})
.receive(on: DispatchQueue.main)
.assign(to: &$usuarios)
}
}
Conclusão
Nesta aula, aprendemos como usar Combine com SwiftUI para realizar tarefas de rede. Aproveitando o fluxo de dados do Combine e as atualizações reativas da interface do SwiftUI, podemos criar uma experiência de aplicativo responsiva e moderna. Com o apropriado tratamento de erros, nosso app se torna mais robusto e amigável para o usuário.
Sinta-se à vontade para expandir este exemplo adicionando recursos como paginação, indicadores de carregamento ou refinando ainda mais a interface. Boa codificação!