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>null60internal 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 */ isEventDispatcherWithNamenull80private 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