Programação Reativa em Swift (iOS/macOS): Combine e RxSwift
Programação reativa é um paradigma baseado no trabalho com fluxos de dados assíncronos e propagação de mudanças. No ecossistema Apple para iOS e macOS, as principais ferramentas são o framework nativo Combine (desde iOS 13/macOS 10.15) e a biblioteca de terceiros RxSwift. Ambos permitem escrever código declarativo, conciso e facilmente testável, livrando o desenvolvedor do "callback hell" e do gerenciamento complexo de estado.
Conceitos Principais
- Publisher (Publicador) — fonte de dados que pode enviar valores, conclusão ou erro.
- Subscriber (Assinante) — recebe dados do Publisher e reage a eles.
- Operator (Operador) — função de transformação do fluxo (map, filter, combineLatest, debounce, etc.).
- Subject — híbrido que pode tanto receber quanto enviar eventos (PassthroughSubject, CurrentValueSubject).
- Scheduler (Agendador) — determina em qual thread o código é executado.
Combine: framework nativo da Apple
Combine está integrado ao Foundation e trabalha com Publisher e Subscriber. Exemplo de uso para processamento de campo de texto:
import Combineimport UIKit
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var label: UILabel! private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() { super.viewDidLoad() // Criando um Publisher a partir do NotificationCenter let publisher = NotificationCenter.default .publisher(for: UITextField.textDidChangeNotification, object: textField) .compactMap { ($0.object as? UITextField)?.text } .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .removeDuplicates() // Assinando publisher .sink { [weak self] value in self?.label.text = "Você digitou: \\(value)" } .store(in: &cancellables) }}No exemplo, usa-se debounce para atrasar a reação à digitação e removeDuplicates para ignorar valores repetidos.
RxSwift: poderosa alternativa
RxSwift é um port do ReactiveX para Swift. Oferece um conjunto mais amplo de operadores e suporta versões mais antigas do iOS. O mesmo exemplo com RxSwift:
import RxSwiftimport RxCocoa
class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var label: UILabel! private let disposeBag = DisposeBag()
override func viewDidLoad() { super.viewDidLoad() textField.rx.text .orEmpty .debounce(.milliseconds(500), scheduler: MainScheduler.instance) .distinctUntilChanged() .subscribe(onNext: { [weak self] text in self?.label.text = "Você digitou: \\(text)" }) .disposed(by: disposeBag) }}Observe o uso de rx.text (via RxCocoa) e distinctUntilChanged() — análogo ao removeDuplicates do Combine.
Operadores Chave
map/compactMap— transformação de valores.filter— permite apenas valores que satisfazem uma condição.combineLatest— combina os últimos valores de múltiplos fluxos.zip— combina elementos aos pares de múltiplos fluxos.flatMap— transforma um fluxo em outro (frequentemente usado para requisições de rede).catch/retry— tratamento de erros.