From c63ea4c3a0448ace4ca44e9f4f8ef6f9c8c96296 Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Wed, 9 Nov 2022 10:48:49 -0800 Subject: [PATCH] react-native code-gen > Add a C++ only TurboModule example (for Android/iOS/macOS/Windows) (#35138) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35138 Changelog: [General][Added] - Add a C++ only TurboModule example (for Android/iOS/macOS/Windows) react-native@0.69 introduced a new bridging layer to ease integration for pure C++ TurboModules using C++ std:: types directly instead of the lower level jsi:: types: https://github.com/facebook/react-native/tree/v0.69.0/ReactCommon/react/bridging This bridging layer can be used in JSI functions or more conveniently in C++ TurboModules. Here is a example of an C++ only TurboModule which will work on Android and iOS and macOS/Windows (using microsoft/react-native-macos|windows) only using flow/TypeScript and standard C++ types. C++ only TurboModules are very handy as they do not require to work with JSI APIs - instead std:: or custom C++ can by used. Reviewed By: javache Differential Revision: D39011736 fbshipit-source-id: 84c833d8540671fde8505f1aeb0265074b248730 --- packages/rn-tester/BUCK | 27 +++ .../NativeCxxModuleExample/CMakeLists.txt | 29 +++ .../NativeCxxModuleExample.cpp | 110 +++++++++ .../NativeCxxModuleExample.h | 68 ++++++ .../NativeCxxModuleExample.js | 64 +++++ .../NativeCxxModuleExample.podspec | 31 +++ .../NativeCxxModuleExample_ConstantsStruct.h | 37 +++ .../NativeCxxModuleExample_ObjectStruct.h | 52 ++++ .../NativeCxxModuleExample_ValueStruct.h | 50 ++++ packages/rn-tester/Podfile | 3 + .../RNTester/RNTesterTurboModuleProvider.mm | 9 +- .../android/app/src/main/jni/CMakeLists.txt | 4 + .../android/app/src/main/jni/OnLoad.cpp | 4 + .../NativeCxxModuleExampleExample.js | 222 ++++++++++++++++++ .../TurboModule/TurboCxxModuleExample.js | 27 +++ .../js/utils/RNTesterList.android.js | 5 + .../rn-tester/js/utils/RNTesterList.ios.js | 4 + 17 files changed, 745 insertions(+), 1 deletion(-) create mode 100644 packages/rn-tester/NativeCxxModuleExample/CMakeLists.txt create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ConstantsStruct.h create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ObjectStruct.h create mode 100644 packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ValueStruct.h create mode 100644 packages/rn-tester/js/examples/TurboModule/NativeCxxModuleExampleExample.js create mode 100644 packages/rn-tester/js/examples/TurboModule/TurboCxxModuleExample.js diff --git a/packages/rn-tester/BUCK b/packages/rn-tester/BUCK index 610c5247756e3e..dae1544ba4ede9 100644 --- a/packages/rn-tester/BUCK +++ b/packages/rn-tester/BUCK @@ -1,3 +1,4 @@ +load("@fbsource//tools/build_defs:glob_defs.bzl", "subdir_glob") load("@fbsource//xplat/hermes/defs:hermes.bzl", "HERMES_BYTECODE_VERSION") load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native") load("//tools/build_defs:fb_xplat_platform_specific_rule.bzl", "fb_xplat_platform_specific_rule") @@ -10,6 +11,7 @@ load("//tools/build_defs/apple:flag_defs.bzl", "get_objc_arc_preprocessor_flags" load("//tools/build_defs/oss:metro_defs.bzl", "rn_library") load( "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", "APPLE", "YOGA_APPLE_TARGET", "js_library_glob", @@ -49,6 +51,7 @@ rn_library( srcs = js_library_glob( [ "js", + "NativeCxxModuleExample", "NativeModuleExample", "NativeComponentExample", "RCTTest", @@ -61,10 +64,13 @@ rn_library( ], ), codegen_components = True, + codegen_modules = True, labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", ], native_component_spec_name = "AppSpecs", + native_module_android_package_name = "com.facebook.fbreact.specs", + native_module_spec_name = "AppSpecs", skip_processors = True, visibility = ["PUBLIC"], deps = [ @@ -319,3 +325,24 @@ rn_xplat_cxx_library2( "//xplat/js/react-native-github:RCTFabricComponentViewsBase", ], ) + +rn_xplat_cxx_library2( + name = "NativeCxxModuleExample", + srcs = glob(["NativeCxxModuleExample/*.cpp"]), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("NativeCxxModuleExample", "*.h"), + ], + prefix = "NativeCxxModuleExample", + ), + fbandroid_compiler_flags = [ + "-fexceptions", + "-frtti", + ], + platforms = (ANDROID, APPLE), + visibility = ["PUBLIC"], + deps = [ + ":AppSpecsJSI", + ], +) diff --git a/packages/rn-tester/NativeCxxModuleExample/CMakeLists.txt b/packages/rn-tester/NativeCxxModuleExample/CMakeLists.txt new file mode 100644 index 00000000000000..ce1fa2d8980137 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fexceptions + -frtti + -std=c++17 + -Wall + -Wpedantic + -Wno-gnu-zero-variadic-macro-arguments + -DFOLLY_NO_CONFIG=1 + -DLOG_TAG=\"ReactNative\") + +file(GLOB nativecxxmoduleexample_SRC CONFIGURE_DEPENDS *.cpp) +add_library(nativecxxmoduleexample STATIC ${nativecxxmoduleexample_SRC}) + +target_include_directories(nativecxxmoduleexample PUBLIC .) +target_include_directories(react_codegen_AppSpecs PUBLIC .) + +target_link_libraries(nativecxxmoduleexample + fbjni + jsi + react_nativemodule_core + react_codegen_AppSpecs) diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp new file mode 100644 index 00000000000000..78c9c1e3a27c17 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "NativeCxxModuleExample.h" + +namespace facebook::react { + +NativeCxxModuleExample::NativeCxxModuleExample( + std::shared_ptr jsInvoker) + : NativeCxxModuleExampleCxxSpec(std::move(jsInvoker)) {} + +void NativeCxxModuleExample::getValueWithCallback( + jsi::Runtime &rt, + AsyncCallback callback) { + callback({"value from callback!"}); +} + +std::vector> NativeCxxModuleExample::getArray( + jsi::Runtime &rt, + std::vector> arg) { + return arg; +} + +bool NativeCxxModuleExample::getBool(jsi::Runtime &rt, bool arg) { + return arg; +} + +ConstantsStruct NativeCxxModuleExample::getConstants(jsi::Runtime &rt) { + return ConstantsStruct{true, 69, "react-native"}; +} + +int32_t NativeCxxModuleExample::getEnum(jsi::Runtime &rt, int32_t arg) { + return arg; +} + +std::map> NativeCxxModuleExample::getMap( + jsi::Runtime &rt, + std::map> arg) { + return arg; +} + +double NativeCxxModuleExample::getNumber(jsi::Runtime &rt, double arg) { + return arg; +} + +ObjectStruct NativeCxxModuleExample::getObject( + jsi::Runtime &rt, + ObjectStruct arg) { + return arg; +} + +std::set NativeCxxModuleExample::getSet( + jsi::Runtime &rt, + std::set arg) { + return arg; +} + +std::string NativeCxxModuleExample::getString( + jsi::Runtime &rt, + std::string arg) { + return arg; +} + +std::string NativeCxxModuleExample::getUnion( + jsi::Runtime &rt, + float x, + std::string y, + jsi::Object z) { + std::string result = "x: " + std::to_string(x) + ", y: " + y + ", z: { "; + if (z.hasProperty(rt, "value")) { + result += "value: "; + result += std::to_string(z.getProperty(rt, "value").getNumber()); + } else if (z.hasProperty(rt, "low")) { + result += "low: "; + result += z.getProperty(rt, "low").getString(rt).utf8(rt); + } + result += " }"; + return result; +} + +ValueStruct NativeCxxModuleExample::getValue( + jsi::Runtime &rt, + double x, + std::string y, + ObjectStruct z) { + ValueStruct result{x, y, z}; + return result; +} + +AsyncPromise NativeCxxModuleExample::getValueWithPromise( + jsi::Runtime &rt, + bool error) { + auto promise = AsyncPromise(rt, jsInvoker_); + if (error) { + promise.reject("intentional promise rejection"); + } else { + promise.resolve("result!"); + } + return promise; +} + +void NativeCxxModuleExample::voidFunc(jsi::Runtime &rt) { + // Nothing to do +} + +} // namespace facebook::react diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h new file mode 100644 index 00000000000000..467a150199c0da --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#if __has_include() // CocoaPod headers on Apple +#include +#elif __has_include("AppSpecsJSI.h") // Cmake headers on Android +#include "AppSpecsJSI.h" +#else // BUCK headers +#include +#endif +#include +#include +#include +#include +#include "NativeCxxModuleExample_ConstantsStruct.h" +#include "NativeCxxModuleExample_ObjectStruct.h" +#include "NativeCxxModuleExample_ValueStruct.h" + +namespace facebook::react { + +class NativeCxxModuleExample + : public NativeCxxModuleExampleCxxSpec { + public: + NativeCxxModuleExample(std::shared_ptr jsInvoker); + + void getValueWithCallback( + jsi::Runtime &rt, + AsyncCallback callback); + + std::vector> getArray( + jsi::Runtime &rt, + std::vector> arg); + + bool getBool(jsi::Runtime &rt, bool arg); + + ConstantsStruct getConstants(jsi::Runtime &rt); + + int32_t getEnum(jsi::Runtime &rt, int32_t arg); + + std::map> getMap( + jsi::Runtime &rt, + std::map> arg); + + double getNumber(jsi::Runtime &rt, double arg); + + ObjectStruct getObject(jsi::Runtime &rt, ObjectStruct arg); + + std::set getSet(jsi::Runtime &rt, std::set arg); + + std::string getString(jsi::Runtime &rt, std::string arg); + + std::string getUnion(jsi::Runtime &rt, float x, std::string y, jsi::Object z); + + ValueStruct + getValue(jsi::Runtime &rt, double x, std::string y, ObjectStruct z); + + AsyncPromise getValueWithPromise(jsi::Runtime &rt, bool error); + + void voidFunc(jsi::Runtime &rt); +}; + +} // namespace facebook::react diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js new file mode 100644 index 00000000000000..d48614a2ca9171 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; + +import {TurboModuleRegistry} from 'react-native'; + +/** FIXME: Enable flow-enum support +export enum EnumInt { + A = 23, + B = 42, +} +*/ + +export type UnionFloat = 1.44 | 2.88 | 5.76; +export type UnionString = 'One' | 'Two' | 'Three'; +export type UnionObject = {value: number} | {low: string}; + +export type ConstantsStruct = {| + const1: boolean, + const2: number, + const3: string, +|}; + +export type ObjectStruct = {| + a: number, + b: string, + c?: ?string, +|}; + +export type ValueStruct = {| + x: number, + y: string, + z: ObjectStruct, +|}; + +export interface Spec extends TurboModule { + +getArray: (arg: Array) => Array; + +getBool: (arg: boolean) => boolean; + +getConstants: () => ConstantsStruct; + // FIXME: Enable flow-enum support + +getEnum: (arg: number /*EnumInt*/) => number /*EnumInt*/; + +getMap: (arg: {[key: string]: ?number}) => {[key: string]: ?number}; + +getNumber: (arg: number) => number; + +getObject: (arg: ObjectStruct) => ObjectStruct; + +getSet: (arg: Array) => Array; + +getString: (arg: string) => string; + +getUnion: (x: UnionFloat, y: UnionString, z: UnionObject) => string; + +getValue: (x: number, y: string, z: ObjectStruct) => ValueStruct; + +getValueWithCallback: (callback: (value: string) => void) => void; + +getValueWithPromise: (error: boolean) => Promise; + +voidFunc: () => void; +} + +export default (TurboModuleRegistry.get( + 'NativeCxxModuleExampleCxx', +): ?Spec); diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec new file mode 100644 index 00000000000000..4eaef3a3978898 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.podspec @@ -0,0 +1,31 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "../package.json"))) + +Pod::Spec.new do |s| + s.name = "NativeCxxModuleExample" + s.version = package["version"] + s.summary = package["description"] + s.description = "NativeCxxModuleExample" + s.homepage = "https://github.com/facebook/react-native.git" + s.license = "MIT" + s.platforms = { :ios => "12.4" } + s.compiler_flags = '-Wno-nullability-completeness' + s.author = "Meta Platforms, Inc. and its affiliates" + s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "#{s.version}" } + s.source_files = "**/*.{h,cpp}" + s.requires_arc = true + s.pod_target_xcconfig = { + "USE_HEADERMAP" => "YES", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + + install_modules_dependencies(s) + + s.dependency "ReactCommon/turbomodule/core" +end diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ConstantsStruct.h b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ConstantsStruct.h new file mode 100644 index 00000000000000..4966c69d23551f --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ConstantsStruct.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +struct ConstantsStruct { + bool const1; + int32_t const2; + std::string const3; + bool operator==(const ConstantsStruct &other) const { + return const1 == other.const1 && const2 == other.const2 && + const3 == other.const3; + } +}; + +template <> +struct Bridging { + static jsi::Object toJs(jsi::Runtime &rt, const ConstantsStruct &value) { + auto result = facebook::jsi::Object(rt); + result.setProperty(rt, "const1", bridging::toJs(rt, value.const1)); + result.setProperty(rt, "const2", bridging::toJs(rt, value.const2)); + result.setProperty(rt, "const3", bridging::toJs(rt, value.const3)); + return result; + } +}; + +} // namespace facebook::react diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ObjectStruct.h b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ObjectStruct.h new file mode 100644 index 00000000000000..e430a3cb36f5a0 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ObjectStruct.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +struct ObjectStruct { + int32_t a; + std::string b; + std::optional c; + bool operator==(const ObjectStruct &other) const { + return a == other.a && b == other.b && c == other.c; + } +}; + +template <> +struct Bridging { + static ObjectStruct fromJs( + jsi::Runtime &rt, + const jsi::Object &value, + const std::shared_ptr &jsInvoker) { + ObjectStruct result{ + bridging::fromJs(rt, value.getProperty(rt, "a"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "b"), jsInvoker), + bridging::fromJs>( + rt, value.getProperty(rt, "c"), jsInvoker)}; + + return result; + } + + static jsi::Object toJs(jsi::Runtime &rt, const ObjectStruct &value) { + auto result = facebook::jsi::Object(rt); + result.setProperty(rt, "a", bridging::toJs(rt, value.a)); + result.setProperty(rt, "b", bridging::toJs(rt, value.b)); + if (value.c) { + result.setProperty(rt, "c", bridging::toJs(rt, value.c.value())); + } + return result; + } +}; + +} // namespace facebook::react diff --git a/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ValueStruct.h b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ValueStruct.h new file mode 100644 index 00000000000000..7e44c92113b274 --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample_ValueStruct.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include "NativeCxxModuleExample_ObjectStruct.h" + +namespace facebook::react { + +struct ValueStruct { + double x; + std::string y; + ObjectStruct z; + bool operator==(const ValueStruct &other) const { + return x == other.x && y == other.y && z == other.z; + } +}; + +template <> +struct Bridging { + static ValueStruct fromJs( + jsi::Runtime &rt, + const jsi::Object &value, + const std::shared_ptr &jsInvoker) { + ValueStruct result{ + bridging::fromJs(rt, value.getProperty(rt, "x"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "y"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "z"), jsInvoker)}; + return result; + } + + static jsi::Object toJs(jsi::Runtime &rt, const ValueStruct &value) { + auto result = facebook::jsi::Object(rt); + result.setProperty(rt, "x", bridging::toJs(rt, value.x)); + result.setProperty(rt, "y", bridging::toJs(rt, value.y)); + result.setProperty(rt, "z", bridging::toJs(rt, value.z)); + return result; + } +}; + +} // namespace facebook::react diff --git a/packages/rn-tester/Podfile b/packages/rn-tester/Podfile index 45ff904de3b762..3e6f2c798d1cab 100644 --- a/packages/rn-tester/Podfile +++ b/packages/rn-tester/Podfile @@ -48,6 +48,9 @@ def pods(target_name, options = {}, use_flipper: !IN_CI && !USE_FRAMEWORKS) # RNTester native modules and components pod 'ScreenshotManager', :path => "NativeModuleExample" + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' + pod 'NativeCxxModuleExample', :path => "NativeCxxModuleExample" + end end target 'RNTester' do diff --git a/packages/rn-tester/RNTester/RNTesterTurboModuleProvider.mm b/packages/rn-tester/RNTester/RNTesterTurboModuleProvider.mm index b8532eab9db215..32e5c3418013a9 100644 --- a/packages/rn-tester/RNTester/RNTesterTurboModuleProvider.mm +++ b/packages/rn-tester/RNTester/RNTesterTurboModuleProvider.mm @@ -7,6 +7,9 @@ #import "RNTesterTurboModuleProvider.h" +#ifdef RCT_NEW_ARCH_ENABLED +#import +#endif #import #import #import @@ -28,7 +31,11 @@ Class RNTesterTurboModuleClassProvider(const char *name) if (name == "SampleTurboCxxModule") { return std::make_shared(jsInvoker); } - +#ifdef RCT_NEW_ARCH_ENABLED + if (name == "NativeCxxModuleExampleCxx") { + return std::make_shared(jsInvoker); + } +#endif return nullptr; } diff --git a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt index d1af2d92e230ca..e5bd4dde4bd31d 100644 --- a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt +++ b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt @@ -11,7 +11,11 @@ project(appmodules) include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) add_subdirectory(${REACT_COMMON_DIR}/react/nativemodule/samples/platform/android/ sampleturbomodule_build) +add_subdirectory(${REACT_COMMON_DIR}/../packages/rn-tester/NativeCxxModuleExample/ nativecxxmoduleexample_build) # RN Tester needs to link against the sample turbomobule target_link_libraries(${CMAKE_PROJECT_NAME} sampleturbomodule) +# RN Tester needs to link against the NativeCxxModuleExample +target_link_libraries(${CMAKE_PROJECT_NAME} + nativecxxmoduleexample) diff --git a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp index 81b8450dfee885..be31e906ef0273 100644 --- a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp +++ b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ void registerComponents( std::shared_ptr cxxModuleProvider( const std::string &name, const std::shared_ptr &jsInvoker) { + if (name == "NativeCxxModuleExampleCxx") { + return std::make_shared(jsInvoker); + } return nullptr; } diff --git a/packages/rn-tester/js/examples/TurboModule/NativeCxxModuleExampleExample.js b/packages/rn-tester/js/examples/TurboModule/NativeCxxModuleExampleExample.js new file mode 100644 index 00000000000000..ba62ba6e64ef4e --- /dev/null +++ b/packages/rn-tester/js/examples/TurboModule/NativeCxxModuleExampleExample.js @@ -0,0 +1,222 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict-local + */ + +import type {RootTag} from 'react-native/Libraries/ReactNative/RootTag'; + +import { + StyleSheet, + Text, + View, + FlatList, + Platform, + TouchableOpacity, + RootTagContext, +} from 'react-native'; +import * as React from 'react'; +import NativeCxxModuleExample /*EnumInt,*/ from '../../../NativeCxxModuleExample/NativeCxxModuleExample'; + +type State = {| + testResults: { + [string]: { + type: string, + value: mixed, + ... + }, + ... + }, +|}; + +type Examples = + | 'callback' + | 'getArray' + | 'getBool' + | 'getConstants' + | 'getEnum' + | 'getMap' + | 'getNumber' + | 'getObject' + | 'getSet' + | 'getString' + | 'getUnion' + | 'getValue' + | 'promise' + | 'rejectPromise' + | 'voidFunc'; + +class NativeCxxModuleExampleExample extends React.Component<{||}, State> { + static contextType: React$Context = RootTagContext; + + state: State = { + testResults: {}, + }; + + // Add calls to methods in TurboModule here + // $FlowFixMe[missing-local-annot] + _tests = { + callback: () => + NativeCxxModuleExample?.getValueWithCallback(callbackValue => + this._setResult('callback', callbackValue), + ), + getArray: () => + NativeCxxModuleExample?.getArray([ + {a: 1, b: 'foo'}, + {a: 2, b: 'bar'}, + null, + ]), + getBool: () => NativeCxxModuleExample?.getBool(true), + getConstants: () => NativeCxxModuleExample?.getConstants(), + getEnum: () => NativeCxxModuleExample?.getEnum(/*EnumInt.A*/ 2), + getMap: () => NativeCxxModuleExample?.getMap({a: 1, b: null, c: 3}), + getNumber: () => NativeCxxModuleExample?.getNumber(99.95), + getObject: () => + NativeCxxModuleExample?.getObject({a: 1, b: 'foo', c: null}), + getSet: () => NativeCxxModuleExample?.getSet([1, 1.1, 1.1, 1.1, 2]), + getString: () => NativeCxxModuleExample?.getString('Hello'), + getUnion: () => NativeCxxModuleExample?.getUnion(1.44, 'Two', {low: '12'}), + getValue: () => + NativeCxxModuleExample?.getValue(5, 'test', {a: 1, b: 'foo'}), + promise: () => + NativeCxxModuleExample?.getValueWithPromise(false).then(valuePromise => + this._setResult('promise', valuePromise), + ), + rejectPromise: () => + NativeCxxModuleExample?.getValueWithPromise(true) + .then(() => {}) + .catch(e => this._setResult('rejectPromise', e.message)), + voidFunc: () => NativeCxxModuleExample?.voidFunc(), + }; + + _setResult( + name: string | Examples, + result: + | $FlowFixMe + | void + | Array<$FlowFixMe> + | boolean + | {const1: boolean, const2: number, const3: string} + | number + | {[key: string]: ?number} + | Promise + | number + | string, + ) { + this.setState(({testResults}) => ({ + testResults: { + ...testResults, + /* $FlowFixMe[invalid-computed-prop] (>=0.111.0 site=react_native_fb) + * This comment suppresses an error found when Flow v0.111 was + * deployed. To see the error, delete this comment and run Flow. */ + [name]: {value: result, type: typeof result}, + }, + })); + } + + _renderResult(name: Examples): React.Node { + const result = this.state.testResults[name] || {}; + return ( + + {JSON.stringify(result.value)} + {result.type} + + ); + } + + componentDidMount(): void { + if (global.__turboModuleProxy == null) { + throw new Error( + 'Cannot load this example because TurboModule is not configured.', + ); + } + Object.keys(this._tests).forEach(item => + this._setResult(item, this._tests[item]()), + ); + } + + render(): React.Node { + return ( + + + + Object.keys(this._tests).forEach(item => + this._setResult(item, this._tests[item]()), + ) + }> + Run all tests + + this.setState({testResults: {}})} + style={[styles.column, styles.button]}> + Clear results + + + item} + renderItem={({item}) => ( + + this._setResult(item, this._tests[item]())}> + {item} + + {this._renderResult(item)} + + )} + /> + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + item: { + flexDirection: 'row', + margin: 6, + }, + column: { + flex: 2, + justifyContent: 'center', + padding: 3, + }, + result: { + alignItems: 'stretch', + justifyContent: 'space-between', + }, + value: { + fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', + fontSize: 12, + }, + type: { + color: '#333', + fontSize: 10, + }, + button: { + borderColor: '#444', + padding: 3, + flex: 1, + }, + buttonTextLarge: { + textAlign: 'center', + color: 'rgb(0,122,255)', + fontSize: 16, + padding: 6, + }, + buttonText: { + color: 'rgb(0,122,255)', + textAlign: 'center', + }, +}); + +module.exports = NativeCxxModuleExampleExample; diff --git a/packages/rn-tester/js/examples/TurboModule/TurboCxxModuleExample.js b/packages/rn-tester/js/examples/TurboModule/TurboCxxModuleExample.js new file mode 100644 index 00000000000000..18e9f53ae249d9 --- /dev/null +++ b/packages/rn-tester/js/examples/TurboModule/TurboCxxModuleExample.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +const React = require('react'); +const NativeCxxModuleExampleExample = require('./NativeCxxModuleExampleExample'); + +exports.displayName = (undefined: ?string); +exports.title = 'Cxx TurboModule'; +exports.category = 'Basic'; +exports.description = 'Usage of Cxx TurboModule'; +exports.examples = [ + { + title: 'TurboCxxModuleExample', + render: function (): React.Element { + return ; + }, + }, +]; diff --git a/packages/rn-tester/js/utils/RNTesterList.android.js b/packages/rn-tester/js/utils/RNTesterList.android.js index 5156c746ceef50..c8e836e82d01b2 100644 --- a/packages/rn-tester/js/utils/RNTesterList.android.js +++ b/packages/rn-tester/js/utils/RNTesterList.android.js @@ -284,6 +284,11 @@ const APIs: Array = [ category: 'Basic', module: require('../examples/TurboModule/TurboModuleExample'), }, + { + key: 'TurboCxxModuleExample', + category: 'Basic', + module: require('../examples/TurboModule/TurboCxxModuleExample'), + }, ]; if (ReactNativeFeatureFlags.shouldEmitW3CPointerEvents()) { diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js index cb0e34893de90b..6b92e41d15a6fa 100644 --- a/packages/rn-tester/js/utils/RNTesterList.ios.js +++ b/packages/rn-tester/js/utils/RNTesterList.ios.js @@ -311,6 +311,10 @@ const APIs: Array = [ module: require('../examples/TurboModule/TurboModuleExample'), supportsTVOS: false, }, + { + key: 'TurboCxxModuleExample', + module: require('../examples/TurboModule/TurboCxxModuleExample'), + }, { key: 'VibrationExample', module: require('../examples/Vibration/VibrationExample'),