1 /** 2 Each module that has a view manager definition needs to be wrapped by `RCTViewManager`. 3 Unfortunately, we can't use just one class because React Native checks for duplicated classes. 4 We're generating its subclasses in runtime as a workaround. 5 */ 6 @objc 7 public class ViewModuleWrapper: RCTViewManager { 8 /** 9 A reference to the module holder that stores the module definition. 10 */ 11 let wrappedModuleHolder: ModuleHolder 12 13 /** 14 The designated initializer. At first, we use this base class to hide `ModuleHolder` from Objective-C runtime. 15 */ 16 public init(_ wrappedModuleHolder: ModuleHolder) { 17 self.wrappedModuleHolder = wrappedModuleHolder 18 } 19 20 /** 21 Convenience initializer intended to be run on the subclasses and from Objective-C. 22 Initializes an object based on another one initialized with the base class. 23 */ 24 @objc 25 public convenience init(from module: ViewModuleWrapper) { 26 self.init(module.wrappedModuleHolder) 27 } 28 29 /** 30 Returns the original name of the wrapped module. 31 */ 32 @objc 33 public func name() -> String { 34 return wrappedModuleHolder.name 35 } 36 37 /** 38 Static function that returns the prefixed name of the wrapped module. 39 Here is just a stub that must be overriden in the autogenerated subclasses (see `createViewModuleWrapperClass`). 40 */ 41 @objc 42 dynamic public override class func moduleName() -> String! { 43 fatalError("Something unexpected has happened. The original implementation of `moduleName` method must be replaced in runtime (see `viewModuleWrapperClass` below).") 44 } 45 46 /** 47 Creates a view from the wrapped module. 48 */ 49 @objc 50 public override func view() -> UIView! { 51 guard let view = wrappedModuleHolder.definition.viewManager?.createView() else { 52 fatalError("Module `\(wrappedModuleHolder.name)` doesn't define the view manager nor view factory.") 53 } 54 return view 55 } 56 57 /** 58 The config for `proxiedProperties` prop. In Objective-C style, this function is generated by `RCT_CUSTOM_VIEW_PROPERTY` macro. 59 */ 60 @objc 61 public class func propConfig_proxiedProperties() -> [String] { 62 return ["NSDictionary", "__custom__"]; 63 } 64 65 /** 66 The setter for `proxiedProperties` prop. In Objective-C style, this function is generated by `RCT_CUSTOM_VIEW_PROPERTY` macro. 67 */ 68 @objc 69 public func set_proxiedProperties(_ json: Any?, forView view: UIView, withDefaultView defaultView: UIView) { 70 guard let json = json as? [String: Any?], 71 let props = wrappedModuleHolder.definition.viewManager?.propsDict() else { 72 return 73 } 74 for (key, value) in json { 75 if let prop = props[key] { 76 prop.set(value: value, onView: view) 77 } 78 } 79 } 80 81 /** 82 Creates a subclass of `ViewModuleWrapper` in runtime. The new class overrides `moduleName` stub. 83 */ 84 @objc 85 public static func createViewModuleWrapperClass(viewName: String) -> ViewModuleWrapper.Type? { 86 // We're namespacing the view name so we know it uses our architecture. 87 let prefixedViewName = "ViewManagerAdapter_\(viewName)" 88 89 return prefixedViewName.withCString { viewNamePtr in 90 // Create a new meta class that inherits from `ViewModuleWrapper`. The class name passed here, doesn't work for Swift classes, 91 // so we also have to override `moduleName` class method. 92 let wrapperClass: AnyClass? = objc_allocateClassPair(ViewModuleWrapper.self, viewNamePtr, 0) 93 94 // Prepare the selector and new implementation that returns correct view name. 95 let moduleNameSel: Selector = #selector(moduleName) 96 let moduleNameBlock: @convention(block) () -> String = { prefixedViewName } 97 let moduleNameImp: IMP = imp_implementationWithBlock(moduleNameBlock) 98 99 // Get the `moduleName` class method and replace its implementation. 100 // We can assume the method already exists as it's defined in `ViewModuleManager` superclass, 101 // but if not then just add it with the proper implementation. 102 if let moduleNameMethod = class_getClassMethod(wrapperClass, moduleNameSel) { 103 method_setImplementation(moduleNameMethod, moduleNameImp) 104 } else { 105 class_addMethod(object_getClass(wrapperClass), moduleNameSel, moduleNameImp, "@@:") 106 } 107 108 return wrapperClass as? ViewModuleWrapper.Type 109 } 110 } 111 } 112