Skip to content

Commit 047c2bd

Browse files
authored
Merge pull request #1 from Flowduino/event-listeners
Version 3.0.0
2 parents eeb980f + 2399ec9 commit 047c2bd

14 files changed

+573
-202
lines changed

README.md

+49-3
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ let package = Package(
125125
dependencies: [
126126
.package(
127127
url: "https://github.com/Flowduino/EventDrivenSwift.git",
128-
.upToNextMajor(from: "2.0.0")
128+
.upToNextMajor(from: "3.0.0")
129129
),
130130
],
131131
//...
@@ -346,13 +346,59 @@ As you can see, we can create and *Dispatch* an *Event* in a single operation. T
346346
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.
347347

348348
## `UIEventReceiver`
349-
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`.
349+
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`.
350+
351+
## `EventListener`
352+
Version 3.0.0 introduced the `EventListener` concept to the Library.
353+
354+
An `EventListener` is a universal way of subscribing to *Events*, anywhere in your code!
355+
356+
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.
357+
358+
**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.
359+
360+
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.
361+
362+
For the sake of this example, let's define this the pure SwiftUI way, *without* taking advantage of our `Observable` library:
363+
```swift
364+
class TemperatureRatingViewModel: ObservableObject {
365+
@Published var temperatureInCelsius: Float
366+
@Published var temperatureRating: TemperatureRating
367+
368+
var listenerToken: UUID
369+
370+
internal func onTemperatureRatingEvent(_ event: TemperatureRatingEvent, _ priority: EventPriority) {
371+
temperatureInCelsius = event.temperatureInCelsius
372+
temperatureRating = event.temperatureRating
373+
}
374+
375+
init() {
376+
// Let's register our Event Listener Callback!
377+
listenerToken = TemperatureRatingEvent.addListener(self, onTemperatureRatingEvent)
378+
}
379+
}
380+
```
381+
It really is **that** simple!
382+
383+
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*.
384+
385+
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*!
386+
387+
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!
388+
389+
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:
390+
```swift
391+
TemperatureRatingEvent.removeListener(listenerToken)
392+
```
393+
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.
394+
395+
`EventListener`s are an extremely versatile and very powerful addition to `EventDrivenSwift`.
350396

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

355-
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*.
401+
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*.
356402

357403
## License
358404

Sources/EventDrivenSwift/Central/EventCentral.swift

+30-10
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ final public class EventCentral: EventDispatcher, EventCentralable {
2121
- Author: Simon J. Stuart
2222
- Version: 1.0.0
2323
*/
24-
private static var _shared: EventDispatchable = EventCentral()
24+
private static var _shared: EventCentral = EventCentral()
2525

2626
/**
2727
Returns the Central Event Dispatcher
@@ -40,19 +40,16 @@ final public class EventCentral: EventDispatcher, EventCentralable {
4040
}
4141
}
4242

43-
/// This just makes it so that your code cannot initialise instances of `EventCentral`. It's a Singleton!
44-
override private init() {}
45-
46-
@inline(__always) public static func addListener(_ listener: EventReceivable, forEventType: Eventable.Type) {
47-
_shared.addListener(listener, forEventType: forEventType)
43+
@inline(__always) public static func addReceiver(_ receiver: EventReceivable, forEventType: Eventable.Type) {
44+
_shared.addReceiver(receiver, forEventType: forEventType)
4845
}
4946

50-
@inline(__always) public static func removeListener(_ listener: EventReceivable, forEventType: Eventable.Type) {
51-
_shared.removeListener(listener, forEventType: forEventType)
47+
@inline(__always) public static func removeReceiver(_ receiver: EventReceivable, forEventType: Eventable.Type) {
48+
_shared.removeReceiver(receiver, forEventType: forEventType)
5249
}
5350

54-
@inline(__always) public static func removeListener(_ listener: EventReceivable) {
55-
_shared.removeListener(listener)
51+
@inline(__always) public static func removeReceiver(_ receiver: EventReceivable) {
52+
_shared.removeReceiver(receiver)
5653
}
5754

5855
@inline(__always) public static func queueEvent(_ event: Eventable, priority: EventPriority) {
@@ -68,4 +65,27 @@ final public class EventCentral: EventDispatcher, EventCentralable {
6865
return _shared.eventCount
6966
}
7067
}
68+
69+
private var _eventListener: EventListenable?
70+
internal var eventListener: EventListenable {
71+
get {
72+
if _eventListener == nil { _eventListener = EventListener() }
73+
return _eventListener!
74+
}
75+
}
76+
77+
@discardableResult @inline(__always) public static func addListener<TEvent>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID where TEvent : Eventable {
78+
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType)
79+
}
80+
81+
@inline(__always) public static func removeListener(_ token: UUID) {
82+
_shared.eventListener.removeListener(token)
83+
}
84+
85+
@inline(__always) public static func removeListener(_ token: UUID, typeOf: Eventable.Type) {
86+
_shared.eventListener.removeListener(token, typeOf: typeOf)
87+
}
88+
89+
/// This just makes it so that your code cannot initialise instances of `EventCentral`. It's a Singleton!
90+
override private init() {}
7191
}

Sources/EventDrivenSwift/Central/EventCentralable.swift

+38-6
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ import Foundation
1010

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

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

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

3333
/**
3434
Adds the given `event` to the Central Event Queue with the given `priority`
@@ -57,4 +57,36 @@ public protocol EventCentralable {
5757
- Returns: The number of Events currently pending in the Queue and Stack combined
5858
*/
5959
static var eventCount: Int { get }
60+
61+
/**
62+
Registers an Event Listner Callback for the given `Eventable` Type with the Central Event Listener
63+
- Author: Simon J. Stuart
64+
- Version: 3.0.0
65+
- Parameters:
66+
- requester: The Object owning the Callback Method
67+
- callback: The code to invoke for the given `Eventable` Type
68+
- forEventType: The `Eventable` Type for which to Register the Callback
69+
- Returns: A `UUID` value representing the `token` associated with this Event Callback
70+
*/
71+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID
72+
73+
/**
74+
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
75+
- Author: Simon J. Stuart
76+
- Version: 3.0.0
77+
- Parameters:
78+
- token: The Token of the Listener you wish to remove
79+
- Note: Using this method is far slower than if you provide the `typeOf` Parameter to satisfy the more-specific overloaded version of this method
80+
*/
81+
static func removeListener(_ token: UUID)
82+
83+
/**
84+
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
85+
- Author: Simon J. Stuart
86+
- Version: 3.0.0
87+
- Parameters:
88+
- token: The Token of the Listener you wish to remove
89+
- typeOf: The Event Type for which the Listener identified by the given `token` is interested
90+
*/
91+
static func removeListener(_ token: UUID, typeOf: Eventable.Type)
6092
}

Sources/EventDrivenSwift/Event/Eventable.swift

+30-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ public protocol Eventable {
3333
- priority: The Priority with which to process this Event
3434
*/
3535
func stack(priority: EventPriority)
36+
37+
/**
38+
Registers an Event Listner Callback for the given `Eventable` Type with the Central Event Listener
39+
- Author: Simon J. Stuart
40+
- Version: 3.0.0
41+
- Parameters:
42+
- requester: The Object owning the Callback Method
43+
- callback: The code to invoke for the given `Eventable` Type
44+
- Returns: A `UUID` value representing the `token` associated with this Event Callback
45+
*/
46+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>) -> UUID
47+
48+
/**
49+
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
50+
- Author: Simon J. Stuart
51+
- Version: 3.0.0
52+
- Parameters:
53+
- token: The Token of the Listener you wish to remove
54+
*/
55+
static func removeListener(_ token: UUID)
3656
}
3757

