Skip to content

Commit 8767aaf

Browse files
committed
Listeners support Latest Only
- `EventListener` now supports Latest Only Events - `EventHandler` now supplements each dispatched `Eventable` with a new `EventDispatchContainer` to encapsulate the `Eventable` alongside its `dispatchTime` - Necessary refactoring and interface changes made to support the above changes Next Up: Support for Latest Only Events on `EventReceiving` implementations.
1 parent 6cd336d commit 8767aaf

File tree

13 files changed

+187
-50
lines changed

13 files changed

+187
-50
lines changed

Sources/EventDrivenSwift/Central/EventCentral.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ final public class EventCentral: EventDispatcher, EventCentralable {
6666
return _shared.eventCount
6767
}
6868
}
69-
69+
7070
private var _eventListener: EventListenable?
7171
internal var eventListener: EventListenable {
7272
get {
@@ -75,8 +75,8 @@ final public class EventCentral: EventDispatcher, EventCentralable {
7575
}
7676
}
7777

78-
@discardableResult @inline(__always) public static func addListener<TEvent>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread) -> EventListenerHandling where TEvent : Eventable {
79-
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType, executeOn: executeOn)
78+
@discardableResult @inline(__always) public static func addListener<TEvent>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all) -> EventListenerHandling where TEvent : Eventable {
79+
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType, executeOn: executeOn, interestedIn: interestedIn)
8080
}
8181

8282
@inline(__always) public static func removeListener(_ token: UUID) {
@@ -87,11 +87,11 @@ final public class EventCentral: EventDispatcher, EventCentralable {
8787
_shared.eventListener.removeListener(token, typeOf: typeOf)
8888
}
8989

90-
@inline(__always) static public func scheduleQueue(_ event: Eventable, at: DispatchTime, priority: EventPriority) {
90+
@inline(__always) public static func scheduleQueue(_ event: Eventable, at: DispatchTime, priority: EventPriority) {
9191
_shared.scheduleQueue(event, at: at, priority: priority)
9292
}
9393

94-
@inline(__always) static public func scheduleStack(_ event: Eventable, at: DispatchTime, priority: EventPriority) {
94+
@inline(__always) public static func scheduleStack(_ event: Eventable, at: DispatchTime, priority: EventPriority) {
9595
_shared.scheduleStack(event, at: at, priority: priority)
9696
}
9797

Sources/EventDrivenSwift/Central/EventCentralable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public protocol EventCentralable {
6868
- forEventType: The `Eventable` Type for which to Register the Callback
6969
- Returns: A `UUID` value representing the `token` associated with this Event Callback
7070
*/
71-
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn) -> EventListenerHandling
71+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest) -> EventListenerHandling
7272

7373
/**
7474
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener

Sources/EventDrivenSwift/Event/Eventable.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ public protocol Eventable {
6363
- callback: The code to invoke for the given `Eventable` Type
6464
- Returns: A `UUID` value representing the `token` associated with this Event Callback
6565
*/
66-
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn) -> EventListenerHandling
67-
68-
// @discardableResult static func addListener(_ requester: AnyObject?, _ eventType: any Eventable.Type, _ callback: @escaping TypedEventCallback<any Eventable.Type>, executeOn: ExecuteEventOn) -> UUID
66+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest) -> EventListenerHandling
6967

7068
/**
7169
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
@@ -75,6 +73,20 @@ public protocol Eventable {
7573
- token: The Token of the Listener you wish to remove
7674
*/
7775
static func removeListener(_ token: UUID)
76+
77+
/**
78+
Returns the Fully-Qualified Type Name for this Eventable Type
79+
- Author: Simon J. Stuart
80+
- Version: 4.3.0
81+
*/
82+
func getEventTypeName() -> String
83+
84+
/**
85+
Returns the Fully-Qualified Type Name for this Eventable Type
86+
- Author: Simon J. Stuart
87+
- Version: 4.3.0
88+
*/
89+
static func getEventTypeName() -> String
7890
}
7991

