-
-
Notifications
You must be signed in to change notification settings - Fork 0
Home
The purpose of this Wiki is to provide you with a central location to learn about every aspect of EventDrivenSwift
, particularly in respect of integrating this library into your Swift projects.
Event-Driven Development is a modern expansion of the traditional Observer Pattern.
Where Event-Driven Development differs is that, instead of the Observer having to directly register itself with the Observable; in an Event-Driven System, the Observer does not need to be contextually aware of the Observable at all.
Instead, individual Components of your system can - whenever they need to - Listen for and Dispatch Events, which are nothing more than a Payload of Information with a Defined Type.
In this way, what would have been considered an Observer is now entirely Decoupled from what would have been considered the Observable.
Even better, the Observer does not need to care about where an Event is coming from. In this way, multiple Components within your system can produce and Dispatch Events, and the Observer can treat them all equally.
Traditional Observer Pattern necessitates direct awareness of an Observable from an Observer, which looks like this: In the above diagram, you can see that each View Model is tightly-coupled to the Data Model Repository.
While we can use Interfaces to provide some degree of decoupling by abstracting our references via the Interface, meaning that any Concrete Implementation of that Interface will work interchangeably for each of the View Models, there are still a number of significant disadvantages to the Traditional Observer Pattern:
- Each Observer (the View Models in the above example) presumes that all Data Models will originate from a singular location. In many scenarios, this would not be the case.
- Every time the Observable changes in any way, or for any reason, all Observers (View Models in the above example) would be invalidated (a state which necessitates an update), thus the associated Views would be invalidated also. This represents a considerable inefficiency, as it is more often not necessary to update an entire View in response to any change. Traditional Observer Pattern doesn't particularly cater to optimised performance.
An enhancement over Traditional Observer Pattern is a concept known as Interfaced-Backed Observer Pattern. In the Swift programming language, we would call this Protocol-Backed Observer Pattern.
This is where an Observer conforms to one (or more) Interfaces (Protocols in Swift) that the Observable has been programmed to invoke in response to specific operations. While the above Diagram may look a little bit complicated at first glance, if you follow each line carefully, you will see that it simply illustrates how each of the three key Operations in the Observable (Data Model Repository) will iterate through all Subscribed Observers and invoke the appropriate method for all Observers conforming to the Protocol applicable to that Operation.
-
MyModelListViewModel
implements bothDataModelRepositoryAdded
andDataModelRepositoryDeleted
, so will be notified every time a Data Model is Added or Removed, thus invalidatingMyModelListView
and displaying immediately the results of these changes. However, it will not be notified when a Data Model is Updated (i.e. Modified). -
MyModelDetailViewModel
implements onlyDataModelRepositoryUpdated
, and so will only be notified every time a Data Model is Updated (i.e. Modified), but not when Models are Added or Removed.
As you can see, this approach - which is provided by our own Observable
Library for Swift - affords a number of advantages over the Traditional Observer Pattern, in that Observers can be infinitely-selective of which Observable operations are relevant to the Observer.
However, the following disadvantage still applies:
- Each Observer (the View Models in the above example) presumes that all Data Models will originate from a singular location. In many scenarios, this would not be the case.
While it is possible for a single Observer to subscribe to multiple Observables, this would simply increase the number of Tight Couplings you need to maintain in your codebase.
So, how do we eliminate these couplings completely?
Event-Driven Observer Pattern is the next evolution, expanding upon Protocol-Backed Observer Pattern, only dispensing with the need for the Observer to know anything at all about one or more Observables.
In Event-Driven Observer Pattern, the Events themselves constitute the Observable, meaning that it doesn't matter where an Event originates from, literally any Object anywhere in your code can Listen for any number of Event Types, and operate upon the Event Payload in the Context applicable to that Observer (Listener, when we're talking about Event-Driven systems)
As you can see in the above Diagram, there is no longer any coupling between the View Models and the Data Model Repository. Indeed, the View Models do not even need to be aware of the existence of the Data Model Repository. They will simply receive the applicable Events whenever an operation takes place, and act upon it.
Always remember that software design and development are about determining and applying the best pattern, practice, and approach for each individual problem. There is no such thing as a one-size fits all solution in software, so it is essential that you determine - as part of your planning stage - which pattern/practice/approach (or combination thereof) best resolves each given requirement within your system.
Event-Driven Observer Pattern is an extremely powerful way of abstracting discrete Components of your system, while retaining total Interoperability.
EventDrivenSwift
is designed specifically to provide the best possible performance balance both at the point of Dispatching an Event, as well as at the point of Processing an Event.
With this in mind, EventDrivenSwift
provides a Central Event Dispatch Handler by default. Whenever you Dispatch an Event through either a Queue or Stack, it will be immediately enqueued within the Central Event Dispatch Handler, where it will subsequently be Dispatched to all registered EventThread
s through its own Thread.
This means that there is a near-zero wait time between instructing an Event to Dispatch, and continuing on in the invoking Thread's execution.
Despite using an intermediary Handler in this manner, the time between Dispatch of an Event and the Processing of that Event by each EventThread
is impressively short! This makes EventDrivenSwift
more than useful for performance-critical applications, including videogames!
EventDrivenSwift
is built on top of our Observable
library, and EventThread
descends from ObservableThread
, meaning that it supports full Observer Pattern behaviour as well as Event-Driven behaviour.
Put simply: you can Observe EventThread
s anywhere in your code that it is necessary, including from SwiftUI Views.
This means that your application can dynamically update your Views in response to Events being received and processed, making your application truly and fully multi-threaded, without you having to produce code to handle the intra-Thread communication yourself.
EventDrivenSwift
is also built on top of our ThreadSafeSwift
library, and every public method and member of every type in EventDrivenSwift
is designed specifically to be Thread-Safe.
It is strongly recommended that your own implementations using EventDrivenSwift
adhere strictly to the best Thread-Safe standards. With that said, unless you are defining a var
or func
that is accessible publicly specifically for the purpose of displaying information on the UI, most back-end implemenations built with a pure Event-Driven methodology will not need to concern themselves too much with Thread-Safety.
If you like EventDrivenSwift
(and any of our other work at Flowduino) please considering Sponsoring our Projects