1 import UIKit 2 import ObjectiveC 3 4 /** 5 A protocol that helps in identifying whether the instance of `ViewModuleWrapper` is of a dynamically created class. 6 */ 7 @objc 8 protocol DynamicModuleWrapperProtocol { 9 @objc wrappedModulenull10 optional func wrappedModule() -> ViewModuleWrapper 11 } 12 13 /** 14 Each module that has a view manager definition needs to be wrapped by `RCTViewManager`. 15 Unfortunately, we can't use just one class because React Native checks for duplicated classes. 16 We're generating its subclasses in runtime as a workaround. 17 */ 18 @objc 19 public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtocol { 20 /** 21 A reference to the module holder that stores the module definition. 22 Enforced unwrapping is required since it can be set right after the object is initialized. 23 */ 24 var wrappedModuleHolder: ModuleHolder! 25 26 /** 27 The designated initializer. At first, we use this base class to hide `ModuleHolder` from Objective-C runtime. 28 */ 29 public init(_ wrappedModuleHolder: ModuleHolder) { 30 self.wrappedModuleHolder = wrappedModuleHolder 31 } 32 33 /** 34 The designated initializer that is used by React Native to create module instances. 35 https://github.com/facebook/react-native/blob/540c41be9/packages/react-native/React/Views/RCTComponentData.m#L506-L507 36 It doesn't matter to return dummy class here. The wrapper will then to subclass dynamically. 37 Must be called on a dynamic class to get access to underlying wrapped module. Throws fatal exception otherwise. 38 */ 39 @objc 40 public override init() { 41 super.init() 42 guard let module = (self as DynamicModuleWrapperProtocol).wrappedModule?() else { 43 return 44 } 45 self.wrappedModuleHolder = module.wrappedModuleHolder 46 } 47 48 /** 49 Dummy initializer, for use only in `EXModuleRegistryAdapter.extraModulesForModuleRegistry:`. 50 */ 51 @objc 52 public init(dummy: Any?) { 53 super.init() 54 } 55 56 /** 57 Returns the original name of the wrapped module. 58 */ 59 @objc 60 public func name() -> String { 61 return wrappedModuleHolder.name 62 } 63 64 /** 65 Static function that returns the class name, but keep in mind that dynamic wrappers 66 have custom class name (see `objc_allocateClassPair` invocation in `createViewModuleWrapperClass`). 67 */ 68 @objc 69 public override class func moduleName() -> String { 70 return NSStringFromClass(Self.self) 71 } 72 73 /** 74 The view manager wrapper doesn't require main queue setup — it doesn't call any UI-related stuff on `init`. 75 Also, lazy-loaded modules must return false here. 76 */ 77 @objc 78 public override class func requiresMainQueueSetup() -> Bool { 79 return false 80 } 81 82 /** 83 Creates a view from the wrapped module. 84 */ 85 @objc 86 public override func view() -> UIView! { 87 guard let appContext = wrappedModuleHolder.appContext else { 88 fatalError(Exceptions.AppContextLost().reason) 89 } 90 guard let view = wrappedModuleHolder.definition.viewManager?.createView(appContext: appContext) else { 91 fatalError("Cannot create a view from module '\(self.name)'") 92 } 93 return view 94 } 95 96 public static let viewManagerAdapterPrefix = "ViewManagerAdapter_" 97 98 /** 99 Creates a subclass of `ViewModuleWrapper` in runtime. The new class overrides `moduleName` stub. 100 */ 101 @objc 102 public static func createViewModuleWrapperClass(module: ViewModuleWrapper) -> ViewModuleWrapper.Type? { 103 // We're namespacing the view name so we know it uses our architecture. 104 let prefixedViewName = "\(viewManagerAdapterPrefix)\(module.name())" 105 106 return prefixedViewName.withCString { viewNamePtr in 107 // Create a new class that inherits from `ViewModuleWrapper`. The class name passed here, doesn't work for Swift classes, 108 // so we also have to override `moduleName` class method. 109 let wrapperClass: AnyClass? = objc_allocateClassPair(ViewModuleWrapper.self, viewNamePtr, 0) 110 111 // Dynamically add instance method returning wrapped module to the dynamic wrapper class. 112 // React Native initializes modules with `init` without params, 113 // so there is no other way to pass it to the instances. 114 let wrappedModuleBlock: @convention(block) () -> ViewModuleWrapper = { module } 115 let wrappedModuleImp: IMP = imp_implementationWithBlock(wrappedModuleBlock) 116 class_addMethod(wrapperClass, Selector("wrappedModule"), wrappedModuleImp, "@@:") 117 118 return wrapperClass as? ViewModuleWrapper.Type 119 } 120 } 121 } 122 123 // The direct event implementation can be cached and lazy-loaded (global and static variables are lazy by default in Swift). 124 let directEventBlockImplementation = imp_implementationWithBlock({ ["RCTDirectEventBlock"] } as @convention(block) () -> [String]) 125