Skip to content

Commit

Permalink
react-native code-gen > Add a C++ only TurboModule example (for Andro…
Browse files Browse the repository at this point in the history
…id/iOS/macOS/Windows) (#35138)

Summary:
Pull Request resolved: #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
  • Loading branch information
christophpurrer authored and facebook-github-bot committed Nov 9, 2022
1 parent bbb3a61 commit d07575b
Show file tree
Hide file tree
Showing 17 changed files with 745 additions and 1 deletion.
27 changes: 27 additions & 0 deletions packages/rn-tester/BUCK
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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",
Expand Down Expand Up @@ -49,6 +51,7 @@ rn_library(
srcs = js_library_glob(
[
"js",
"NativeCxxModuleExample",
"NativeModuleExample",
"NativeComponentExample",
"RCTTest",
Expand All @@ -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 = [
Expand Down Expand Up @@ -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",
],
)
29 changes: 29 additions & 0 deletions packages/rn-tester/NativeCxxModuleExample/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
110 changes: 110 additions & 0 deletions packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp
Original file line number Diff line number Diff line change
@@ -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<CallInvoker> jsInvoker)
: NativeCxxModuleExampleCxxSpec(std::move(jsInvoker)) {}

void NativeCxxModuleExample::getValueWithCallback(
jsi::Runtime &rt,
AsyncCallback<std::string> callback) {
callback({"value from callback!"});
}

std::vector<std::optional<ObjectStruct>> NativeCxxModuleExample::getArray(
jsi::Runtime &rt,
std::vector<std::optional<ObjectStruct>> 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<std::string, std::optional<int32_t>> NativeCxxModuleExample::getMap(
jsi::Runtime &rt,
std::map<std::string, std::optional<int32_t>> 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<float> NativeCxxModuleExample::getSet(
jsi::Runtime &rt,
std::set<float> 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<std::string> NativeCxxModuleExample::getValueWithPromise(
jsi::Runtime &rt,
bool error) {
auto promise = AsyncPromise<std::string>(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
68 changes: 68 additions & 0 deletions packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h
Original file line number Diff line number Diff line change
@@ -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(<React-Codegen/AppSpecsJSI.h>) // CocoaPod headers on Apple
#include <React-Codegen/AppSpecsJSI.h>
#elif __has_include("AppSpecsJSI.h") // Cmake headers on Android
#include "AppSpecsJSI.h"
#else // BUCK headers
#include <AppSpecs/AppSpecsJSI.h>
#endif
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "NativeCxxModuleExample_ConstantsStruct.h"
#include "NativeCxxModuleExample_ObjectStruct.h"
#include "NativeCxxModuleExample_ValueStruct.h"

namespace facebook::react {

class NativeCxxModuleExample
: public NativeCxxModuleExampleCxxSpec<NativeCxxModuleExample> {
public:
NativeCxxModuleExample(std::shared_ptr<CallInvoker> jsInvoker);

void getValueWithCallback(
jsi::Runtime &rt,
AsyncCallback<std::string> callback);

std::vector<std::optional<ObjectStruct>> getArray(
jsi::Runtime &rt,
std::vector<std::optional<ObjectStruct>> arg);

bool getBool(jsi::Runtime &rt, bool arg);

ConstantsStruct getConstants(jsi::Runtime &rt);

int32_t getEnum(jsi::Runtime &rt, int32_t arg);

std::map<std::string, std::optional<int32_t>> getMap(
jsi::Runtime &rt,
std::map<std::string, std::optional<int32_t>> arg);

double getNumber(jsi::Runtime &rt, double arg);

ObjectStruct getObject(jsi::Runtime &rt, ObjectStruct arg);

std::set<float> getSet(jsi::Runtime &rt, std::set<float> 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<std::string> getValueWithPromise(jsi::Runtime &rt, bool error);

void voidFunc(jsi::Runtime &rt);
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -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<ObjectStruct | null>) => Array<ObjectStruct | null>;
+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<number>) => Array<number>;
+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<string>;
+voidFunc: () => void;
}

export default (TurboModuleRegistry.get<Spec>(
'NativeCxxModuleExampleCxx',
): ?Spec);
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit d07575b

Please sign in to comment.