Skip to content

Commit e30522c

Browse files
committed
Improved Listeners by making them return a Handler
UUID tokens for `EventListener.addListener` have been replaced by a new `EventListenerHandling` protocol describing the `EventListenerHandler` class. This can be used to call `.remove()` and unregister the `Listener`. References are `weak`, and perform `nil`-checks as appropriate, so calling `.remove()` against an `EventListenerHandler` for an `EventListenable` that has been garbage collected won't raise any errors.
1 parent 0073f8b commit e30522c

File tree

9 files changed

+76
-12
lines changed

9 files changed

+76
-12
lines changed

Sources/EventDrivenSwift/Central/EventCentral.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ final public class EventCentral: EventDispatcher, EventCentralable {
7474
}
7575
}
7676

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

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) -> UUID
71+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn) -> 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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public protocol Eventable {
4343
- callback: The code to invoke for the given `Eventable` Type
4444
- Returns: A `UUID` value representing the `token` associated with this Event Callback
4545
*/
46-
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn) -> UUID
46+
@discardableResult static func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn) -> EventListenerHandling
4747

4848
// @discardableResult static func addListener(_ requester: AnyObject?, _ eventType: any Eventable.Type, _ callback: @escaping TypedEventCallback<any Eventable.Type>, executeOn: ExecuteEventOn) -> UUID
4949

@@ -82,7 +82,7 @@ extension Eventable {
8282
EventCentral.stackEvent(self, priority: priority)
8383
}
8484

85-
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread) -> UUID {
85+
@discardableResult static public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, executeOn: ExecuteEventOn = .requesterThread) -> EventListenerHandling {
8686
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn)
8787
}
8888

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) -> UUID
64+
@discardableResult func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn) -> EventListenerHandling
6565

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

Sources/EventDrivenSwift/EventListener/EventListener.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ open class EventListener: EventHandler, EventListenable {
8080
}
8181
}
8282

