Programação Reativa em Swift: Combine e RxSwift
Programação reativa é um paradigma orientado a fluxos de dados assíncronos e propagação de mudanças. No ecossistema Swift (iOS/macOS), as principais ferramentas para implementar esse paradigma são o framework Combine (integrado ao Apple SDK) e a biblioteca de terceiros RxSwift. Ambas as ferramentas permitem descrever declarativamente o tratamento de eventos, reduzindo a quantidade de funções callback e tornando o código mais legível e previsível.
Neste artigo, exploraremos os conceitos-chave da programação reativa, compararemos Combine e RxSwift e forneceremos exemplos práticos para iOS e macOS.
Conceitos Principais
Na base da abordagem reativa estão três entidades:
- Publisher (Observable) — fonte de dados que gera eventos (valores, erros, conclusão).
- Subscriber (Observer) — consumidor que reage aos eventos.
- Operator — função que transforma o fluxo de dados (filtragem, transformação, combinação).
Exemplo de um Publisher simples no Combine:
import Combine
let publisher = Just("Hello, Reactive World!")let subscription = publisher.sink { value in print(value)}// Saída: Hello, Reactive World!Combine vs RxSwift: Comparação
Ambos os frameworks resolvem problemas semelhantes, mas possuem diferenças:
- Combine — framework nativo da Apple (iOS 13+, macOS 10.15+). Usa tipagem estrita e integra-se com SwiftUI e URLSession.
- RxSwift — biblioteca multiplataforma com uma grande comunidade. Suporta versões mais antigas do iOS (a partir do iOS 8).
- Desempenho: Combine é otimizado para Apple Silicon, RxSwift pode ser mais flexível em cenários personalizados.
Exemplos de Código para iOS/macOS
1. Tratamento Reativo de Campo de Texto (Combine + UIKit)
Inscrevendo-se em alterações de texto em UITextField:
import UIKitimport Combine
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField) .compactMap { ($0.object as? UITextField)?.text } .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .sink { text in print("Usuário digitou: \\(text)") // Aqui pode fazer uma requisição de rede ou atualizar a UI } .store(in: &cancellables) }}2. Combinando Múltiplos Fluxos (RxSwift para macOS)
Exemplo de combinação de duas sequências observable:
import RxSwift
let disposeBag = DisposeBag()
let firstObservable = Observable.of(1, 2, 3)let secondObservable = Observable.of("A", "B", "C")
Observable.zip(firstObservable, secondObservable) .subscribe(onNext: { (number, letter) in print("\\(number) -> \\(letter)") }) .disposed(by: disposeBag)
// Saída:// 1 -> A// 2 -> B// 3 -> C3. Tratamento de Requisição de Rede com Combine
Usando URLSession para carregar dados:
import Combineimport Foundation
struct Post: Decodable { let id: Int let title: String}
func fetchPosts() -> AnyPublisher<[Post], Error> { let url = URL(string: "https://jsonplaceholder.typicode.com/posts")! return URLSession.shared.dataTaskPublisher(for: url) .map(\\.data) .decode(type: [Post].self, decoder: JSONDecoder()) .eraseToAnyPublisher()} Também na biblioteca
Configuração de aplicações em Rust: bibliotecas config e dotenv
Caffeine para Java: guia completo com exemplos práticos
UUID em JavaScript (Node.js): guia completo com exemplos práticos
Configuração de aplicações em C++: bibliotecas e ferramentas para configuração flexível