Skip to content

Commit a6607c0

Browse files
okwasniewskifacebook-github-bot
authored andcommitted
feat: js runtime switching C API for Swift (#49489)
Summary: This PR implements a C API to switch JS Engines that can be used from Swift. Here is an example: ```swift import React import React_RCTAppDelegate import ReactAppDependencyProvider import UIKit import RCTRuntime class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { override func sourceURL(for bridge: RCTBridge) -> URL? { self.bundleURL() } override func bundleURL() -> URL? { #if DEBUG RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") #else Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } override func createJSRuntimeFactory() -> JSRuntimeFactory { jsrt_create_jsc_factory() // Easily switch engines here } } ``` ## Changelog: [IOS] [ADDED] - js runtime C API for Swift Pull Request resolved: #49489 Test Plan: CI Green Reviewed By: huntie Differential Revision: D69976988 Pulled By: cipolleschi fbshipit-source-id: 9333ec62ca99a28c3121f558bbff1ce0457779e3
1 parent 785249d commit a6607c0

20 files changed

+276
-26
lines changed

packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
#endif
2424
#import <React/RCTComponentViewFactory.h>
2525
#import <React/RCTComponentViewProtocol.h>
26-
#if USE_HERMES
27-
#import <ReactCommon/RCTHermesInstance.h>
28-
#elif USE_THIRD_PARTY_JSC != 1
29-
#import <ReactCommon/RCTJscInstance.h>
30-
#endif // USE_HERMES
3126
#import <react/nativemodule/defaults/DefaultTurboModules.h>
3227

3328
using namespace facebook::react;

packages/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#import <ReactCommon/RCTHost.h>
1010
#import "RCTAppSetupUtils.h"
1111
#import "RCTDependencyProvider.h"
12+
#if USE_HERMES
13+
#import <React/RCTHermesInstanceFactory.h>
14+
#elif USE_THIRD_PARTY_JSC != 1
15+
#import <React/RCTJscInstanceFactory.h>
16+
#endif
1217

1318
#import <react/nativemodule/defaults/DefaultTurboModules.h>
1419

@@ -38,6 +43,15 @@ - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)
3843
rootViewController.view = rootView;
3944
}
4045

46+
- (JSRuntimeFactoryRef)createJSRuntimeFactory
47+
{
48+
#if USE_HERMES
49+
return jsrt_create_hermes_factory();
50+
#elif USE_THIRD_PARTY_JSC != 1
51+
return jsrt_create_jsc_factory();
52+
#endif
53+
}
54+
4155
- (void)customizeRootView:(RCTRootView *)rootView
4256
{
4357
// Override point for customization after application launch.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <UIKit/UIKit.h>
9+
#import <react/runtime/JSRuntimeFactoryCAPI.h>
10+
11+
#pragma once
12+
13+
NS_ASSUME_NONNULL_BEGIN
14+
15+
@protocol RCTJSRuntimeConfiguratorProtocol
16+
17+
- (JSRuntimeFactoryRef)createJSRuntimeFactory;
18+
19+
@end
20+
21+
NS_ASSUME_NONNULL_END

packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import <UIKit/UIKit.h>
1111
#import "RCTArchConfiguratorProtocol.h"
1212
#import "RCTDependencyProvider.h"
13+
#import "RCTJSRuntimeConfiguratorProtocol.h"
1314
#import "RCTRootViewFactory.h"
1415
#import "RCTUIConfiguratorProtocol.h"
1516

@@ -34,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
3435
RCTTurboModuleManagerDelegate,
3536
RCTComponentViewFactoryComponentProvider,
3637
#endif
38+
RCTJSRuntimeConfiguratorProtocol,
3739
RCTArchConfiguratorProtocol>
3840

3941
/// Return the bundle URL for the main bundle.

packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@
2525
#endif
2626
#import <React/RCTComponentViewFactory.h>
2727
#import <React/RCTComponentViewProtocol.h>
28-
#if USE_HERMES
29-
#import <ReactCommon/RCTHermesInstance.h>
30-
#elif USE_THIRD_PARTY_JSC != 1
31-
#import <ReactCommon/RCTJscInstance.h>
32-
#endif // USE_HERMES
3328
#import <react/nativemodule/defaults/DefaultTurboModules.h>
3429

3530
#import "RCTDependencyProvider.h"
@@ -39,6 +34,7 @@
3934
@interface RCTReactNativeFactory () <
4035
RCTComponentViewFactoryComponentProvider,
4136
RCTHostDelegate,
37+
RCTJSRuntimeConfiguratorProtocol,
4238
RCTTurboModuleManagerDelegate>
4339
@end
4440

@@ -114,6 +110,13 @@ - (NSURL *_Nullable)bundleURL
114110
return _delegate.bundleURL;
115111
}
116112

113+
#pragma mark - RCTJSRuntimeConfiguratorProtocol
114+
115+
- (JSRuntimeFactoryRef)createJSRuntimeFactory
116+
{
117+
return [_delegate createJSRuntimeFactory];
118+
}
119+
117120
#pragma mark - RCTArchConfiguratorProtocol
118121