3858
/**
@@ -53,10 +73,18 @@ extension Eventable {
5373
*/
5474
extension Eventable {
5575
public func queue(priority: EventPriority = .normal) {
56-
EventCentral.shared.queueEvent(self, priority: priority)
76+
EventCentral.queueEvent(self, priority: priority)
5777
}
5878

5979
public func stack(priority: EventPriority = .normal) {
60-
EventCentral.shared.stackEvent(self, priority: priority)
80+
EventCentral.stackEvent(self, priority: priority)
81+
}
82+
83+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>) -> UUID {
84+
return EventCentral.addListener(requester, callback, forEventType: Self.self)
85+
}
86+
87+
static func removeListener(_ token: UUID) {
88+
EventCentral.removeListener(token, typeOf: Self.self)
6189
}
6290
}

Sources/EventDrivenSwift/EventDispatcher/EventDispatchable.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@ import Foundation
1515
*/
1616
public protocol EventDispatchable: EventHandlable {
1717
/**
18-
Registers the given `listener` for the given `Eventable` Type
18+
Registers the given `receiver` for the given `Eventable` Type
1919
- Author: Simon J. Stuart
2020
- Version: 1.0.0
2121
*/
22-
func addListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
22+
func addReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)
2323

2424
/**
25-
Unregisters the given `listener` from the given `Eventable` Type
25+
Unregisters the given `receiver` from the given `Eventable` Type
2626
- Author: Simon J. Stuart
2727
- Version: 1.0.0
2828
*/
29-
func removeListener(_ listener: any EventReceivable, forEventType: Eventable.Type)
29+
func removeReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type)
3030

3131
/**
32-
Unregisters the given `listener` from all `Eventable` Types
32+
Unregisters the given `receiver` from all `Eventable` Types
3333
- Author: Simon J. Stuart
3434
- Version: 1.0.0
3535
*/
36-
func removeListener(_ listener: any EventReceivable)
36+
func removeReceiver(_ receiver: any EventReceivable)
3737
}

0 commit comments

Comments
 (0)