1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 /**
4  An object that can dispatch native events.
5  */
6 public final class EventDispatcher {
7   /**
8    Type of the event dispatcher handler.
9    - Note: It must be marked as `@convention(block)` as long as we support Objective-C views and thus need to use `setValue(_:forKey:)`.
10    */
11   internal typealias Handler = @convention(block) ([String: Any]) -> Void
12 
13   /**
14    A custom name of the dispatching event. The default name is usually a name of the property holding the dispatcher.
15    */
16   internal var customName: String?
17 
18   /**
19    A function that is invoked to dispatch the event, no-op by default.
20    Something that manages the events should override it.
21    */
22   internal var handler: Handler?
23 
24   /**
25    Default initializer of the event dispatcher. Provide a custom name if you want the dispatcher
26    to refer to an event with different name than the property holding the dispatcher.
27    */
28   public init(_ customName: String? = nil) {
29     self.customName = customName
30   }
31 
32   // MARK: - Calling as function
33 
34   /**
35    Dispatches the event with the given dictionary as a payload.
36    */
callAsFunctionnull37   public func callAsFunction(_ payload: [String: Any]) {
38     handler?(payload)
39   }
40 
41   /**
42    Dispatches the event with the given record as a payload.
43    */
callAsFunctionnull44   public func callAsFunction(_ payload: Record) {
45     handler?(payload.toDictionary())
46   }
47 
48   /**
49    Dispatches the event with an empty payload.
50    */
callAsFunctionnull51   public func callAsFunction() {
52     handler?([:])
53   }
54 }
55 
56 /**
57  Installs convenient event dispatchers for the given event and view.
58  The provided handler can be specific to Paper or Fabric.
59  */
installEventDispatcher<ViewType>null60 internal func installEventDispatcher<ViewType>(forEvent eventName: String, onView view: ViewType, handler: @escaping EventDispatcher.Handler) {
61   // Find view's property that is of type `EventDispatcher` and refers to this particular event name.
62   let child = Mirror(reflecting: view).children.first {
63     return isEventDispatcherWithName($0, eventName)
64   }
65 
66   if var eventDispatcher = child?.value as? EventDispatcher {
67     eventDispatcher.handler = handler
68   } else if let view = view as? UIView, view.responds(to: Selector(eventName)) {
69     // This is to handle events in legacy views written in Objective-C.
70     // Note that the property should be of type EXDirectEventBlock.
71     view.setValue(handler, forKey: eventName)
72   } else {
73     log.warn("Couldn't find the property for event '\(eventName)' in '\(type(of: view))' class")
74   }
75 }
76 
77 /**
78  Checks whether the mirror child refers to the event dispatcher with the given event name.
79  */
isEventDispatcherWithNamenull80 private func isEventDispatcherWithName(_ mirrorChild: Mirror.Child, _ eventName: String) -> Bool {
81   guard let eventDispatcher = mirrorChild.value as? EventDispatcher else {
82     return false
83   }
84   return eventDispatcher.customName != nil ? eventDispatcher.customName == eventName : mirrorChild.label == eventName
85 }
86