Event Bus e padrão Observer em Swift (iOS/macOS)
No desenvolvimento para iOS e macOS, surge frequentemente a necessidade de organizar uma interação fracamente acoplada entre os componentes do aplicativo. O padrão Observer e sua implementação através de um barramento de eventos (Event Bus) são ferramentas poderosas para criar uma arquitetura flexível e escalável. Neste artigo, vamos analisar como implementar um Event Bus em Swift, compará-lo com os mecanismos integrados da Apple (NotificationCenter, Combine) e mostrar exemplos práticos para iOS/macOS.
O que é Event Bus e padrão Observer?
Padrão Observer 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 sobre isso.
Event Bus (barramento de eventos) é um mecanismo centralizado que permite que qualquer componente envie eventos (publique) e os receba (assine), sem se conhecerem diretamente. Isso implementa o princípio do acoplamento fraco (loose coupling).
No ecossistema da Apple, já existem soluções integradas:
- NotificationCenter — implementação clássica do Observer, mas com limitações de tipagem e segurança.
- Combine — framework moderno para programação reativa, fornecendo poderosos Publisher/Subscriber.
No entanto, um Event Bus personalizado oferece controle total e pode ser mais facilmente integrado em projetos legados.
Implementação de um Event Bus simples em Swift
Vamos criar um barramento de eventos universal usando genéricos e referências fracas para evitar vazamentos de memória.
import Foundation
// Protocolo para eventosprotocol Event { }
// Implementação do Event Busfinal class EventBus { static let shared = EventBus() private var observers = [ObjectIdentifier: [WeakObserver]]() private init() { } // Assinatura de evento func subscribe<T: Event>(observer: AnyObject, handler: @escaping (T) -> Void) { let id = ObjectIdentifier(T.self) let weakRef = WeakObserver(observer: observer, handler: { event in guard let castedEvent = event as? T else { return } handler(castedEvent) }) if observers[id] != nil { observers[id]?.append(weakRef) } else { observers[id] = [weakRef] } // Limpeza de referências mortas cleanDeadObservers(for: id) } // Publicação de evento func publish<T: Event>(_ event: T) { let id = ObjectIdentifier(T.self) guard let observerList = observers[id] else { return } for weakObserver in observerList { weakObserver.handler?(event) } // Remove objetos liberados cleanDeadObservers(for: id) } // Cancelamento de assinatura func unsubscribe(observer: AnyObject, eventType: Event.Type) { let id = ObjectIdentifier(eventType) observers[id]?.removeAll { $0.observer === observer } } private func cleanDeadObservers(for id: ObjectIdentifier) { observers[id]?.removeAll { $0.observer == nil } }}
// Classe auxiliar para referência fracaprivate class WeakObserver { weak var observer: AnyObject? var handler: ((Event) -> Void)? init(observer: AnyObject, handler: @escaping (Event) -> Void) { self.observer = observer self.handler = handler }}Exemplo de uso
Vamos definir eventos personalizados:
struct UserLoggedInEvent: Event { let userId: String let userName: String}
struct DataLoadedEvent: Event { let data: [String]}Uso em um ViewController:
class ProfileViewController: UIViewController { override func Também na biblioteca
Validação de dados em C++: bibliotecas e métodos (libvalidate, nlohmann/json, Boost)
Validação de dados em JavaScript (Node.js): Joi, Yup, Zod
Processamento de imagens em C#: redimensionar e cortar com a biblioteca ImageSharp
Apache HttpClient para Java: guia completo com exemplos