8092
/**
@@ -110,12 +122,26 @@ extension Eventable {
110122
EventCentral.scheduleStack(self, at: at, priority: priority)
111123
}
112124

113-
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread) -> EventListenerHandling {
114-
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn)
125+
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all) -> EventListenerHandling {
126+
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn, interestedIn: interestedIn)
115127
}
116128

117129
public static func removeListener(_ token: UUID) {
118130
EventCentral.removeListener(token, typeOf: Self.self)
119131
}
120132
}
121133

134+
/**
135+
Extension to provide central access to Event Type Names
136+
- Author: Simon J. Stuart
137+
- Version: 4.3.0
138+
*/
139+
extension Eventable {
140+
@inline(__always) public func getEventTypeName() -> String {
141+
return String(reflecting: type(of: self))
142+
}
143+
144+
@inline(__always) public static func getEventTypeName() -> String {
145+
return String(reflecting: self)
146+
}
147+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// EventListeners.swift
3+
// Copyright (c) 2022, Flowduino
4+
// Authored by Simon J. Stuart on 21st August 2022
5+
//
6+
// Subject to terms, restrictions, and liability waiver of the MIT License
7+
//
8+
9+
import Foundation
10+
11+
public protocol EventListenerCallbackContainer {
12+
associatedtype TEventType: Eventable
13+
14+
static func buildBlock(_ callback: @escaping ((_ event: TEventType, _ priority: EventPriority) -> ()), _ owner: AnyObject, _ executeOn: ExecuteEventOn) -> EventListenerHandling
15+
}
16+
17+
@resultBuilder
18+
public struct EventListenerCallback<TEventType: Eventable>: EventListenerCallbackContainer {
19+
public static func buildBlock(_ callback: @escaping ((_ event: TEventType, _ priority: EventPriority) -> ()), _ owner: AnyObject, _ executeOn: ExecuteEventOn = .requesterThread) -> EventListenerHandling {
20+
return TEventType.addListener(owner, callback, executeOn: executeOn)
21+
}
22+
23+
init() {
24+
print("Init called")
25+
}
26+
}
27+
28+
@resultBuilder
29+
public struct EventListeners<TOwner: AnyObject> {
30+
public static func buildBlock(_ owner: TOwner, _ executeOn: ExecuteEventOn = .requesterThread, _ eventType: Eventable.Type, _ events: EventCallback...) -> [EventListenerHandling] {
31+
var results = [EventListenerHandling]()
32+
for event in events {
33+
// results.append(EventCentral.addListener(owner, event, forEventType: eventType))
34+
}
35+
return results
36+
}
37+
}

Sources/EventDrivenSwift/EventDispatcher/EventDispatcher.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ open class EventDispatcher: EventHandler, EventDispatching {
2929
@ThreadSafeSemaphore private var receivers = [String:[ReceiverContainer]]()
3030

3131
public func addReceiver(_ receiver: any EventReceiving, forEventType: Eventable.Type) {
32-
let eventTypeName = String(reflecting: forEventType)
32+
let eventTypeName = forEventType.getEventTypeName()
3333

3434
_receivers.withLock { receivers in
3535
var bucket = receivers[eventTypeName]
@@ -50,7 +50,7 @@ open class EventDispatcher: EventHandler, EventDispatching {
5050
}
5151

5252
public func removeReceiver(_ receiver: any EventReceiving, forEventType: Eventable.Type) {
53-
let eventTypeName = String(reflecting: forEventType)
53+
let eventTypeName = forEventType.getEventTypeName()
5454

5555
_receivers.withLock { receivers in
5656
var bucket = receivers[eventTypeName]
@@ -82,8 +82,8 @@ open class EventDispatcher: EventHandler, EventDispatching {
8282
- Author: Simon J. Stuart
8383
- Version: 1.0.0
8484
*/
85-
override open func processEvent(_ event: any Eventable, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
86-
let eventTypeName = String(reflecting: type(of: event))
85+
override open func processEvent(_ event: EventDispatchContainer, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
86+
let eventTypeName = event.event.getEventTypeName()
8787

8888
var snapReceivers = [String:[ReceiverContainer]]()
8989

Sources/EventDrivenSwift/EventHandler/EventHandler.swift

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,37 @@ import Observable
2121

2222
*/
2323
open class EventHandler: ObservableThread, EventHandling {
24+
/**
25+
A new Container to associate each Event with information about its Dispatch (such as Dispatch Time)
26+
- Author: Simon J. Stuart
27+
- Version: 4.3.0
28+
- Note: This was added principally to support Latest-Only Event Listeners/Callbacks
29+
*/
30+
public struct EventDispatchContainer {
31+
var event: any Eventable
32+
var dispatchTime: DispatchTime = DispatchTime.now()
33+
}
34+
2435
/**
2536
A Map of `EventPriority` against an Array of `Eventable` in that corresponding Queue
2637
- Author: Simon J. Stuart
2738
- Version: 1.0.0
2839
*/
29-
@ThreadSafeSemaphore internal var queues = [EventPriority:[any Eventable]]()
40+
@ThreadSafeSemaphore internal var queues = [EventPriority:[EventDispatchContainer]]()
3041

3142
/**
3243
A Map of `EventPriority` against an Array of `Eventable` in that corresponding Stack
3344
- Author: Simon J. Stuart
3445
- Version: 1.0.0
3546
*/
36-
@ThreadSafeSemaphore internal var stacks = [EventPriority:[any Eventable]]()
47+
@ThreadSafeSemaphore internal var stacks = [EventPriority:[EventDispatchContainer]]()
48+
49+
/**
50+
Keeps track of the latest Dispatch Times for every `Eventable` type
51+
- Author: Simon J. Stuart
52+
- Version: 4.3.0
53+
*/
54+
@ThreadSafeSemaphore internal var latestEventDispatchTime = [String:DispatchTime]()
3755

3856
/**
3957
The number of Events currently pending in the Queue and Stack combined
@@ -47,8 +65,8 @@ open class EventHandler: ObservableThread, EventHandling {
4765
var stackCount: Int = 0
4866
var queueCount: Int = 0
4967

50-
var snapStacks = [EventPriority:[any Eventable]]() // Snapshot of the current Stacks
51-
var snapQueues = [EventPriority:[any Eventable]]() // Snapshot of hte current Queues
68+
var snapStacks = [EventPriority:[EventDispatchContainer]]() // Snapshot of the current Stacks
69+
var snapQueues = [EventPriority:[EventDispatchContainer]]() // Snapshot of hte current Queues
5270

5371
_stacks.withLock { stacks in // With the Lock
5472
snapStacks = stacks // Grab a Snapshot
@@ -77,22 +95,42 @@ open class EventHandler: ObservableThread, EventHandling {
7795
*/
7896
internal var eventsPending = DispatchSemaphore(value: 0)
7997

80-
public func queueEvent(_ event: any Eventable, priority: EventPriority = .normal) {
98+
public func queueEvent(_ event: EventDispatchContainer, priority: EventPriority) {
8199
_queues.withLock { queues in
82-
if queues[priority] == nil { queues[priority] = [any Eventable]() } // Create the empty Array if it doesn't exist
100+
if queues[priority] == nil { queues[priority] = [EventDispatchContainer]() } // Create the empty Array if it doesn't exist
83101
queues[priority]!.append(event)
84102
}
85103
eventsPending.signal()
86104
}
105+
106+
public func queueEvent(_ event: any Eventable, priority: EventPriority = .normal) {
107+
let eventDispatchContainer = EventDispatchContainer(event: event)
108+
109+
_latestEventDispatchTime.withLock { eventDispatches in
110+
eventDispatches[event.getEventTypeName()] = eventDispatchContainer.dispatchTime
111+
}
112+
113+
queueEvent(eventDispatchContainer, priority: priority)
114+
}
87115

88-
public func stackEvent(_ event: any Eventable, priority: EventPriority = .normal) {
116+
public func stackEvent(_ event: EventDispatchContainer, priority: EventPriority) {
89117
_stacks.withLock { stacks in
90-
if stacks[priority] == nil { stacks[priority] = [any Eventable]() } // Create the empty Array if it doesn't exist
118+
if stacks[priority] == nil { stacks[priority] = [EventDispatchContainer]() } // Create the empty Array if it doesn't exist
91119
stacks[priority]!.append(event)
92120
}
93121
eventsPending.signal()
94122
}
95123

124+
public func stackEvent(_ event: any Eventable, priority: EventPriority = .normal) {
125+
let eventDispatchContainer = EventDispatchContainer(event: event)
126+
127+
_latestEventDispatchTime.withLock { eventDispatches in
128+
eventDispatches[event.getEventTypeName()] = eventDispatchContainer.dispatchTime
129+
}
130+
131+
stackEvent(eventDispatchContainer, priority: priority)
132+
}
133+
96134
public func scheduleQueue(_ event: any Eventable, at: DispatchTime, priority: EventPriority) {
97135
Task {
98136
DispatchQueue.main.asyncAfter(deadline: at) {
@@ -109,15 +147,14 @@ open class EventHandler: ObservableThread, EventHandling {
109147
}
110148
}
111149

112-
113150
/**
114151
Processes an Event
115152
- Author: Simon J. Stuart
116153
- Version: 1.0.0
117154
- Parameters:
118155
- event: The Event to Process.
119156
*/
120-
open func processEvent(_ event: any Eventable, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
157+
open func processEvent(_ event: EventDispatchContainer, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
121158
preconditionFailure("processEvent must be overriden!")
122159
}
123160

@@ -128,7 +165,7 @@ open class EventHandler: ObservableThread, EventHandling {
128165
- Parameters:
129166
- events: The `Array` of `Eventable` objects
130167
*/
131-
@inline(__always) private func processEvents(_ events: [any Eventable], dispatchMethod: EventDispatchMethod, priority: EventPriority) {
168+
@inline(__always) private func processEvents(_ events: [EventDispatchContainer], dispatchMethod: EventDispatchMethod, priority: EventPriority) {
132169
for event in events {
133170
processEvent(event, dispatchMethod: dispatchMethod, priority: priority)
134171
}
@@ -141,7 +178,7 @@ open class EventHandler: ObservableThread, EventHandling {
141178
*/
142179
private func processEventStacks() {
143180
// We need to take a snapshot of the Stacks and empty them
144-
var eventStacks = [EventPriority:[any Eventable]]()
181+
var eventStacks = [EventPriority:[EventDispatchContainer]]()
145182
_stacks.withLock { stacks in
146183
eventStacks = stacks
147184
stacks.removeAll()
@@ -161,7 +198,7 @@ open class EventHandler: ObservableThread, EventHandling {
161198
*/
162199
private func processEventQueues() {
163200
// We need to take a snapshot of the Queues and empty them
164-
var eventQueues = [EventPriority:[any Eventable]]()
201+
var eventQueues = [EventPriority:[EventDispatchContainer]]()
165202
_queues.withLock { queues in
166203
eventQueues = queues
167204
queues.removeAll()

Sources/EventDrivenSwift/EventHandler/EventHandling.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,26 @@ public protocol EventHandling {
3434
*/
3535
func stackEvent(_ event: any Eventable, priority: EventPriority)
3636

37+
/**
38+
Adds the given `event` to the Event Queue with the given `priority` retaining the original Dispatch Information
39+
- Author: Simon J. Stuart
40+
- Version: 4.3.0
41+
- Parameters:
42+
- event: The Event
43+
- priority: The Priority of the Event
44+
*/
45+
func queueEvent(_ event: EventHandler.EventDispatchContainer, priority: EventPriority)
46+
47+
/**
48+
Adds the given `event` to the Event Stack with the given `priority` retaining the original Dispatch Information
49+
- Author: Simon J. Stuart
50+
- Version: 4.3.0
51+
- Parameters:
52+
- event: The Event
53+
- priority: The Priority of the Event
54+
*/
55+
func stackEvent(_ event: EventHandler.EventDispatchContainer, priority: EventPriority)
56+
3757
/**
3858
Schedule the Event to be dispatched with the given `priority`
3959
- Author: Simon J. Stuart

Sources/EventDrivenSwift/EventListener/EventListenable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public protocol EventListenable: AnyObject, EventReceiving {
6161
- executeOn: Tells the `EventListenable` whether to execute the Callback on the `requester`'s Thread, or the Listener's.
6262
- Returns: A `UUID` value representing the `token` associated with this Event Callback
6363
*/
64-
@discardableResult func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn) -> EventListenerHandling
64+
@discardableResult func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest) -> EventListenerHandling
6565

6666
/**
6767
Locates and removes the given Listener `token` (if it exists)

0 commit comments

Comments
 (0)