VIPER of many faces — Part 4: EventEmitter iOS Demo
As announced in Part 3, it’s now time to show, how we have made our VIPER client architecture less convoluted and more forgiving with the use of Event Emitters.
EventEmitter class & Co.
I am sooooooooo… sorry to disappoint you, but this is all the code there is for this long saga of ours: 😧
Main stuff is done in our EventEmitterType
protocol and its extension. Using it is very easy:
- Subscribing object, conforming to
EventEmitting
subscribes by passing a closure with a simple[String: Any]
payload, which is handled later EventEmitterType
stores these closures in itscallbacks
array property- Event-creating objects, also conforming to
EventEmitting
send the event payload through theirEventEmitterType
property and itsnotify
function EventEmitterType
calls all the subscribing callbacks anonymously and passes the payload- Subscribing objects inspect the payload to see if it contains the information they are interested in
That’s all about our Event Emitting protocol reusable code, boilerplate, actually. Now let’s see the implementation of it:
DashboardEventEmitter
class contains literally only 2 properties which have to be instantiated to enable storing callbacks and serial queue to ensure synchronous payload delivery- Optional
DashboardEvent
enum to structure events, being able to see them all on one place. We could have more of such events enums per functionality, but it’s not really needed in our case here, where only 3 events are fired and handled
And that’s everything what we have to do about implementing the protocol. Very lean, very thin. 😊
Let’s see now how the event creation and handling is done.
Event Creation
OK, let’s see, how the events are created:
We have done event creation to our views in a following way:
- implemented
EventEmitting
protocol by simply adding weakly referencedDashboardEventEmitter
property - firing event with
notify
function and the payload which complies with convention defined by receiver, whoever that might be
One could argue that we should not put this into view object, but it’s very lean and easy to test, so I guess the argument doesn’t stand. Core idea is that we don’t traverse from one to other objects too many times just to deliver the event payload. We use only EventEmitterType
, added to the view at the time of its creation.
Event Handling
We have following events:
- “refundTapped”
- “expandTopHighlights”
- “paymentDetailOpen”
The first one is triggered by tapping the button “Request for refund” on the cell in Last Payments table view. This one creates payload for with 2 items, one for PaymentsModule and another with “analytics” key for AnalyticsModule, which we shall introduce sometime later.
The other 2 events are just typical Analytics events with “analytics” key and will be handled by special class we created for Analytics Event handling DashboardAnalytics
.
Let’s look at the code:
We can see we handle events in DashboardPresenter
and in new, specialised class DashboardAnalytics
. Presenter simply waits for “refundTapped” event and then fires the payload back to ApplicationServices
, which starts the DashboardModule
. DashboardAnalytics
picks up anything with the key “analytics” reads the content, adapts values to AnalyticsModule convention, defined my marketing people most probably (which is certainly not expected to be the same as our internal event naming) and then it also combines it into a payload and fires it back to ApplicationServices.
Let’s play and do the following:
- expand Top Highlights by tapping on any
- tap on one payment to see the details
- go back and tap “Request for refund” button
What ApplicationServices
should have written to console is this:
3 events, with 4 payload items, last event with 2: one for PaymentsModule and another one for AnalyticsModule.
Wrap-up
Take some time and look into the repository with this example. Try to figure out, how this approach could help your cases. The issue addressed in Part 1 seems to be handled pretty well with it.
You might ask why using this, why not using RxSwift or something similar? Well, FRP is much tougher, much more strong-opinionated approach, very dominating, once you start using it and because of this and many other reasons also unfortunately quite divisive among developers…
Approach presented in this series, but especially in this last part is very lean and could be changed per-need at any time. Also, event-based approach is somewhat easier to comprehend compared to reactive, although they are siblings in the core. For developers who might find reactive approach a bit intimidating, this might be good way to do their job.