expo-modules-jsi 56.0.2 → 56.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/apple/ExpoModulesJSI.podspec +0 -1
- package/apple/Package.swift +3 -0
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +34 -7
- package/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h +42 -31
- package/apple/scripts/build-xcframework.sh +17 -7
- package/apple/scripts/generate-modulemap.sh +4 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.3 — 2026-05-11
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Fixed launch-time crash in apps with source-built React Native. ([#45636](https://github.com/expo/expo/pull/45636) by [@tsapeta](https://github.com/tsapeta))
|
|
18
|
+
|
|
13
19
|
## 56.0.2 — 2026-05-08
|
|
14
20
|
|
|
15
21
|
### 🐛 Bug fixes
|
|
@@ -51,7 +51,6 @@ Pod::Spec.new do |s|
|
|
|
51
51
|
|
|
52
52
|
s.dependency 'React-Core'
|
|
53
53
|
s.dependency 'ReactCommon'
|
|
54
|
-
s.dependency 'React-runtimescheduler'
|
|
55
54
|
|
|
56
55
|
# Create a stub xcframework if needed, so CocoaPods generates the
|
|
57
56
|
# "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks" build phases.
|
package/apple/Package.swift
CHANGED
|
@@ -14,8 +14,11 @@ let podsRoot = resolvePodsRoot()
|
|
|
14
14
|
// framework and its headers don't get mirrored to Pods/Headers/Public. Clang
|
|
15
15
|
// ignores missing `-I` paths, so they're no-ops elsewhere. `RN_ROOT` is
|
|
16
16
|
// forwarded from build-xcframework.sh (Node-resolved for hoisted monorepos).
|
|
17
|
+
// `REACT_NATIVE_PATH` is exported by Xcode for hosts that build RN from a
|
|
18
|
+
// non-npm location, e.g. Expo Go.
|
|
17
19
|
let publicHeaders = "\(podsRoot)/Headers/Public"
|
|
18
20
|
let reactNative = ProcessInfo.processInfo.environment["RN_ROOT"]
|
|
21
|
+
?? ProcessInfo.processInfo.environment["REACT_NATIVE_PATH"]
|
|
19
22
|
?? "\(podsRoot)/../../node_modules/react-native"
|
|
20
23
|
let headerSearchPaths = [
|
|
21
24
|
publicHeaders,
|
|
@@ -38,28 +38,55 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
|
|
|
38
38
|
lazy var runtimeActor: JavaScriptRuntimeActor = JavaScriptRuntimeActor(runtime: self)
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
Creates a runtime from the JSI runtime.
|
|
41
|
+
Creates a runtime from the JSI runtime. The scheduler runs tasks synchronously
|
|
42
|
+
on the caller's thread — for the React-backed runtime, use
|
|
43
|
+
`init(unsafePointer:nativeScheduler:dispatch:)` instead.
|
|
42
44
|
*/
|
|
43
45
|
internal init(_ runtime: facebook.jsi.Runtime) {
|
|
44
46
|
self.pointee = runtime
|
|
45
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
47
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
Creates Hermes runtime.
|
|
51
|
+
Creates a standalone Hermes runtime. Scheduled tasks run synchronously —
|
|
52
|
+
no React scheduler is wired up.
|
|
50
53
|
*/
|
|
51
54
|
public init() {
|
|
52
55
|
self.pointee = expo.createHermesRuntime()
|
|
53
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
56
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
57
60
|
Creates a runtime from a raw pointer to the underlying `facebook.jsi.Runtime`.
|
|
61
|
+
Scheduled tasks run synchronously — for the React-backed runtime, use
|
|
62
|
+
`init(unsafePointer:nativeScheduler:dispatch:)` instead.
|
|
58
63
|
*/
|
|
59
64
|
public init(unsafePointer: UnsafeMutableRawPointer) {
|
|
60
65
|
let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
|
|
61
66
|
self.pointee = runtime
|
|
62
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
67
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
Creates a runtime bound to a host-provided React `RuntimeScheduler`. Calls to
|
|
72
|
+
`schedule(...)` / `.execute(...)` dispatch through `dispatch`, which the host
|
|
73
|
+
implements against the real `react::RuntimeScheduler`. This is the path the
|
|
74
|
+
React Native factory uses.
|
|
75
|
+
|
|
76
|
+
- `unsafePointer`: raw pointer to the underlying `facebook::jsi::Runtime`.
|
|
77
|
+
- `scheduler`: raw pointer to the `react::RuntimeScheduler` instance.
|
|
78
|
+
- `dispatch`: raw pointer to a C function with signature
|
|
79
|
+
`void (*)(void *scheduler, int priority, void (^callback)())`.
|
|
80
|
+
*/
|
|
81
|
+
public init(
|
|
82
|
+
unsafePointer: UnsafeMutableRawPointer,
|
|
83
|
+
scheduler: UnsafeMutableRawPointer,
|
|
84
|
+
dispatch: UnsafeRawPointer
|
|
85
|
+
) {
|
|
86
|
+
let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
|
|
87
|
+
let fn = unsafeBitCast(dispatch, to: expo.RuntimeScheduler.ScheduleFn.self)
|
|
88
|
+
self.pointee = runtime
|
|
89
|
+
self.scheduler = expo.RuntimeScheduler(scheduler, fn)
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
/**
|
|
@@ -348,8 +375,8 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
|
|
|
348
375
|
Schedules a closure to be executed with granted synchronized access to the runtime.
|
|
349
376
|
*/
|
|
350
377
|
public func schedule(priority: SchedulerPriority = .normal, @_implicitSelfCapture _ closure: @escaping @JavaScriptActor () -> sending Void) -> Void {
|
|
351
|
-
let
|
|
352
|
-
scheduler.scheduleTask(
|
|
378
|
+
let cxxPriority = expo.RuntimeScheduler.Priority(rawValue: priority.rawValue) ?? .NormalPriority
|
|
379
|
+
scheduler.scheduleTask(cxxPriority) {
|
|
353
380
|
JavaScriptActor.assumeIsolated(closure)
|
|
354
381
|
}
|
|
355
382
|
}
|
|
@@ -2,65 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
#ifdef __cplusplus
|
|
4
4
|
|
|
5
|
-
#include <
|
|
5
|
+
#include <atomic>
|
|
6
6
|
#include <swift/bridging>
|
|
7
|
-
#include <jsi/jsi.h>
|
|
8
|
-
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
|
9
|
-
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
|
|
10
|
-
|
|
11
|
-
namespace jsi = facebook::jsi;
|
|
12
|
-
namespace react = facebook::react;
|
|
13
7
|
|
|
14
8
|
namespace expo {
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
Wrapper
|
|
18
|
-
|
|
11
|
+
Wrapper around React Native's RuntimeScheduler. The native scheduler reference
|
|
12
|
+
and dispatch trampoline are supplied by the host (e.g. ExpoReactNativeFactory)
|
|
13
|
+
at construction time. The xcframework intentionally avoids linking against
|
|
14
|
+
React-runtimescheduler so the prebuilt binary works with hosts that build RN
|
|
15
|
+
either as a dynamic framework or as a static archive — without needing
|
|
16
|
+
-undefined dynamic_lookup to resolve React internals.
|
|
17
|
+
|
|
18
|
+
Priority values mirror facebook::react::SchedulerPriority — kept here as a
|
|
19
|
+
plain enum so we don't include the React header.
|
|
19
20
|
*/
|
|
20
21
|
class RuntimeScheduler {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
public:
|
|
23
|
+
enum class Priority : int {
|
|
24
|
+
ImmediatePriority = 1,
|
|
25
|
+
UserBlockingPriority = 2,
|
|
26
|
+
NormalPriority = 3,
|
|
27
|
+
LowPriority = 4,
|
|
28
|
+
IdlePriority = 5,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
using ScheduleTaskCallback = void(^)();
|
|
23
32
|
|
|
24
33
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
Trampoline implemented by the host — casts `nativeScheduler` back to
|
|
35
|
+
react::RuntimeScheduler* and calls scheduleTask on it. Keeping it as a
|
|
36
|
+
function pointer keeps React types out of this header.
|
|
27
37
|
*/
|
|
38
|
+
using ScheduleFn = void (*)(void *nativeScheduler, int priority, ScheduleTaskCallback callback);
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
void *const nativeScheduler{nullptr};
|
|
42
|
+
const ScheduleFn scheduleFn{nullptr};
|
|
43
|
+
|
|
28
44
|
std::atomic<int> refCount{1};
|
|
29
45
|
|
|
30
46
|
public:
|
|
31
47
|
/**
|
|
32
|
-
Constructs
|
|
33
|
-
|
|
48
|
+
Constructs a scheduler bound to a host-provided native RuntimeScheduler.
|
|
49
|
+
`scheduleTask` dispatches through `fn`, which the host implements against
|
|
50
|
+
the real react::RuntimeScheduler.
|
|
34
51
|
*/
|
|
35
|
-
RuntimeScheduler(
|
|
36
|
-
|
|
37
|
-
reactRuntimeScheduler = binding->getRuntimeScheduler();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
52
|
+
RuntimeScheduler(void *scheduler, ScheduleFn fn) noexcept
|
|
53
|
+
: nativeScheduler(scheduler), scheduleFn(fn) {}
|
|
40
54
|
|
|
41
55
|
/**
|
|
42
|
-
Constructs a no-op scheduler
|
|
43
|
-
|
|
56
|
+
Constructs a no-op scheduler. Scheduled tasks run synchronously on the
|
|
57
|
+
caller's thread — intended for standalone runtimes (e.g. tests) that have
|
|
58
|
+
no React scheduler.
|
|
44
59
|
*/
|
|
45
60
|
RuntimeScheduler() {}
|
|
46
61
|
|
|
47
|
-
RuntimeScheduler(const RuntimeScheduler &) = delete;
|
|
62
|
+
RuntimeScheduler(const RuntimeScheduler &) = delete;
|
|
48
63
|
|
|
49
64
|
/**
|
|
50
65
|
Whether the scheduler can dispatch work asynchronously to the JS thread.
|
|
51
66
|
When false, tasks run synchronously and callers should avoid dispatching to background queues.
|
|
52
67
|
*/
|
|
53
68
|
bool supportsAsyncScheduling() const noexcept {
|
|
54
|
-
return
|
|
69
|
+
return scheduleFn != nullptr;
|
|
55
70
|
}
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (reactRuntimeScheduler) {
|
|
61
|
-
reactRuntimeScheduler->scheduleTask(priority, [callback = std::move(callback)](jsi::Runtime &runtime) {
|
|
62
|
-
callback();
|
|
63
|
-
});
|
|
72
|
+
void scheduleTask(Priority priority, ScheduleTaskCallback callback) noexcept {
|
|
73
|
+
if (scheduleFn != nullptr) {
|
|
74
|
+
scheduleFn(nativeScheduler, static_cast<int>(priority), callback);
|
|
64
75
|
} else {
|
|
65
76
|
callback();
|
|
66
77
|
}
|
|
@@ -89,8 +89,10 @@ compute_hash() {
|
|
|
89
89
|
# Force C locale so sort order is consistent regardless of the environment.
|
|
90
90
|
# Xcode build phases run without locale variables, which changes sort ordering.
|
|
91
91
|
(
|
|
92
|
-
# Include PODS_ROOT so switching between worktrees
|
|
92
|
+
# Include PODS_ROOT and RN_ROOT so switching between worktrees or RN
|
|
93
|
+
# sources invalidates the cache.
|
|
93
94
|
echo "PODS_ROOT=${PODS_ROOT:-}"
|
|
95
|
+
echo "RN_ROOT=${RN_ROOT:-}"
|
|
94
96
|
echo "$all_files" | LC_ALL=C sort | while IFS= read -r file; do
|
|
95
97
|
echo "$file"
|
|
96
98
|
cat "$file"
|
|
@@ -243,12 +245,20 @@ fi
|
|
|
243
245
|
# whether PODS_ROOT was passed as relative or absolute.
|
|
244
246
|
PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
|
|
245
247
|
|
|
246
|
-
# Resolve react-native
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
# Resolve react-native. Order:
|
|
249
|
+
# 1. REACT_NATIVE_PATH env var (set by Xcode from the Podfile's build setting)
|
|
250
|
+
# — for hosts that build RN from a non-npm location, e.g. Expo Go which
|
|
251
|
+
# uses the `react-native-lab/react-native` submodule, not node_modules.
|
|
252
|
+
# 2. `node -p require.resolve(...)` so the script works in any node_modules
|
|
253
|
+
# layout (hoisted monorepos, pnpm/yarn workspaces).
|
|
254
|
+
# 3. Relative fallback from PODS_ROOT for when `node` isn't on PATH.
|
|
255
|
+
# Forwarded to Package.swift and the modulemap generator below.
|
|
256
|
+
if [[ -n "${REACT_NATIVE_PATH:-}" && -d "${REACT_NATIVE_PATH}" ]]; then
|
|
257
|
+
RN_ROOT="$(cd "$REACT_NATIVE_PATH" && pwd)"
|
|
258
|
+
else
|
|
259
|
+
RN_ROOT="$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null \
|
|
260
|
+
|| echo "${PODS_ROOT}/../../node_modules/react-native")"
|
|
261
|
+
fi
|
|
252
262
|
|
|
253
263
|
mode="$( [[ -d "${PODS_ROOT}/React-Core-prebuilt/React.xcframework" ]] && echo "prebuilt RN" || echo "source-built RN")"
|
|
254
264
|
[[ -f "${PODS_ROOT}/Target Support Files/React-jsi/React-jsi-umbrella.h" ]] && mode="${mode}, static frameworks"
|
|
@@ -38,7 +38,10 @@ mkdir -p "$GENERATED_DIR"
|
|
|
38
38
|
|
|
39
39
|
JSI_UMBRELLA="${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi.h"
|
|
40
40
|
if [[ ! -f "$JSI_UMBRELLA" ]]; then
|
|
41
|
-
|
|
41
|
+
# Resolution order matches build-xcframework.sh: RN_ROOT (forwarded by the
|
|
42
|
+
# build script), REACT_NATIVE_PATH (exported by Xcode for hosts like Expo Go
|
|
43
|
+
# that build RN from a submodule), node resolve, then a relative fallback.
|
|
44
|
+
RN="${RN_ROOT:-${REACT_NATIVE_PATH:-$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null || echo "${PODS_ROOT}/../../node_modules/react-native")}}"
|
|
42
45
|
JSI_UMBRELLA="${RN}/ReactCommon/jsi/jsi/jsi.h"
|
|
43
46
|
fi
|
|
44
47
|
[[ -f "$JSI_UMBRELLA" ]] || { echo "error: cannot locate jsi.h" >&2; exit 1; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-jsi",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.3",
|
|
4
4
|
"description": "The JavaScript Interface for Expo Modules",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"sideEffects": [],
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"./apple/scripts/test.sh"
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "42013232893cb2aa71ab218e9b422d4a8476b3f0",
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "apple/scripts/build-xcframework.sh",
|
|
47
47
|
"test": "apple/scripts/test.sh"
|