📱Escaping Closures em SwiftUI
Trabalhar com operações assíncronas em Swift e SwiftU 👨🏽💻
🙋🏽♂️Eae pessoal, tudo bem?
Em Swift e SwiftUI as closures são blocos de código auto-contidos que podem ser passados e usados em nossos aplicativos. Elas são especialmente úteis para callbacks e tratamento assíncrono.
Um tipo particular de closure que todo desenvolvedor Swift precisa conhecer é a “escaping closure”.
🤔O que são Closures?
Closures são blocos de código que podem ser passados e usados em seu código. Eles podem capturar e armazenar referências a variáveis e constantes de fora de seu escopo. Em Swift, closures são frequentemente usados em callbacks e como handlers assíncronos.
Há três formas principais de definir e usar closures:
- Inline Closures: São definidos diretamente no local onde são usados. Eles são frequentemente usados como argumentos em funções.
let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { (number: Int) -> Int in
return number * 2
}
- Trailing Closures: Se o último parâmetro de uma função é um closure, você pode usar a sintaxe de trailing closure, que melhora a legibilidade do código, especialmente quando o closure é longo.
let doubledNumbers = numbers.map { number in
number * 2
}
- Named Closures: São closures atribuídos a uma variável ou constante e podem ser reutilizados.
let doubleNumber: (Int) -> Int = { number in
return number * 2
}
let doubledNumbers = numbers.map(doubleNumber)
O conceito de escaping closures se aplica a qualquer uma dessas formas, mas geralmente está mais associado a closures passados como parâmetros para funções, onde sua execução pode ocorrer após a função retornar.
🤷🏽♂️E o Escaping Closures ?
Estes são os que podem ser executados após a função que os recebeu como argumento ter retornado. Para marcar um closure como escaping, usa-se a anotação @escaping
.
Contexto onde Escaping Closures são usados
Escaping closures são geralmente usados em operações assíncronas, como chamadas de rede, onde o closure é executado após a conclusão da operação assíncrona. Iremos ver um exemplo para clarificar melhor tudo isso.
👨🏽💻Exemplificando
Vamos criar um pequeno aplicativo utilizando Swift e SwiftUI que faz uma requisição a uma API pública (iremos usar a https://jsonplaceholder.typicode.com/) e exibir os dados em uma lista.
Passo 1: Configuração do Projeto
Crie um novo projeto no XCode em SwiftUI e certifique-se de que o Swift e o SwiftUI estejam selecionados. Nomeie seu projeto como “EscapingClosuresExemplo”.
Passo 2: Estrutura de projeto em camadas
Estruturar nosso projeto em camadas, como o MVVM (Model-View-ViewModel), ajuda a organizar nosso código de forma mais modular e sustentável.
Vamos organizar nosso projeto em três camadas principais:
- Model: Representa os dados e a lógica de negócio.
- View: Representa a interface do usuário.
- ViewModel: Faz a ponte entre a Model e a View, fornecendo dados à View e respondendo às interações do usuário.
Estrutura Completa do Projeto
A estrutura final deste nosso projeto de exemplo ficará dessa forma:
EscapingClosuresExemplo/
├── EscapingClosuresExemploApp.swift
├── Model/
│ ├── Post.swift
│ └── NetworkService.swift
├── ViewModel/
│ └── PostViewModel.swift
└── View/
└── ContentView.swift
Model
A Model contém a definição dos dados e as operações relacionadas. Vamos criar dois arquivos:
Post.swift
: Definição do modelo de dados.
import Foundation
struct Post: Codable, Identifiable {
let id: Int
let title: String
let body: String
}
NetworkService.swift
: Serviço de rede irá buscar os posts da API. É aqui onde usaremos um escaping closure.
import Foundation
class NetworkService {
func fetchPosts(completion: @escaping ([Post]) -> Void) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let posts = try JSONDecoder().decode([Post].self, from: data)
DispatchQueue.main.async {
completion(posts)
}
} catch {
print("Error decoding: \(error)")
}
}
}.resume()
}
}
No NetworkService
, a função fetchPosts
faz uma requisição a uma URL e usa um escaping closure para passar os dados de volta. O uso de @escaping
indica que o closure pode ser chamado depois que a função fetchPosts
termina sua execução.
ViewModel
O ViewModel é responsável por interagir com o serviço de rede e fornecer dados para a View. Vamos criar um arquivo:
PostViewModel.swift
: Definição do ViewModel.
import Combine
class PostViewModel: ObservableObject {
@Published var posts: [Post] = []
private var networkService = NetworkService()
func getPosts() {
networkService.fetchPosts { [weak self] posts in
self?.posts = posts
}
}
}
O PostViewModel
é responsável por buscar os dados e atualizá-los para a view. Ele usa o NetworkService
para fazer a requisição e atualizar a propriedade posts
quando os dados são recebidos.
View
A View é responsável por exibir os dados e interagir com o usuário. Vamos criar um arquivo:
ContentView.swift
: Definição da interface do usuário.
import SwiftUI
struct ContentView: View {
@StateObject private var viewModel = PostViewModel()
var body: some View {
NavigationView {
List(viewModel.posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.subheadline)
}
}
.navigationTitle("Posts")
.onAppear {
viewModel.getPosts()
}
}
}
}
@main
struct EscapingClosuresExemploApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
A ContentView
usa o PostViewModel
para buscar e exibir os posts. Quando a view aparece (onAppear
), o getPosts
é chamado para iniciar a requisição.
Considerações Finais
Pessoal entender escaping closures é fundamental para trabalhar com operações assíncronas em Swift e SwiftUI. Eles permitem que você gerencie callbacks e processos que precisam continuar a execução depois que a função inicial já retornou, como requisições de rede.
Espero ter ajudado para que você possa começar a usar escaping closures em seus próximos projetos SwiftUI.
Fique à vontade para experimentar e ajustar o código para se adequar às suas necessidades.
Até a próxima e bons estudos! 👨🏽🎓 🚀
“Aprender é a única coisa de que a mente nunca se cansa, nunca tem medo e nunca se arrepende.” — Autor Desconhecido