Skip to content

Commit e95a164

Browse files
committed
5.2.0 - EventListener Custom Filter
- Added support to `EventListener` to take a custom Filter Callback (returning a `Bool` to determine whether or not the `Listener` is interested in the `Eventable`
1 parent 435a5bf commit e95a164

File tree

6 files changed

+46
-9
lines changed

6 files changed

+46
-9
lines changed

Sources/EventDrivenSwift/Central/EventCentral.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0) -> EventListenerHandling where TEvent : Eventable {
79-
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType, executeOn: executeOn, interestedIn: interestedIn, maximumAge: maximumAge)
78+
@discardableResult @inline(__always) public static func addListener<TEvent>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0, customFilter: TypedEventFilterCallback<TEvent>? = nil) -> EventListenerHandling where TEvent : Eventable {
79+
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType, executeOn: executeOn, interestedIn: interestedIn, maximumAge: maximumAge, customFilter: customFilter)
8080
}
8181

8282
@inline(__always) public static func removeListener(_ token: UUID) {

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, interestedIn: EventListenerInterest, maximumAge: UInt64) -> EventListenerHandling
71+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest, maximumAge: UInt64, customFilter: TypedEventFilterCallback<TEvent>?) -> 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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +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, interestedIn: EventListenerInterest, maximumAge: UInt64) -> EventListenerHandling
66+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest, maximumAge: UInt64, customFilter: TypedEventFilterCallback<TEvent>?) -> EventListenerHandling
6767

6868
/**
6969
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
@@ -122,8 +122,8 @@ extension Eventable {
122122
EventCentral.scheduleStack(self, at: at, priority: priority)
123123
}
124124

125-
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0) -> EventListenerHandling {
126-
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn, interestedIn: interestedIn, maximumAge: maximumAge)
125+
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0, customFilter: TypedEventFilterCallback<TEvent>? = nil) -> EventListenerHandling {
126+
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn, interestedIn: interestedIn, maximumAge: maximumAge, customFilter: customFilter)
127127
}
128128

129129
public static func removeListener(_ token: UUID) {

Sources/EventDrivenSwift/EventListener/EventListenable.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Foundation
1414
- Version: 3.0.0
1515
*/
1616
public typealias EventCallback = (_ event: any Eventable, _ priority: EventPriority, _ dispatchTime: DispatchTime) -> ()
17+
public typealias EventFilterCallback = (_ event: any Eventable, _ priority: EventPriority, _ dispatchTime: DispatchTime) -> Bool
1718

1819
/**
1920
Convienience `typealias` used for Typed Event Callbacks
@@ -63,7 +64,7 @@ public protocol EventListenable: AnyObject, EventReceiving {
6364
- maximumAge: If `interestedIn` == `.youngerThan`, this is the number of nanoseconds between the time of dispatch and the moment of processing where the Listener will be interested in the Event. Any Event older will be ignored
6465
- Returns: A `UUID` value representing the `token` associated with this Event Callback
6566
*/
66-
@discardableResult func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest, maximumAge: UInt64) -> EventListenerHandling
67+
@discardableResult func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn, interestedIn: EventListenerInterest, maximumAge: UInt64, customFilter: TypedEventFilterCallback<TEvent>?) -> EventListenerHandling
6768

