Skip to content

Commit b21ff99

Browse files
committed
Considerable Refactoring and EventListener implementations
- Unifying the Naming Conventions whereby `EventReceiver` will always be referred to as an Event Receiver. - Introduced a new `EventListener` concept, which has been implemented and is pending suitable Unit Testing - Reconsidering the `UIEventReceiver` entirely in favour of registering simple `EventListeners` instead.
1 parent eeb980f commit b21ff99

File tree

9 files changed

+342
-69
lines changed

9 files changed

+342
-69
lines changed

Sources/EventDrivenSwift/Central/EventCentral.swift

+24-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,21 @@ final public class EventCentral: EventDispatcher, EventCentralable {
6865
return _shared.eventCount
6966
}
7067
}
68+
69+
internal var eventListener = EventListener()
70+
71+
@inline(__always) public static func addListener<TEvent>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID where TEvent : Eventable {
72+
return _shared.eventListener.addListener(requester, callback, forEventType: forEventType)
73+
}
74+
75+
@inline(__always) public static func removeListener(_ token: UUID) {
76+
_shared.eventListener.removeListener(token)
77+
}
78+
79+
@inline(__always) public static func removeListener(_ token: UUID, typeOf: Eventable.Type) {
80+
_shared.eventListener.removeListener(token, typeOf: typeOf)
81+
}
82+
83+
/// This just makes it so that your code cannot initialise instances of `EventCentral`. It's a Singleton!
84+
override private init() {}
7185
}

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+
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/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
}

Sources/EventDrivenSwift/EventDispatcher/EventDispatcher.swift

+42-42
Original file line numberDiff line numberDiff line change
@@ -11,102 +11,102 @@ import ThreadSafeSwift
1111
import Observable
1212

