diff --git a/packages/react-native-nitro-image/src/index.ts b/packages/react-native-nitro-image/src/index.ts index e16eaae66..a7bcd5159 100644 --- a/packages/react-native-nitro-image/src/index.ts +++ b/packages/react-native-nitro-image/src/index.ts @@ -12,15 +12,19 @@ export * from './specs/ImageFactory.nitro' /** * Constructors for creating instances of `Image`. */ -export const ImageConstructors = NitroModules.get('ImageFactory') +export const ImageConstructors = + NitroModules.createHybridObject('ImageFactory') /** * The Hybrid Test Object */ -export const HybridTestObject = NitroModules.get('TestObject') +export const HybridTestObject = + NitroModules.createHybridObject('TestObject') /** * The Swift-Kotlin implemented Hybrid Test Object */ export const HybridSwiftKotlinTestObject = - NitroModules.get('SwiftKotlinTestObject') + NitroModules.createHybridObject( + 'SwiftKotlinTestObject' + ) diff --git a/packages/react-native-nitro-modules/android/src/main/java/com/margelo/nitro/HybridObjectRegistry.java b/packages/react-native-nitro-modules/android/src/main/java/com/margelo/nitro/HybridObjectRegistry.java index eb21b8ef2..0f5dfa7c0 100644 --- a/packages/react-native-nitro-modules/android/src/main/java/com/margelo/nitro/HybridObjectRegistry.java +++ b/packages/react-native-nitro-modules/android/src/main/java/com/margelo/nitro/HybridObjectRegistry.java @@ -4,14 +4,14 @@ /** * A registry that holds initializers for HybridObjects. - * This will be used to initialize them from JS using `NitroModules.get(name)`. + * This will be used to initialize them from JS using `NitroModules.createHybridObject(name)`. * @noinspection JavaJniMissingFunction */ public class HybridObjectRegistry { /** * Registers the given HybridObject in the `HybridObjectRegistry`. * It will be uniquely identified via it's `hybridObjectName`, and can be initialized from - * JS using `NitroModules.get(name)` - which will call the `constructorFn` here. + * JS using `NitroModules.createHybridObject(name)` - which will call the `constructorFn` here. */ public static native void registerHybridObjectConstructor(String hybridObjectName, HybridObjectInitializer initializer); diff --git a/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.cpp b/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.cpp index ca4822109..81caba4d1 100644 --- a/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.cpp +++ b/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.cpp @@ -15,6 +15,19 @@ std::unordered_map return _constructorsMap; } +bool HybridObjectRegistry::hasHybridObject(const std::string& name) { + return getRegistry().contains(name); +} + +std::vector HybridObjectRegistry::getAllHybridObjectNames() { + std::vector keys; + keys.reserve(getRegistry().size()); + for (const auto& entry : getRegistry()) { + keys.push_back(entry.first); + } + return keys; +} + void HybridObjectRegistry::registerHybridObjectConstructor(const std::string& hybridObjectName, HybridObjectConstructorFn&& constructorFn) { Logger::log(TAG, "Registering HybridObject \"%s\"...", hybridObjectName); auto& map = HybridObjectRegistry::getRegistry(); diff --git a/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.hpp b/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.hpp index a05f3ca50..16ef81058 100644 --- a/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.hpp +++ b/packages/react-native-nitro-modules/cpp/registry/HybridObjectRegistry.hpp @@ -15,7 +15,7 @@ namespace margelo::nitro { /** * A registry that holds initializers for HybridObjects. - * This will be used to initialize them from JS using `NitroModules.get(name)`. + * This will be used to initialize them from JS using `NitroModules.createHybridObject(name)`. */ class HybridObjectRegistry { public: @@ -28,11 +28,13 @@ class HybridObjectRegistry { /** * Registers the given HybridObject in the `HybridObjectRegistry`. * It will be uniquely identified via it's `hybridObjectName`, and can be initialized from - * JS using `NitroModules.get(name)` - which will call the `constructorFn` here. + * JS using `NitroModules.createHybridObject(name)` - which will call the `constructorFn` here. */ static void registerHybridObjectConstructor(const std::string& hybridObjectName, HybridObjectConstructorFn&& constructorFn); static std::shared_ptr createHybridObject(const std::string& hybridObjectName); + static bool hasHybridObject(const std::string& hybridObjectName); + static std::vector getAllHybridObjectNames(); private: static std::unordered_map& getRegistry(); diff --git a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp index e0caaff31..b54367eb9 100644 --- a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp +++ b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp @@ -32,12 +32,14 @@ jsi::Value NativeNitroModules::get(jsi::Runtime& runtime, const jsi::PropNameID& } if (name == "createHybridObject") { return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "install"), 2, + runtime, jsi::PropNameID::forUtf8(runtime, "createHybridObject"), 2, [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { - if (count != 1 && count != 2) { +#if DEBUG + if (count != 1 && count != 2) [[unlikely]] { throw jsi::JSError(runtime, "NitroModules.createHybridObject(..) expects 1 or 2 arguments, but " + std::to_string(count) + " were supplied!"); } +#endif jsi::String objectName = args[0].asString(runtime); std::optional optionalArgs = std::nullopt; if (count > 1) { @@ -47,6 +49,25 @@ jsi::Value NativeNitroModules::get(jsi::Runtime& runtime, const jsi::PropNameID& return createHybridObject(runtime, objectName, optionalArgs); }); } + if (name == "hasHybridObject") { + return jsi::Function::createFromHostFunction( + runtime, jsi::PropNameID::forUtf8(runtime, "hasHybridObject"), 1, + [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { +#if DEBUG + if (count != 1) [[unlikely]] { + throw jsi::JSError(runtime, + "NitroModules.hasHybridObject(..) expects 1 argument (name), but received " + std::to_string(count) + "!"); + } +#endif + jsi::String objectName = args[0].asString(runtime); + return hasHybridObject(runtime, objectName); + }); + } + if (name == "getAllHybridObjectNames") { + return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "getAllHybridObjectNames"), 0, + [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, + size_t count) -> jsi::Value { return getAllHybridObjectNames(runtime); }); + } return jsi::Value::undefined(); } @@ -66,4 +87,19 @@ jsi::Value NativeNitroModules::createHybridObject(jsi::Runtime& runtime, const j return hybridObject->toObject(runtime); } +jsi::Value NativeNitroModules::hasHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName) { + std::string name = hybridObjectName.utf8(runtime); + bool exists = HybridObjectRegistry::hasHybridObject(name); + return exists; +} + +jsi::Value NativeNitroModules::getAllHybridObjectNames(jsi::Runtime& runtime) { + std::vector keys = HybridObjectRegistry::getAllHybridObjectNames(); + jsi::Array array(runtime, keys.size()); + for (size_t i = 0; i < keys.size(); i++) { + array.setValueAtIndex(runtime, i, jsi::String::createFromUtf8(runtime, keys[i])); + } + return array; +} + } // namespace facebook::react diff --git a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp index 20d398eed..9c6a67fce 100644 --- a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp +++ b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp @@ -24,6 +24,8 @@ class NativeNitroModules : public TurboModule { void install(jsi::Runtime& runtime); jsi::Value createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName, const std::optional& args); + jsi::Value hasHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName); + jsi::Value getAllHybridObjectNames(jsi::Runtime& runtime); public: constexpr static auto kModuleName = "NitroModulesCxx"; diff --git a/packages/react-native-nitro-modules/src/NativeNitroModules.ts b/packages/react-native-nitro-modules/src/NativeNitroModules.ts index 39d12e48b..7246d5faa 100644 --- a/packages/react-native-nitro-modules/src/NativeNitroModules.ts +++ b/packages/react-native-nitro-modules/src/NativeNitroModules.ts @@ -6,6 +6,8 @@ import { ModuleNotFoundError } from './ModuleNotFoundError' export interface Spec extends TurboModule { install(): void createHybridObject(name: string, args?: UnsafeObject): UnsafeObject + hasHybridObject(name: string): boolean + getAllHybridObjectNames(): string[] } let turboModule: Spec | undefined diff --git a/packages/react-native-nitro-modules/src/NitroModules.ts b/packages/react-native-nitro-modules/src/NitroModules.ts index 1372327fb..0cb00370f 100644 --- a/packages/react-native-nitro-modules/src/NitroModules.ts +++ b/packages/react-native-nitro-modules/src/NitroModules.ts @@ -22,9 +22,23 @@ export const NitroModules = { * @returns An instance of {@linkcode T} * @throws an Error if {@linkcode T} has not been registered under the name {@linkcode name}. */ - get>(name: string): T { + createHybridObject>(name: string): T { const nitro = getNativeNitroModules() const instance = nitro.createHybridObject(name) return instance as T }, + /** + * Get a list of all registered Hybrid Objects. + */ + getAllHybridObjectNames(): string[] { + const nitro = getNativeNitroModules() + return nitro.getAllHybridObjectNames() + }, + /** + * Returns whether a HybridObject under the given {@linkcode name} is registered, or not. + */ + hasHybridObject(name: string): boolean { + const nitro = getNativeNitroModules() + return nitro.hasHybridObject(name) + }, }