6869
/**
6970
Locates and removes the given Listener `token` (if it exists)

Sources/EventDrivenSwift/EventListener/EventListener.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ open class EventListener: EventHandler, EventListenable {
3434
var executeOn: ExecuteEventOn = .requesterThread
3535
var interestedIn: EventListenerInterest = .all
3636
var maximumEventAge: UInt64 = 0
37+
var customFilter: EventFilterCallback?
3738
}
3839

3940
/**
@@ -73,6 +74,8 @@ open class EventListener: EventHandler, EventListenable {
7374

7475
if listener.interestedIn == .youngerThan && listener.maximumEventAge != 0 && (DispatchTime.now().uptimeNanoseconds - event.dispatchTime.uptimeNanoseconds) > listener.maximumEventAge { continue } // If this Receiver has a maximum age of interest, and this Event is older than that... skip it!
7576

77+
if listener.interestedIn == .custom && (listener.customFilter == nil || !listener.customFilter!(event.event, priority, event.dispatchTime)) { continue }
78+
7679
switch listener.executeOn {
7780
case .requesterThread:
7881
Task { // We raise a Task because we don't want the entire Listener blocked in the event the dispatchQueue is busy or blocked!
@@ -91,12 +94,18 @@ open class EventListener: EventHandler, EventListenable {
9194
}
9295
}
9396

94-
@discardableResult public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0) -> EventListenerHandling {
97+
@discardableResult public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread, interestedIn: EventListenerInterest = .all, maximumAge: UInt64 = 0, customFilter: TypedEventFilterCallback<TEvent>? = nil) -> EventListenerHandling {
9598
let eventTypeName = forEventType.getEventTypeName()
9699
let method: EventCallback = { event, priority, dispatchTime in
97100
self.callTypedEventCallback(callback, forEvent: event, priority: priority, dispatchTime: dispatchTime)
98101
}
99-
let eventListenerContainer = EventListenerContainer(requester: requester, callback: method, dispatchQueue: OperationQueue.current?.underlyingQueue, executeOn: executeOn, interestedIn: interestedIn, maximumEventAge: maximumAge)
102+
var filterMethod: EventFilterCallback? = nil
103+
if customFilter != nil {
104+
filterMethod = { event, priority, dispatchTime in
105+
self.callTypedEventFilterCallback(customFilter!, forEvent: event, priority: priority, dispatchTime: dispatchTime)
106+
}
107+
}
108+
let eventListenerContainer = EventListenerContainer(requester: requester, callback: method, dispatchQueue: OperationQueue.current?.underlyingQueue, executeOn: executeOn, interestedIn: interestedIn, maximumEventAge: maximumAge, customFilter: filterMethod)
100109
_eventListeners.withLock { eventCallbacks in
101110
var bucket = eventCallbacks[eventTypeName]
102111
if bucket == nil { bucket = [EventListenerContainer]() } // Create a new bucket if there isn't already one!
@@ -161,10 +170,28 @@ open class EventListener: EventHandler, EventListenable {
161170
- callback: The code (Closure or Callback Method) to execute for the given `forEvent`, typed generically using `TEvent`
162171
- forEvent: The instance of the `Eventable` type to be processed
163172
- priority: The `EventPriority` with which the `forEvent` was dispatched
173+
- dispatchTime: The `DispatchTime` at which `forEvent` was Dispatched
164174
*/
165175
internal func callTypedEventCallback<TEvent: Eventable>(_ callback: @escaping TypedEventCallback<TEvent>, forEvent: Eventable, priority: EventPriority, dispatchTime: DispatchTime) {
166176
if let typedEvent = forEvent as? TEvent {
167177
callback(typedEvent, priority, dispatchTime)
168178
}
169179
}
180+
181+
/**
182+
Performs a Transparent Type Test, Type Cast, and Method Call to the Custom Filter via a `callback` Closure.
183+
- Author: Simon J. Stuart
184+
- Version: 5.2.0
185+
- Parameters:
186+
- callback: The code (Closure or Callback Method) to execute for the given `forEvent`, typed generically using `TEvent`... returns `true` if the Listener is interested in `forEvent`, `false` if the Listener wants to ignore it
187+
- forEvent: The instance of the `Eventable` type to be processed
188+
- priority: The `EventPriority` with which the `forEvent` was dispatched
189+
- dispatchTime: The `DispatchTime` at which `forEvent` was Dispatched
190+
*/
191+
internal func callTypedEventFilterCallback<TEvent: Eventable>(_ callback: @escaping TypedEventFilterCallback<TEvent>, forEvent: Eventable, priority: EventPriority, dispatchTime: DispatchTime) -> Bool {
192+
if let typedEvent = forEvent as? TEvent {
193+
return callback(typedEvent, priority, dispatchTime)
194+
}
195+
return false /// We will simply return `false` if the Event is of the wrong Type
196+
}
170197
}

Sources/EventDrivenSwift/EventListener/EventListenerInterest.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,13 @@ public enum EventListenerInterest: CaseIterable {
2525
- Version: 5.0.0
2626
*/
2727
case youngerThan
28+
29+
/**
30+
Receivers will ignore any Event where the Filter Callback returns `false`, and accept any Event where the Filter Callback returns `true`
31+
- Author: Simon J. Stuart
32+
- Version: 5.2.0
33+
*/
34+
case custom
2835
}
36+
37+
public typealias TypedEventFilterCallback<EventType: Eventable> = (_ event: EventType, _ priority: EventPriority, _ dispatchTime: DispatchTime) -> Bool

0 commit comments

Comments
 (0)