119122
- (BOOL)newArchEnabled
@@ -269,6 +272,8 @@ - (RCTRootViewFactory *)createRCTRootViewFactory
269272
};
270273
}
271274

275+
configuration.jsRuntimeConfiguratorDelegate = self;
276+
272277
return [[RCTRootViewFactory alloc] initWithTurboModuleDelegate:self hostDelegate:self configuration:configuration];
273278
}
274279

packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#import <React/RCTBridge.h>
99
#import <React/RCTRootView.h>
1010
#import <React/RCTUtils.h>
11+
#import "RCTJSRuntimeConfiguratorProtocol.h"
1112

1213
@protocol RCTCxxBridgeDelegate;
1314
@protocol RCTComponentViewFactoryComponentProvider;
@@ -110,6 +111,8 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc
110111
*/
111112
@property (nonatomic, nullable) RCTCustomizeRootViewBlock customizeRootView;
112113

114+
@property (nonatomic, weak, nullable) id<RCTJSRuntimeConfiguratorProtocol> jsRuntimeConfiguratorDelegate;
115+
113116
#pragma mark - RCTBridgeDelegate blocks
114117

115118
/**

packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@
2626
#import <React/RCTFabricSurface.h>
2727
#import <React/RCTSurfaceHostingProxyRootView.h>
2828
#import <React/RCTSurfacePresenter.h>
29-
#if USE_HERMES
30-
#import <ReactCommon/RCTHermesInstance.h>
31-
#elif USE_THIRD_PARTY_JSC != 1
32-
#import <ReactCommon/RCTJscInstance.h>
33-
#else
34-
#endif // USE_HERMES
3529
#import <ReactCommon/RCTHost+Internal.h>
3630
#import <ReactCommon/RCTHost.h>
3731
#import <ReactCommon/RCTTurboModuleManager.h>
@@ -264,13 +258,16 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions
264258

265259
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
266260
{
267-
#if USE_HERMES
268-
return std::make_shared<facebook::react::RCTHermesInstance>(nullptr, /* allocInOldGenBeforeTTI */ false);
269-
#elif USE_THIRD_PARTY_JSC != 1
270-
return std::make_shared<facebook::react::RCTJscInstance>();
271-
#else
272-
throw std::runtime_error("No JSRuntimeFactory specified.");
273-
#endif
261+
if (_configuration.jsRuntimeConfiguratorDelegate == nil) {
262+
[NSException raise:@"RCTReactNativeFactoryDelegate::createJSRuntimeFactory not implemented"
263+
format:@"Delegate must implement a valid createJSRuntimeFactory method"];
264+
return nullptr;
265+
}
266+
267+
auto jsRuntimeFactory = [_configuration.jsRuntimeConfiguratorDelegate createJSRuntimeFactory];
268+
269+
return std::shared_ptr<facebook::react::JSRuntimeFactory>(
270+
reinterpret_cast<facebook::react::JSRuntimeFactory *>(jsRuntimeFactory), &js_runtime_factory_destroy);
274271
}
275272

276273
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge

packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Pod::Spec.new do |s|
9090
add_dependency(s, "React-rendererdebug")
9191
add_dependency(s, "React-featureflags")
9292
add_dependency(s, "React-jsitooling", :framework_name => "JSITooling")
93+
add_dependency(s, "React-RCTRuntime", :framework_name => "RCTRuntime")
9394

9495
depend_on_js_engine(s)
9596
end

packages/react-native/React-Core.podspec

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,16 @@ Pod::Spec.new do |s|
9393
"React/FBReactNativeSpec/**/*",
9494
"React/Tests/**/*",
9595
"React/Inspector/**/*",
96+
"React/Runtime/**/*",
9697
]
9798
# If we are using Hermes (the default is use hermes, so USE_HERMES can be nil), we don't have jsc installed
9899
# So we have to exclude the JSCExecutorFactory
99-
if use_hermes || ENV['USE_THIRD_PARTY_JSC'] == '1'
100+
if use_hermes
101+
exclude_files = exclude_files.append("React/CxxBridge/JSCExecutorFactory.{h,mm}")
102+
elsif ENV['USE_THIRD_PARTY_JSC'] == '1'
100103
exclude_files = exclude_files.append("React/CxxBridge/JSCExecutorFactory.{h,mm}")
101104
end
105+
102106
ss.exclude_files = exclude_files
103107
ss.private_header_files = "React/Cxx*/*.h"
104108
end
@@ -141,6 +145,7 @@ Pod::Spec.new do |s|
141145
s.resource_bundles = {'React-Core_privacy' => 'React/Resources/PrivacyInfo.xcprivacy'}
142146

143147
add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern')
148+
add_dependency(s, "React-jsitooling", :framework_name => "JSITooling")
144149
add_dependency(s, "RCTDeprecation")
145150

