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