Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 3.0.0 #1

Merged
merged 2 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/Flowduino/EventDrivenSwift.git",
.upToNextMajor(from: "2.0.0")
.upToNextMajor(from: "3.0.0")
),
],
//...
Expand Down Expand Up @@ -346,13 +346,59 @@ As you can see, we can create and *Dispatch* an *Event* in a single operation. T
Now that we've walked through these basic Usage Examples, see if you can produce your own `EventReceiver` to process `TemperatureRatingEvent`s. Everything you need to achieve this has already been demonstrated in this document.

## `UIEventReceiver`
Version 2.0.0 introduces the `UIEventReceiver` base class, which operates exactly the same way as `EventReciever`, with the notable difference being that your registered *Event* Callbacks will **always** be invoked on the `MainActor` (or "UI Thread"). You can simply inherit from `UIEventReceiver` instead of `EventReceiver` whenever it is imperative for one or more *Event* Callbacks to execute on the `MainActor`.
Version 2.0.0 introduced the `UIEventReceiver` base class, which operates exactly the same way as `EventReciever`, with the notable difference being that your registered *Event* Callbacks will **always** be invoked on the `MainActor` (or "UI Thread"). You can simply inherit from `UIEventReceiver` instead of `EventReceiver` whenever it is imperative for one or more *Event* Callbacks to execute on the `MainActor`.

## `EventListener`
Version 3.0.0 introduced the `EventListener` concept to the Library.

An `EventListener` is a universal way of subscribing to *Events*, anywhere in your code!

By design, `EventDrivenSwift` provides a *Central Event Listener*, which is automatically initialized should any of your code register a *Listener* for an *Event* by reference to the `Eventable` type.

**Important Note:** `EventListener` will always invoke the associated `Callbacks` on the same Thread (or `DispatchQueue`) from whence the *Listener* registered! This is an extremely useful behaviour, because it means that *Listeners* registered from the `MainActor` (or "UI Thread") will always execute on that Thread, with no additional overhead or code required by you.

Let's register a simple *Listener* in some arbitrary `class`. For this example, let's produce a hypothetical *View Model* that will *Listen* for `TemperatureRatingEvent`, and would invalidate an owning `View` to show the newly-received values.

For the sake of this example, let's define this the pure SwiftUI way, *without* taking advantage of our `Observable` library:
```swift
class TemperatureRatingViewModel: ObservableObject {
@Published var temperatureInCelsius: Float
@Published var temperatureRating: TemperatureRating

var listenerToken: UUID

internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority) {
temperatureInCelsius = event.temperatureInCelsius
temperatureRating = event.temperatureRating
}

init() {
// Let's register our Event Listener Callback!
listenerToken = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent)
}
}
```
It really is **that** simple!

We can use a direct reference to an `Eventable` type, and invoke the `addListener` method, automatically bound to all `Eventable` types, to register our *Listener*.

In the above example, whenever the *Reciprocal Event* named `TemperatureRatingEvent` is dispatched, the `onTemperatureRatingEvent` method of any `TemperatureRatingViewModel` instance(s) will be invoked, in the context of that *Event*!

Don't worry about managing the lifetime of your *Listener*! If the object which owns the *Listener* is destroyed, the *Listener* will be automatically unregistered for you!

Another thing to note about the above example is the `listenerToken`. Whenever you register a *Listener*, it will return a Unique Universal ID (a `UUID`) value. You can use this value to *Unregister* your *Listener* at any time:
```swift
TemperatureRatingEvent.removeListener(listenerToken)
```
This way, when an *Event* is no longer relevant to your code, you can simply call `removeListener` against the `Eventable` type, and pass in the token returned when you added the *Listener* in the first place.

`EventListener`s are an extremely versatile and very powerful addition to `EventDrivenSwift`.

## Features Coming Soon
`EventDrivenSwift` is an evolving and ever-improving Library, so here is a list of the features you can expect in future releases:
- **Event Pools** - A superset expanding upon a given `EventReceiver` descendant type to provide pooled processing based on given scaling rules and conditions.

These are the features intended for the next Release, which will either be *2.1.0* or *3.0.0* depending on whether these additions require interface-breaking changes to the interfaces in version *2.0.0*.
These are the features intended for the next Release, which will either be *3.1.0* or *4.0.0* depending on whether these additions require interface-breaking changes to the interfaces in version *3.0.0*.

## License