83-
@discardableResult public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread) -> UUID {
83+
@discardableResult public func addListener<TEvent: Eventable>(_ requester: AnyObject?, _ callback: @escaping TypedEventCallback<TEvent>, forEventType: Eventable.Type, executeOn: ExecuteEventOn = .requesterThread) -> EventListenerHandling {
8484
let eventTypeName = String(reflecting: forEventType)
8585
let method: EventCallback = { event, priority in
8686
self.callTypedEventCallback(callback, forEvent: event, priority: priority)
@@ -95,7 +95,7 @@ open class EventListener: EventHandler, EventListenable {
9595

9696
/// We automatically register the Listener with the Central Event Dispatcher
9797
EventCentral.shared.addReceiver(self, forEventType: forEventType)
98-
return eventListenerContainer.token
98+
return EventListenerHandler(eventListenable: self, token: eventListenerContainer.token)
9999
}
100100

101101
public func removeListener(_ token: UUID) {

Sources/EventDrivenSwift/EventListener/EventListening.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88

99
import Foundation
1010

11+
/**
12+
Apply `EventListening` to any `Class` intent on listening for `Eventable`s to register `@EventMethod`-decorated (immutable) Listeners via Reflection.
13+
- Author: Simon J. Stuart
14+
- Version: 4.1.0
15+
*/
1116
public protocol EventListening: AnyObject {
1217
/**
1318
Invoke this method to automatically register any Event Listener callback bearing the `@EventMethod` wrapper.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// EventListenerHandler.swift
3+
// Copyright (c) 2022, Flowduino
4+
// Authored by Simon J. Stuart on 28th August 2022
5+
//
6+
// Subject to terms, restrictions, and liability waiver of the MIT License
7+
//
8+
9+
import Foundation
10+
11+
/**
12+
A Handler for an `EventListener` your code has registered. You can use this to revoke your Event Listeners at any time.
13+
- Author: Simon J. Stuart
14+
- Version: 4.1.0
15+
*/
16+
public class EventListenerHandler: EventListenerHandling {
17+
/**
18+
`weak` reference to the `EventListenable` against which this Listener is registered.
19+
- Author: Simon J. Stuart
20+
- Version: 4.1.0
21+
*/
22+
private weak var eventListenable: EventListenable?
23+
24+
/**
25+
This is the Token Key assoicated with your Listener
26+
- Author: Simon J. Stuart
27+
- Version: 4.1.0
28+
*/
29+
private var token: UUID
30+
31+
public func remove() {
32+
eventListenable?.removeListener(token)
33+
}
34+
35+
public init(eventListenable: EventListenable, token: UUID) {
36+
self.token = token
37+
}
38+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// EventListenerHandling.swift
3+
// Copyright (c) 2022, Flowduino
4+
// Authored by Simon J. Stuart on 28th August 2022
5+
//
6+
// Subject to terms, restrictions, and liability waiver of the MIT License
7+
//
8+
9+
import Foundation
10+
11+
/**
12+
A Handler for an `EventListener` your code has registered. You can use this to revoke your Event Listeners at any time.
13+
- Author: Simon J. Stuart
14+
- Version: 4.1.0
15+
*/
16+
public protocol EventListenerHandling: AnyObject {
17+
/**
18+
Removes your Event Listener
19+
*/
20+
func remove()
21+
}

Tests/EventDrivenSwiftTests/BasicEventListenerTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ final class BasicEventListenerTests: XCTestCase, EventListening {
3535
}
3636

3737
var myFoo = 0
38-
var listenerToken: UUID? = nil
38+
var listenerHandler: EventListenerHandling? = nil
3939
let testOne = TestEventTypeOne(foo: 1000) // Create the Event
4040
var awaiter = DispatchSemaphore(value: 0)
4141

4242
func testEventListenerOnListenerThread() throws {
4343
registerListeners()
4444
XCTAssertEqual(myFoo, 0, "Expect initial value of eventThread.foo to be 0, but it's \(myFoo)")
4545

46-
listenerToken = TestEventTypeOne.addListener(self, { (event: TestEventTypeOne, priority) in
46+
listenerHandler = TestEventTypeOne.addListener(self, { (event: TestEventTypeOne, priority) in
4747
self.myFoo = event.foo
4848
self.awaiter.signal()
4949
}, executeOn: .listenerThread)
@@ -56,14 +56,14 @@ final class BasicEventListenerTests: XCTestCase, EventListening {
5656
XCTAssertEqual(result, .success, "The Event Handler was not invoked in time!")
5757
XCTAssertEqual(self.myFoo, testOne.foo, "Expect new value of eventThread.foo to be \(testOne.foo), but it's \(self.myFoo)")
5858

59-
TestEventTypeOne.removeListener(listenerToken!)
59+
listenerHandler?.remove()
6060
}
6161

6262
func testEventListenerOnTaskThread() throws {
6363
// registerListeners()
6464
XCTAssertEqual(myFoo, 0, "Expect initial value of eventThread.foo to be 0, but it's \(myFoo)")
6565

66-
listenerToken = TestEventTypeOne.addListener(self, { (event: TestEventTypeOne, priority) in
66+
listenerHandler = TestEventTypeOne.addListener(self, { (event: TestEventTypeOne, priority) in
6767
self.myFoo = event.foo
6868
self.awaiter.signal()
6969
}, executeOn: .taskThread)
@@ -74,6 +74,6 @@ final class BasicEventListenerTests: XCTestCase, EventListening {
7474
XCTAssertEqual(result, .success, "The Event Handler was not invoked in time!")
7575
XCTAssertEqual(self.myFoo, testOne.foo, "Expect new value of eventThread.foo to be \(testOne.foo), but it's \(self.myFoo)")
7676

77-
TestEventTypeOne.removeListener(listenerToken!)
77+
listenerHandler?.remove()
7878
}
7979
}

0 commit comments

Comments
 (0)