116 lines
3.5 KiB
Swift
116 lines
3.5 KiB
Swift
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
|
||
}
|
||
}
|