Expand Down
40 changes: 30 additions & 10 deletions Sources/EventDrivenSwift/Central/EventCentral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final public class EventCentral: EventDispatcher, EventCentralable {
- Author: Simon J. Stuart
- Version: 1.0.0
*/
private static var _shared: EventDispatchable = EventCentral()
private static var _shared: EventCentral = EventCentral()

/**
Returns the Central Event Dispatcher
Expand All @@ -40,19 +40,16 @@ final public class EventCentral: EventDispatcher, EventCentralable {
}
}

/// This just makes it so that your code cannot initialise instances of `EventCentral`. It's a Singleton!
override private init() {}

@inline(__always) public static func addListener(_ listener: EventReceivable, forEventType: Eventable.Type) {
_shared.addListener(listener, forEventType: forEventType)
@inline(__always) public static func addReceiver(_ receiver: EventReceivable, forEventType: Eventable.Type) {
_shared.addReceiver(receiver, forEventType: forEventType)
}

@inline(__always) public static func removeListener(_ listener: EventReceivable, forEventType: Eventable.Type) {
_shared.removeListener(listener, forEventType: forEventType)
@inline(__always) public static func removeReceiver(_ receiver: EventReceivable, forEventType: Eventable.Type) {
_shared.removeReceiver(receiver, forEventType: forEventType)
}

@inline(__always) public static func removeListener(_ listener: EventReceivable) {
_shared.removeListener(listener)
@inline(__always) public static func removeReceiver(_ receiver: EventReceivable) {
_shared.removeReceiver(receiver)
}

@inline(__always) public static func queueEvent(_ event: Eventable, priority: EventPriority) {
Expand All @@ -68,4 +65,27 @@ final public class EventCentral: EventDispatcher, EventCentralable {
return _shared.eventCount
}
}

private var _eventListener: EventListenable?
internal var eventListener: EventListenable {
get {
if _eventListener == nil { _eventListener = EventListener() }
return _eventListener!
}
}

@discardableResult @inline(__always) public static func addListener<TEvent>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID where TEvent : Eventable {
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType)
}

@inline(__always) public static func removeListener(_ token: UUID) {
_shared.eventListener.removeListener(token)
}

@inline(__always) public static func removeListener(_ token: UUID, typeOf: Eventable.Type) {
_shared.eventListener.removeListener(token, typeOf: typeOf)
}

/// This just makes it so that your code cannot initialise instances of `EventCentral`. It's a Singleton!
override private init() {}
}
44 changes: 38 additions & 6 deletions Sources/EventDrivenSwift/Central/EventCentralable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ import Foundation

public protocol EventCentralable {
/**
Registers the given `listener` for the given `Eventable` Type with the Central Event Dispatcher
Registers the given `receiver` for the given `Eventable` Type with the Central Event Dispatcher
- Author: Simon J. Stuart
- Version: 1.0.0
*/
static func addListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
static func addReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)

/**
Unregisters the given `listener` from the given `Eventable` Type for the Central Event Dispatcher
Unregisters the given `receiver` from the given `Eventable` Type for the Central Event Dispatcher
- Author: Simon J. Stuart
- Version: 1.0.0
*/
static func removeListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
static func removeReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)

/**
Unregisters the given `listener` from all `Eventable` Types from the Central Event Dispatcher
Unregisters the given `receiver` from all `Eventable` Types from the Central Event Dispatcher
- Author: Simon J. Stuart
- Version: 1.0.0
*/
static func removeListener(_ listener: any EventReceivable)
static func removeReceiver(_ receiver: any EventReceivable)

/**
Adds the given `event` to the Central Event Queue with the given `priority`
Expand Down Expand Up @@ -57,4 +57,36 @@ public protocol EventCentralable {
- Returns: The number of Events currently pending in the Queue and Stack combined
*/
static var eventCount: Int { get }

/**
Registers an Event Listner Callback for the given `Eventable` Type with the Central Event Listener
- Author: Simon J. Stuart
- Version: 3.0.0
- Parameters:
- requester: The Object owning the Callback Method
- callback: The code to invoke for the given `Eventable` Type
- forEventType: The `Eventable` Type for which to Register the Callback
- Returns: A `UUID` value representing the `token` associated with this Event Callback
*/
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID

/**
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
- Author: Simon J. Stuart
- Version: 3.0.0
- Parameters:
- token: The Token of the Listener you wish to remove
- Note: Using this method is far slower than if you provide the `typeOf` Parameter to satisfy the more-specific overloaded version of this method
*/
static func removeListener(_ token: UUID)

/**
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
- Author: Simon J. Stuart
- Version: 3.0.0
- Parameters:
- token: The Token of the Listener you wish to remove
- typeOf: The Event Type for which the Listener identified by the given `token` is interested
*/
static func removeListener(_ token: UUID, typeOf: Eventable.Type)
}
32 changes: 30 additions & 2 deletions Sources/EventDrivenSwift/Event/Eventable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ public protocol Eventable {
- priority: The Priority with which to process this Event
*/
func stack(priority: EventPriority)

/**
Registers an Event Listner Callback for the given `Eventable` Type with the Central Event Listener
- Author: Simon J. Stuart
- Version: 3.0.0
- Parameters:
- requester: The Object owning the Callback Method
- callback: The code to invoke for the given `Eventable` Type
- Returns: A `UUID` value representing the `token` associated with this Event Callback
*/
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>) -> UUID

/**
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
- Author: Simon J. Stuart
- Version: 3.0.0
- Parameters:
- token: The Token of the Listener you wish to remove
*/
static func removeListener(_ token: UUID)
}

/**
Expand All @@ -53,10 +73,18 @@ extension Eventable {
*/
extension Eventable {
public func queue(priority: EventPriority = .normal) {
EventCentral.shared.queueEvent(self, priority: priority)
EventCentral.queueEvent(self, priority: priority)
}

public func stack(priority: EventPriority = .normal) {
EventCentral.shared.stackEvent(self, priority: priority)
EventCentral.stackEvent(self, priority: priority)
}

@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>) -> UUID {
return EventCentral.addListener(requester, callback, forEventType: Self.self)
}

static func removeListener(_ token: UUID) {
EventCentral.removeListener(token, typeOf: Self.self)
}
}
12 changes: 6 additions & 6 deletions Sources/EventDrivenSwift/EventDispatcher/EventDispatchable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ import Foundation
*/
public protocol EventDispatchable: EventHandlable {
/**
Registers the given `listener` for the given `Eventable` Type
Registers the given `receiver` for the given `Eventable` Type
- Author: Simon J. Stuart
- Version: 1.0.0
*/
func addListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
func addReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)

/**
Unregisters the given `listener` from the given `Eventable` Type
Unregisters the given `receiver` from the given `Eventable` Type
- Author: Simon J. Stuart
- Version: 1.0.0
*/
func removeListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
func removeReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)

/**
Unregisters the given `listener` from all `Eventable` Types
Unregisters the given `receiver` from all `Eventable` Types
- Author: Simon J. Stuart
- Version: 1.0.0
*/
func removeListener(_ listener: any EventReceivable)
func removeReceiver(_ receiver: any EventReceivable)
}
Loading