Skip to content
Simon J Stuart edited this page Sep 5, 2022 · 4 revisions

Welcome to the EventDrivenSwift wiki!

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.

What is Event Driven Development?

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

Traditional Observer Pattern necessitates direct awareness of an Observable from an Observer, which looks like this: Diagram of Traditional Observer Pattern 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.

Interface-Backed (Protocol-Backed) Observer Pattern

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. Diagram of Protocol-Backed Observer Pattern 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 both DataModelRepositoryAdded and DataModelRepositoryDeleted, so will be notified every time a Data Model is Added or Removed, thus invalidating MyModelListView 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 only DataModelRepositoryUpdated, 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

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) Diagram of Event-Driven Observer Pattern

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.

There are no Silver Bullets!

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.

Performance-Centric

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 EventThreads 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!

Built on Observable

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 EventThreads 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.

Built on ThreadSafeSwift

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.