Skip to content

Commit 8f41434

Browse files
committed
V 4.1.0 - New Listener and Thread Callback System
- Added a new `@EventMethod` Property Wrapper to enable in-line, automatically-registering Event-invoked Callback Methods on any class implementing `EventListening` (for automatic registration) - Any Class implementing `EventListening` can now invoke `registerListeners()` at the end of its `init` method (or anywhere post-init you wish to do so, anyway) to automatically register all `@EventMethod`-decorated Event Callbacks. - Added a version of `@EventMethod` specifically for use in `EventThread` types. You don't need to invoke `registerListeners()` for your `EventThread` types, because the base `init` will already do this for you. - `EventThread` now supports multiple Callbacks for the same `Eventable` type. Invoking `addEventCallback` will now return a `UUID` containing the unique `token` of each Callback. This can be used with `removeEventCallback` to unregister the Callback at any time.
1 parent 05531f7 commit 8f41434

File tree

11 files changed

+373
-33
lines changed

11 files changed

+373
-33
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) -> UUID 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) -> UUID
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: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ 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) -> UUID
47+
48+
// @discardableResult static func addListener(_ requester: AnyObject?, _ eventType: any Eventable.Type, _ callback: @escaping TypedEventCallback<any Eventable.Type>, executeOn: ExecuteEventOn) -> UUID
4749

4850
/**
4951
Locates and removes the given Listener `token` (if it exists) from the Central Event Listener
@@ -80,11 +82,12 @@ extension Eventable {
8082
EventCentral.stackEvent(self, priority: priority)
8183
}
8284

83-
@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) -> UUID {
8486
return EventCentral.addListener(requester, callback, forEventType: Self.self, executeOn: executeOn)
8587
}
86-
88+
8789
public static func removeListener(_ token: UUID) {
8890
EventCentral.removeListener(token, typeOf: Self.self)
8991
}
9092
}
93+
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// EventMethod.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 typealias EventMethodTypedEventCallback<TOwner: AnyObject, TEvent: Any> = (_ sender: TOwner, _ event: TEvent, _ priority: EventPriority) -> ()
12+
13+
/**
14+
Any Property wrapped with `EventMethod` will automatically conform to `EventMethodContainer`
15+
- Author: Simon J. Stuart
16+
- Version: 4.1.0
17+
- Note: This is used to conformity-test decorated `var`s to automatically register Event Listeners
18+
*/
19+
public protocol EventMethodContainer {
20+
associatedtype TEventType: Eventable
21+
associatedtype TOwner: AnyObject
22+
23+
var wrappedValue: EventMethodTypedEventCallback<TOwner, TEventType> { get set }
24+
25+
var owner: AnyObject? { get set }
26+
27+
mutating func unregister()
28+
}
29+
30+
/**
31+
Decorate Typed Event Callback Closures as `var` with `@EventMethod<TEventType>` to automatically register them.
32+
- Author: Simon J. Stuart
33+
- Version: 4.1.0
34+
*/
35+
@propertyWrapper
36+
public struct EventMethod<TOwner: AnyObject, TEventType: Eventable>: EventMethodContainer {
37+
mutating public func unregister() {
38+
lock.wait()
39+
if token != nil {
40+
TEventType.removeListener(token!)
41+
token = nil
42+
}
43+
lock.signal()
44+
}
45+
46+
private var token: UUID? = nil
47+
private var lock = DispatchSemaphore(value: 1)
48+
49+
public var wrappedValue: EventMethodTypedEventCallback<TOwner, TEventType> {
50+
didSet {
51+
reRegsiterListener()
52+
}
53+
}
54+
public var executeOn: ExecuteEventOn {
55+
didSet {
56+
reRegsiterListener()
57+
}
58+
}
59+
60+
public var owner: AnyObject? {
61+
didSet {
62+
reRegsiterListener()
63+
}
64+
}
65+
66+
private func callback(event: TEventType, priority: EventPriority) {
67+
if let typedOwner = owner as? TOwner {
68+
wrappedValue(typedOwner, event, priority)
69+
}
70+
}
71+
72+
mutating private func reRegsiterListener() {
73+
lock.wait()
74+
if token != nil {
75+
TEventType.removeListener(token!)
76+
}
77+
if owner != nil {
78+
token = TEventType.addListener(
79+
owner,
80+
callback,
81+
executeOn: executeOn)
82+
}
83+
lock.signal()
84+
}
85+
86+
public init(wrappedValue: @escaping EventMethodTypedEventCallback<TOwner, TEventType>, executeOn: ExecuteEventOn = .requesterThread) {
87+
self.wrappedValue = wrappedValue
88+
self.executeOn = executeOn
89+
}
90+
}