146151
depend_on_js_engine(s)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <react/runtime/JSRuntimeFactoryCAPI.h>
9+
10+
#pragma once
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
JSRuntimeFactoryRef jsrt_create_hermes_factory(void);
17+
18+
#ifdef __cplusplus
19+
}
20+
#endif
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTHermesInstanceFactory.h"
9+
#import <ReactCommon/RCTHermesInstance.h>
10+
11+
using namespace facebook::react;
12+
13+
extern "C" {
14+
15+
JSRuntimeFactoryRef jsrt_create_hermes_factory(void)
16+
{
17+
return reinterpret_cast<JSRuntimeFactoryRef>(new RCTHermesInstance(nullptr, /* allocInOldGenBeforeTTI */ false));
18+
}
19+
20+
} // extern "C"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <react/runtime/JSRuntimeFactoryCAPI.h>
9+
10+
#pragma once
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
JSRuntimeFactoryRef jsrt_create_jsc_factory(void);
17+
18+
#ifdef __cplusplus
19+
}
20+
#endif
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTJscInstanceFactory.h"
9+
#import <ReactCommon/RCTJscInstance.h>
10+
11+
using namespace facebook::react;
12+
13+
extern "C" {
14+
15+
JSRuntimeFactoryRef jsrt_create_jsc_factory(void)
16+
{
17+
return reinterpret_cast<JSRuntimeFactoryRef>(new RCTJscInstance());
18+
}
19+
20+
} // extern "C"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
require "json"
7+
8+
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
9+
version = package['version']
10+
11+
source = { :git => 'https://github.com/facebook/react-native.git' }
12+
if version == '1000.0.0'
13+
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
14+
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
15+
else
16+
source[:tag] = "v#{version}"
17+
end
18+
19+
folly_config = get_folly_config()
20+
folly_compiler_flags = folly_config[:compiler_flags]
21+
folly_version = folly_config[:version]
22+
boost_config = get_boost_config()
23+
boost_compiler_flags = boost_config[:compiler_flags]
24+
new_arch_flags = ENV['RCT_NEW_ARCH_ENABLED'] == '1' ? ' -DRCT_NEW_ARCH_ENABLED=1' : ''
25+
26+
header_search_paths = [
27+
"\"$(PODS_ROOT)/boost\"",
28+
"\"$(PODS_ROOT)/RCT-Folly\"",
29+
"\"$(PODS_ROOT)/DoubleConversion\"",
30+
"\"$(PODS_ROOT)/fast_float/include\"",
31+
"\"$(PODS_ROOT)/fmt/include\""
32+
]
33+
34+
module_name = "RCTRuntime"
35+
header_dir = "React"
36+
37+
Pod::Spec.new do |s|
38+
s.name = "React-RCTRuntime"
39+
s.version = version
40+
s.summary = "RCTRuntime for React Native."
41+
s.homepage = "https://reactnative.dev/"
42+
s.license = package["license"]
43+
s.author = "Meta Platforms, Inc. and its affiliates"
44+
s.platforms = min_supported_versions
45+
s.source = source
46+
s.source_files = "*.{h,mm}"
47+
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags + new_arch_flags
48+
s.header_dir = header_dir
49+
s.module_name = module_name
50+
51+
if ENV['USE_FRAMEWORKS']
52+
s.header_mappings_dir = "./"
53+
end
54+
55+
s.pod_target_xcconfig = {
56+
"HEADER_SEARCH_PATHS" => header_search_paths,
57+
"OTHER_CFLAGS" => "$(inherited) " + folly_compiler_flags + new_arch_flags,
58+
"DEFINES_MODULE" => "YES",
59+
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard()
60+
}.merge!(ENV['USE_FRAMEWORKS'] != nil ? {
61+
"PUBLIC_HEADERS_FOLDER_PATH" => "#{module_name}.framework/Headers/#{header_dir}"
62+
}: {})
63+
64+
s.dependency "React-Core"
65+
s.dependency "RCT-Folly/Fabric", folly_version
66+
s.dependency "glog"
67+
s.dependency "React-jsi"
68+
add_dependency(s, "React-jsitooling", :framework_name => "JSITooling")
69+
add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern')
70+
71+
add_dependency(s, "React-RuntimeCore")
72+
add_dependency(s, "React-RuntimeApple")
73+
74+
if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1"
75+
s.dependency "hermes-engine"
76+
add_dependency(s, "React-RuntimeHermes")
77+
s.exclude_files = "RCTJscInstanceFactory.{h,mm}"
78+
elsif ENV['USE_THIRD_PARTY_JSC'] == '1'
79+
s.exclude_files = ["RCTHermesInstanceFactory.{mm,h}", "RCTJscInstanceFactory.{mm,h}"]
80+
else
81+
s.exclude_files = ["RCTHermesInstanceFactory.{mm,h}"]
82+
end
83+
depend_on_js_engine(s)
84+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "JSRuntimeFactoryCAPI.h"
9+
#include "JSRuntimeFactory.h"
10+
11+
void js_runtime_factory_destroy(JSRuntimeFactoryRef factory) {
12+
if (factory) {
13+
delete static_cast<facebook::react::JSRuntimeFactory*>(factory);
14+
}
15+
}

0 commit comments

Comments
 (0)