1313
/**
14-
An implementation of `EventHandler` designed to Queue/Stack outbound events, and Dispatch them to all registered `listeners`
14+
An implementation of `EventHandler` designed to Queue/Stack outbound events, and Dispatch them to all registered `receivers`
1515
- Author: Simon J. Stuart
1616
- Version: 1.0.0
1717
- Note: While you can inherit from and even create instances of `EventDispatcher`, best practice would be to use `EventCentral.shared` as the central Event Dispatcher.
1818
*/
1919
open class EventDispatcher: EventHandler, EventDispatchable {
20-
struct ListenerContainer {
21-
weak var listener: (any EventReceivable)?
20+
struct ReceiverContainer {
21+
weak var receiver: (any EventReceivable)?
2222
}
2323

2424
/**
25-
Stores all of the Listeners against the fully-qualified name of the corresponding `Eventable` Type
25+
Stores all of the Receivers against the fully-qualified name of the corresponding `Eventable` Type
2626
- Author: Simon J. Stuart
2727
- Version: 1.0.0
2828
*/
29-
@ThreadSafeSemaphore private var listeners = [String:[ListenerContainer]]()
29+
@ThreadSafeSemaphore private var receivers = [String:[ReceiverContainer]]()
3030

31-
public func addListener(_ listener: any EventReceivable, forEventType: Eventable.Type) {
31+
public func addReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type) {
3232
let eventTypeName = String(reflecting: forEventType)
3333

34-
_listeners.withLock { listeners in
35-
var bucket = listeners[eventTypeName]
34+
_receivers.withLock { receivers in
35+
var bucket = receivers[eventTypeName]
3636
let newBucket = bucket == nil
37-
if newBucket { bucket = [ListenerContainer]() } /// If there's no Bucket for this Event Type, create one
37+
if newBucket { bucket = [ReceiverContainer]() } /// If there's no Bucket for this Event Type, create one
3838

39-
/// If it's NOT a New Bucket, and the Bucket already contains this Listener...
40-
if !newBucket && bucket!.contains(where: { listenerContainer in
41-
listenerContainer.listener != nil && ObjectIdentifier(listenerContainer.listener!) == ObjectIdentifier(listener)
39+
/// If it's NOT a New Bucket, and the Bucket already contains this Receiver...
40+
if !newBucket && bucket!.contains(where: { receiverContainer in
41+
receiverContainer.receiver != nil && ObjectIdentifier(receiverContainer.receiver!) == ObjectIdentifier(receiver)
4242
}) {
4343
return // ... just Return!
4444
}
4545

46-
/// If we reach here, the Listener is not already in the Bucket, so let's add it!
47-
bucket!.append(ListenerContainer(listener: listener))
48-
listeners[eventTypeName] = bucket!
46+
/// If we reach here, the Receiver is not already in the Bucket, so let's add it!
47+
bucket!.append(ReceiverContainer(receiver: receiver))
48+
receivers[eventTypeName] = bucket!
4949
}
5050
}
5151

52-
public func removeListener(_ listener: any EventReceivable, forEventType: Eventable.Type) {
52+
public func removeReceiver(_ receiver: any EventReceivable, forEventType: Eventable.Type) {
5353
let eventTypeName = String(reflecting: forEventType)
5454

55-
_listeners.withLock { listeners in
56-
var bucket = listeners[eventTypeName]
57-
if bucket == nil { return } /// Can't remove a Listener if there isn't even a Bucket for hte Event Type
55+
_receivers.withLock { receivers in
56+
var bucket = receivers[eventTypeName]
57+
if bucket == nil { return } /// Can't remove a Receiver if there isn't even a Bucket for hte Event Type
5858

59-
/// Remove any Listeners from this Event-Type Bucket for the given `listener` instance.
60-
bucket!.removeAll { listenerContainer in
61-
listenerContainer.listener != nil && ObjectIdentifier(listenerContainer.listener!) == ObjectIdentifier(listener)
59+
/// Remove any Receivers from this Event-Type Bucket for the given `receiver` instance.
60+
bucket!.removeAll { receiverContainer in
61+
receiverContainer.receiver != nil && ObjectIdentifier(receiverContainer.receiver!) == ObjectIdentifier(receiver)
6262
}
6363
}
6464
}
6565

66-
public func removeListener(_ listener: any EventReceivable) {
67-
_listeners.withLock { listeners in
68-
for (eventTypeName, bucket) in listeners { /// Iterate every Event Type
66+
public func removeReceiver(_ receiver: any EventReceivable) {
67+
_receivers.withLock { receivers in
68+
for (eventTypeName, bucket) in receivers { /// Iterate every Event Type
6969
var newBucket = bucket // Copy the Bucket
70-
newBucket.removeAll { listenerContainer in /// Remove any occurences of the given Listener from the Bucket
71-
listenerContainer.listener != nil && ObjectIdentifier(listenerContainer.listener!) == ObjectIdentifier(listener)
70+
newBucket.removeAll { receiverContainer in /// Remove any occurences of the given Receiver from the Bucket
71+
receiverContainer.receiver != nil && ObjectIdentifier(receiverContainer.receiver!) == ObjectIdentifier(receiver)
7272
}
73-
listeners[eventTypeName] = newBucket /// Update the Bucket for this Event Type
73+
receivers[eventTypeName] = newBucket /// Update the Bucket for this Event Type
7474
}
7575
}
7676
}
7777

7878
/**
79-
Dispatch the Event to all Subscribed Listeners
79+
Dispatch the Event to all Subscribed Receivers
8080
- Author: Simon J. Stuart
8181
- Version: 1.0.0
8282
*/
8383
override internal func processEvent(_ event: any Eventable, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
8484
let eventTypeName = String(reflecting: type(of: event))
8585

86-
var snapListeners = [String:[ListenerContainer]]()
86+
var snapReceivers = [String:[ReceiverContainer]]()
8787

88-
_listeners.withLock { listeners in
89-
// We should take this opportunity to remove any nil listeners
90-
listeners[eventTypeName]?.removeAll(where: { listenerContainer in
91-
listenerContainer.listener == nil
88+
_receivers.withLock { receivers in
89+
// We should take this opportunity to remove any nil receivers
90+
receivers[eventTypeName]?.removeAll(where: { receiverContainer in
91+
receiverContainer.receiver == nil
9292
})
93-
snapListeners = listeners
93+
snapReceivers = receivers
9494
}
9595

96-
let bucket = snapListeners[eventTypeName]
97-
if bucket == nil { return } /// No Listeners, so nothing more to do!
96+
let bucket = snapReceivers[eventTypeName]
97+
if bucket == nil { return } /// No Receivers, so nothing more to do!
9898

99-
for listener in bucket! {
100-
if listener.listener == nil { /// If the Listener is `nil`...
99+
for receiver in bucket! {
100+
if receiver.receiver == nil { /// If the Recevier is `nil`...
101101
continue
102102
}
103103

104-
// so, we have a listener... let's deal with it!
104+
// so, we have a receiver... let's deal with it!
105105
switch dispatchMethod {
106106
case .stack:
107-
listener.listener!.stackEvent(event, priority: priority)
107+
receiver.receiver!.stackEvent(event, priority: priority)
108108
case .queue:
109-
listener.listener!.queueEvent(event, priority: priority)
109+
receiver.receiver!.queueEvent(event, priority: priority)
110110
}
111111
}
112112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// EventListenable.swift
3+
// Copyright (c) 2022, Flowduino
4+
// Authored by Simon J. Stuart on 11th August 2022
5+
//
6+
// Subject to terms, restrictions, and liability waiver of the MIT License
7+
//
8+
9+
import Foundation
10+
11+
/**
12+
Convienience `typealias` used for Event Callbacks
13+
- Author: Simon J. Stuart
14+
- Version: 3.0.0
15+
*/
16+
typealias EventCallback = (_ event: any Eventable, _ priority: EventPriority) -> ()
17+
18+
/**
19+
Convienience `typealias` used for Typed Event Callbacks
20+
- Author: Simon J. Stuart
21+
- Version: 3.0.0
22+
*/
23+
public typealias TypedEventCallback<TEvent: Any> = (_ event: TEvent, _ priority: EventPriority) -> ()
24+
25+
/**
26+
Provides a simple means of Receiving Events and invoking appropriate Callbacks
27+
- Author: Simon J. Stuart
28+
- Version: 3.0.0
29+
*/
30+
public protocol EventListenable: AnyObject, EventReceivable {
31+
/**
32+
Registers an Event Callback for the given `Eventable` Type
33+
- Author: Simon J. Stuart
34+
- Version: 3.0.0
35+
- Parameters:
36+
- requester: The Object owning the Callback Method
37+
- callback: The code to invoke for the given `Eventable` Type
38+
- forEventType: The `Eventable` Type for which to Register the Callback
39+
- Returns: A `UUID` value representing the `token` associated with this Event Callback
40+
*/
41+
func addListener<TEvent: Eventable>(_ requester: AnyObject, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type) -> UUID
42+
43+
/**
44+
Locates and removes the given Listener `token` (if it exists)
45+
- Author: Simon J. Stuart
46+
- Version: 3.0.0
47+
- Parameters:
48+
- token: The Token of the Listener you wish to remove
49+
- Note: Using this method is far slower than if you provide the `typeOf` Parameter to satisfy the more-specific overloaded version of this method
50+
*/
51+
func removeListener(_ token: UUID)
52+
53+
/**
54+
Locates and removes the given Listener `token` (if it exists)
55+
- Author: Simon J. Stuart
56+
- Version: 3.0.0
57+
- Parameters:
58+
- token: The Token of the Listener you wish to remove
59+
- typeOf: The Event Type for which the Listener identified by the given `token` is interested
60+
*/
61+
func removeListener(_ token: UUID, typeOf: Eventable.Type)
62+
}

0 commit comments

Comments
 (0)