Skip to content

Listeners

Giuseppe Lanza edited this page Dec 2, 2018 · 3 revisions

A listener is a main target feature. It is written to react to module events (or a specific set of a module's events). Each events listener should fulfil the single responsibility principle.

There are two types of Events Listeners: Generic ones and specific ones.

  • AnyEventsListenening conformance will give you generic listeners capable of registering to events from any producer.

  • EventsListening conformance will give you listeners for producers capable of emitting specific types of events.

Events Listeners must be added to the module manager to be notified of the creation of new modules. In this phase, the module manager will ask all the known listeners to register to the module's events. If the specified listener is interested in the type of events that a module can produce, then it can subscribe to them.

Example 1:

class EventsLogger: AnyEventsListening {
    @discardableResult func registerToEvents(for producer: AnyEventsProducer) -> Bool {
        producer.anyEvent
            .map { String(describing: $0) }
            .subscribe(onNext: { print($0) })
            .disposed(by: producer.disposeBag)
        return true
    }
}

This events listener is listening to any event produced by any producer. The purpose of this listener is to log emitted event to standard output.

Example 2:

class ProductArrayAnalyticsEventsListener: EventsListening {
    @discardableResult func registerToEvents(for producer: AnyEventsProducer, events: Observable<ProductArrayEvent>) -> Bool {
        producer[event: ProductArrayEvent.productSelected]
            .subscribe(onNext: { [weak self] (product) in
                print("[Analytics] Product \(product.productName) with iD \(product.remoteId) was selected")
                self?.track(event: "product.selected", properties:["id": product.remoteId])
            }).disposed(by: producer.disposeBag)

        return true
    }

    func track(event: String, properties: [String: Any]? = nil) { ... }
}

This events listener is interested in ProductArrayEvent and is logging the event of productSelected using an analytics provider every time this event occurs.

Example 3:

class MainFlowRoutingEventsListener: EventsListening, Routing {
    let router: Router
    
    init(router: Router) {
        self.router = router
    }

    @discardableResult func registerToEvents(for producer: AnyEventsProducer, events: Observable<ProductArrayEvent>) -> Bool {
        producer[event: ProductArrayEvent.productSelected]
            .toRoutableObservable()
            .subscribe(onNext: { [weak self] (product) in
                let step: ModuleRoutingStep = .product(routingContext: .mainFlow, id: product.remoteId, product: product)
                let presentableStep = PresentableRoutingStep(withStep: step, presentationMode: .push(withCloseButton: false, onClose: nil))
                self?.router.route(to: presentableStep)
            }).disposed(by: producer.disposeBag)

        return true
    }

The events listener in the example is EventsListening and it is Routing. This is a particular events listener that is listening to events that should cause routing. Specifically, in this listener, we are interested in all events coming from AnyEventsProducer able to produce ProductArrayEvent. Of these set of events, we are interested specifically in the productSelected event which indicates that a product in the list of products was selected. If this event happens, then we want to push on the screen a product detail page. For more info about routing please read the Routing section of this wiki.

Conclusion

AnyEventsProducers have a subscript that allows filtering by an event and extract their payload (if any). You can filter by event for any Observable<EventProtocol> by using the capture(event:) method.

In theory, there is no limit to the amount of events listeners that can be written to fulfil any specific reaction to events. It is, in fact, possible to have analytics events listeners for each module (highly recommended) and it is possible to have events listeners for specific purposes. (CoreSpotlight, NSUserActivity, Analytics {Provider a, b, c}, Main flow routing, Checkout flow routing, AppDelegate events, and so on...)