Event Bus e padrão Observer em Swift (iOS/macOS)
No desenvolvimento para iOS e macOS, frequentemente surge a necessidade de organizar uma interação fracamente acoplada entre os componentes do aplicativo. O padrão Observer e sua implementação na forma de Event Bus (barramento de eventos) é uma ferramenta poderosa para resolver esse problema. Neste artigo, vamos detalhar como implementar um Event Bus em Swift, usando recursos modernos da linguagem (Generics, closures, weak references).
O que é o padrão Observer?
O padrão Observer (Observador) define uma relação "um para muitos" entre objetos. Quando um objeto (publicador) altera seu estado, todos os objetos dependentes (assinantes) são automaticamente notificados e atualizados. Isso evita o acoplamento rígido e simplifica a manutenção do código.
Em Swift, as implementações nativas do Observer são:
NotificationCenter— um barramento global de notificações, embutido no Foundation.- Key-Value Observing (KVO) — um mecanismo para observar propriedades de objetos.
- Combine framework (iOS 13+) — um framework reativo com suporte a publicadores e assinantes.
No entanto, muitas vezes é necessária uma implementação mais leve e type-safe, especialmente ao trabalhar com código legado ou em projetos onde o Combine não está disponível.
Event Bus: uma implementação simples e flexível
Event Bus é um despachante central de eventos que permite assinar e enviar eventos sem conhecimento direto sobre os assinantes. Nós o implementaremos usando genéricos e referências fracas para evitar vazamentos de memória.
Implementação básica do Event Bus
import Foundation
// Protocolo para todos os eventosprotocol Event { }
// Tipos de eventos concretosstruct UserLoggedInEvent: Event { let userId: String let userName: String}
struct DataLoadedEvent: Event { let data: [String]}
// Classe Event Busfinal class EventBus { static let shared = EventBus() private init() {} // Dicionário para armazenar assinantes private var subscribers = [ObjectIdentifier: [Any]]() // Assinar um evento func subscribe<T: Event>(observer: AnyObject, handler: @escaping (T) -> Void) { let key = ObjectIdentifier(T.self) let wrapped = Subscription(observer: observer, handler: handler as Any) if subscribers[key] != nil { subscribers[key]?.append(wrapped) } else { subscribers[key] = [wrapped] } } // Cancelar a assinatura de todos os eventos para um observador específico func unsubscribe(observer: AnyObject) { for key in subscribers.keys { subscribers[key]?.removeAll(where: { subscription in if let sub = subscription as? Subscription { return sub.observer === observer } return false }) } } // Publicar um evento func publish<T: Event>(event: T) { let key = ObjectIdentifier(T.self) guard let subs = subscribers[key] else { return } for subscription in subs { if let sub = subscription as? Subscription { if let handler = sub.handler as? (T) -> Void { handler(event) } } } }}
// Estrutura auxiliar para armazenar assinaturasprivate struct Subscription { weak var observer: AnyObject? let handler: Any init(observer: AnyObject, handler: Any) { self.observer = observer self.handler = handler }}Usando o Event Bus
// ViewController que assina eventosclass LoginViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Assinamos o evento