/** Each module that has a view manager definition needs to be wrapped by `RCTViewManager`. Unfortunately, we can't use just one class because React Native checks for duplicated classes. We're generating its subclasses in runtime as a workaround. */ @objc public class ViewModuleWrapper: RCTViewManager { /** A reference to the module holder that stores the module definition. */ let wrappedModuleHolder: ModuleHolder /** The designated initializer. At first, we use this base class to hide `ModuleHolder` from Objective-C runtime. */ public init(_ wrappedModuleHolder: ModuleHolder) { self.wrappedModuleHolder = wrappedModuleHolder } /** Convenience initializer intended to be run on the subclasses and from Objective-C. Initializes an object based on another one initialized with the base class. */ @objc public convenience init(from module: ViewModuleWrapper) { self.init(module.wrappedModuleHolder) } /** Returns the original name of the wrapped module. */ @objc public func name() -> String { return wrappedModuleHolder.name } /** Static function that returns the prefixed name of the wrapped module. Here is just a stub that must be overriden in the autogenerated subclasses (see `createViewModuleWrapperClass`). */ @objc dynamic public override class func moduleName() -> String! { fatalError("Something unexpected has happened. The original implementation of `moduleName` method must be replaced in runtime (see `viewModuleWrapperClass` below).") } /** Creates a view from the wrapped module. */ @objc public override func view() -> UIView! { guard let view = wrappedModuleHolder.definition.viewManager?.createView() else { fatalError("Module `\(wrappedModuleHolder.name)` doesn't define the view manager nor view factory.") } return view } /** The config for `proxiedProperties` prop. In Objective-C style, this function is generated by `RCT_CUSTOM_VIEW_PROPERTY` macro. */ @objc public class func propConfig_proxiedProperties() -> [String] { return ["NSDictionary", "__custom__"]; } /** The setter for `proxiedProperties` prop. In Objective-C style, this function is generated by `RCT_CUSTOM_VIEW_PROPERTY` macro. */ @objc public func set_proxiedProperties(_ json: Any?, forView view: UIView, withDefaultView defaultView: UIView) { guard let json = json as? [String: Any?], let props = wrappedModuleHolder.definition.viewManager?.propsDict() else { return } for (key, value) in json { if let prop = props[key] { prop.set(value: value, onView: view) } } } /** Creates a subclass of `ViewModuleWrapper` in runtime. The new class overrides `moduleName` stub. */ @objc public static func createViewModuleWrapperClass(viewName: String) -> ViewModuleWrapper.Type? { // We're namespacing the view name so we know it uses our architecture. let prefixedViewName = "ViewManagerAdapter_\(viewName)" return prefixedViewName.withCString { viewNamePtr in // Create a new meta class that inherits from `ViewModuleWrapper`. The class name passed here, doesn't work for Swift classes, // so we also have to override `moduleName` class method. let wrapperClass: AnyClass? = objc_allocateClassPair(ViewModuleWrapper.self, viewNamePtr, 0) // Prepare the selector and new implementation that returns correct view name. let moduleNameSel: Selector = #selector(moduleName) let moduleNameBlock: @convention(block) () -> String = { prefixedViewName } let moduleNameImp: IMP = imp_implementationWithBlock(moduleNameBlock) // Get the `moduleName` class method and replace its implementation. // We can assume the method already exists as it's defined in `ViewModuleManager` superclass, // but if not then just add it with the proper implementation. if let moduleNameMethod = class_getClassMethod(wrapperClass, moduleNameSel) { method_setImplementation(moduleNameMethod, moduleNameImp) } else { class_addMethod(object_getClass(wrapperClass), moduleNameSel, moduleNameImp, "@@:") } return wrapperClass as? ViewModuleWrapper.Type } } }