Sobes.tech
Middle

Alguma vez usou a biblioteca Combine nos seus projetos?

sobes.tech IA

Resposta da IA

Sim, já utilizei.

A principal aplicação do Combine nos meus projetos está relacionada com o processamento de eventos assíncronos, tais como:

  • Requisições de rede: processamento de respostas de API, transformação de dados, tratamento de erros.
  • Alterações nos elementos de UI: reação a ações do utilizador, alterações em campos de texto, deslizes.
  • Processamento de dados locais: acompanhamento de alterações em bases de dados (por exemplo, Core Data), notificações do FileManager.
  • Criação de fluxos de dados reativos na arquitetura MVVM para ligação entre a View e o ViewModel.

Vantagens do uso do Combine:

  1. Redução do volume de código e melhoria da sua legibilidade ao trabalhar com operações assíncronas.
  2. Gestão centralizada de erros e cancelamento de operações.
  3. Composição e transformação fáceis de fluxos de dados usando operadores.
  4. Integração nativa com frameworks da Apple (SwiftUI, Core Data, URLSession).

Exemplo de uso do Combine para uma requisição de rede:

import Combine
import Foundation

struct APIResult: Decodable {
    let value: String
}

func fetchData(from url: URL) -> AnyPublisher<APIResult, Error> {
    URLSession.shared.dataTaskPublisher(for: url)
        .map(\data) // Extrai os dados do tuple (data, response)
        .decode(type: APIResult.self, decoder: JSONDecoder()) // Decodifica os dados
        .receive(on: DispatchQueue.main) // Muda para o thread principal para atualizar a UI
        .eraseToAnyPublisher() // Remove o tipo específico para simplicidade
}

// Exemplo de subscrição
let cancellable = fetchData(from: URL(string: "https://api.example.com/data")!)
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("Requisição concluída com sucesso")
        case .failure(let error):
            print("Erro na requisição: \(error.localizedDescription)")
        }
    }, receiveValue: { result in
        print("Dados recebidos: \(result.value)")
    })
// É necessário guardar a referência a cancellable para manter a assinatura ativa

Também utilizei o Combine para ligar propriedades no MVVM:

class ViewModel {
    @Published var text: String = ""
    @Published var isValid: Bool = false

    private var cancellables = Set<AnyCancellable>()

    init() {
        $text
            .map { $0.count > 0 } // Transforma o texto em valor booleano
            .assign(to: &$isValid) // Atribui o resultado à propriedade isValid
    }
}

class ViewController: UIViewController {
    let textView = UITextField()
    let validationLabel = UILabel()
    let viewModel = ViewModel()

    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Ligação do UITextField ao ViewModel
        NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textView)
            .compactMap { ($0.object as? UITextField)?.text }
            .assign(to: \.text, on: viewModel) // Atribui o texto ao ViewModel

        // Ligação do ViewModel ao UILabel
        viewModel.$isValid
            .map { $0 ? "Válido" : "Inválido" }
            .assign(to: \.text, on: validationLabel) // Atribui o texto ao UILabel
            .store(in: &cancellables) // Guarda a assinatura
    }
}

A experiência com o Combine é positiva, pois simplifica bastante o trabalho com assíncronismo em comparação com abordagens mais antigas (closures, delegates) em cenários complexos.