Sources/EventDrivenSwift/EventListener/EventListenable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Foundation
1313
- Author: Simon J. Stuart
1414
- Version: 3.0.0
1515
*/
16-
typealias EventCallback = (_ event: any Eventable, _ priority: EventPriority) -> ()
16+
public typealias EventCallback = (_ event: any Eventable, _ priority: EventPriority) -> ()
1717

1818
/**
1919
Convienience `typealias` used for Typed Event Callbacks
@@ -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) -> UUID
6565

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

Sources/EventDrivenSwift/EventListener/EventListener.swift

Lines changed: 1 addition & 1 deletion
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) -> UUID {
8484
let eventTypeName = String(reflecting: forEventType)
8585
let method: EventCallback = { event, priority in
8686
self.callTypedEventCallback(callback, forEvent: event, priority: priority)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// EventListening.swift
3+
// Copyright (c) 2022, Flowduino
4+
// Authored by Simon J. Stuart on 4th August 2022
5+
//
6+
// Subject to terms, restrictions, and liability waiver of the MIT License
7+
//
8+
9+
import Foundation
10+
11+
public protocol EventListening: AnyObject {
12+
/**
13+
Invoke this method to automatically register any Event Listener callback bearing the `@EventMethod` wrapper.
14+
- Author: Simon J. Stuart
15+
- Version: 4.1.0
16+
- Note: Any Event Callback implemented this way will be automatically registered for you.
17+
````
18+
@EventMethod<MyEventThreadType, MyEventType>
19+
private var onMyEvent = {
20+
(self, event: MyEventType, priority: EventPriority) in
21+
/// Do something with `MyEventType` via its `event` reference here
22+
}
23+
````
24+
*/
25+
func registerListeners()
26+
27+
/**
28+
Invoke this method to automatically unregister any Event Listener callback bearing the `@EventMethod` wrapper.
29+
- Author: Simon J. Stuart
30+
- Version: 4.1.0
31+
*/
32+
func unregisterListeners()
33+
}
34+
35+
/**
36+
Universal implementations to automatically Register and Unregister `@EventMethod`-decorated Event Listener Callbacks using Reflection
37+
- Author: Simon J. Stuart
38+
- Version: 4.1.0
39+
*/
40+
public extension EventListening {
41+
func registerListeners() {
42+
let mirror = Mirror(reflecting: self)
43+
for child in mirror.children {
44+
if var child = child.value as? (any EventMethodContainer) {
45+
child.owner = self
46+
}
47+
}
48+
}
49+
50+
func unregisterListeners() {
51+
let mirror = Mirror(reflecting: self)
52+
for child in mirror.children {
53+
if var child = child.value as? (any EventMethodContainer) {
54+
child.unregister()
55+
}
56+
}
57+
}
58+
}

Sources/EventDrivenSwift/EventReceiver/EventReceiver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ import Observable
2020
- Note: `EventThread` inherits from this
2121
*/
2222
open class EventReceiver: EventHandler, EventReceiving {
23-
23+
2424
}

0 commit comments

Comments
 (0)