import Foundation import UIKit /// Brownfield 运行时启动失败时抛出的错误。 public enum DuooomiBleRuntimeError: Error, LocalizedError { case hostManagerUnavailable case reactNativeViewUnavailable public var errorDescription: String? { switch self { case .hostManagerUnavailable: return "Unable to locate ReactNativeHostManager. Make sure the brownfield framework is linked correctly." case .reactNativeViewUnavailable: return "Unable to create ReactNativeView automatically. Pass createReactNativeView when starting the runtime." } } } /// 负责在宿主视图层中挂载隐藏的 RN 容器并启动 JS 运行时。 public final class DuooomiBleRuntime { /// 供宿主全局复用的单例实例。 public static let shared = DuooomiBleRuntime() private weak var mountedView: UIView? private weak var containerView: UIView? private init() {} /// 在指定容器中启动 Brownfield 运行时。 /// /// - Parameters: /// - containerView: 用于挂载隐藏 React Native 视图的宿主容器。 /// - initializeHost: 可选的宿主初始化逻辑;若不传则尝试自动初始化。 /// - createReactNativeView: 可选的 RN View 构造逻辑;若不传则尝试自动创建。 /// - Throws: `DuooomiBleRuntimeError`。 public func start( in containerView: UIView, initializeHost: (() -> Void)? = nil, createReactNativeView: (() -> UIView)? = nil ) throws { if mountedView != nil { return } if let initializeHost { initializeHost() } else { try autoInitializeHostManager() } let reactNativeView = createReactNativeView?() ?? try autoCreateReactNativeView() reactNativeView.isHidden = true reactNativeView.isUserInteractionEnabled = false reactNativeView.frame = CGRect(x: 0, y: 0, width: 1, height: 1) reactNativeView.accessibilityElementsHidden = true containerView.addSubview(reactNativeView) self.containerView = containerView self.mountedView = reactNativeView DuooomiBleManager.shared.start() } /// 停止运行时并移除已挂载的隐藏 RN 视图。 public func stop() { DuooomiBleManager.shared.stop() mountedView?.removeFromSuperview() mountedView = nil containerView = nil } private func autoInitializeHostManager() throws { let possibleClassNames = [ "ReactNativeHostManager", "DuooomiBleSDK.ReactNativeHostManager", ] for className in possibleClassNames { guard let managerClass = NSClassFromString(className) as? NSObject.Type else { continue } let sharedSelector = NSSelectorFromString("shared") let initializeSelector = NSSelectorFromString("initialize") let managerObject = (managerClass as AnyObject).perform(sharedSelector)?.takeUnretainedValue() as? NSObject if let managerObject, managerObject.responds(to: initializeSelector) { managerObject.perform(initializeSelector) return } } throw DuooomiBleRuntimeError.hostManagerUnavailable } private func autoCreateReactNativeView() throws -> UIView { let possibleClassNames = [ "ReactNativeView", "DuooomiBleSDK.ReactNativeView", ] for className in possibleClassNames { guard let viewClass = NSClassFromString(className) as? NSObject.Type else { continue } if let view = viewClass.init() as? UIView { return view } } throw DuooomiBleRuntimeError.reactNativeViewUnavailable } }