expo-modules-jsi 56.0.3 → 56.0.4

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 CHANGED
@@ -10,6 +10,13 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 56.0.4 — 2026-05-13
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fixed xcframework build cache not invalidating when React-jsi headers change. ([#45735](https://github.com/expo/expo/pull/45735) by [@tsapeta](https://github.com/tsapeta))
18
+ - [iOS] Added support for `facebook::jsi::IRuntime` so the package builds against React Native 0.86+, while staying compatible with 0.85 and react-native-tvos. ([#45728](https://github.com/expo/expo/pull/45728) by [@zoontek](https://github.com/zoontek))
19
+
13
20
  ## 56.0.3 — 2026-05-11
14
21
 
15
22
  ### 🐛 Bug fixes
@@ -14,3 +14,11 @@ Namespaces:
14
14
  SwiftImportAs: reference
15
15
  SwiftReleaseOp: immortal
16
16
  SwiftRetainOp: immortal
17
+ - Name: IRuntime
18
+ SwiftImportAs: reference
19
+ SwiftReleaseOp: immortal
20
+ SwiftRetainOp: immortal
21
+ - Name: ICast
22
+ SwiftImportAs: reference
23
+ SwiftReleaseOp: immortal
24
+ SwiftRetainOp: immortal
@@ -7,21 +7,21 @@ internal import ExpoModulesJSI_Cxx
7
7
  */
8
8
  internal protocol JSIRepresentable: JavaScriptRepresentable, Sendable, ~Copyable {
9
9
  /**
10
- Creates an instance of this type from the given `facebook.jsi.Value` in `facebook.jsi.Runtime`.
10
+ Creates an instance of this type from the given `facebook.jsi.Value` in `facebook.jsi.IRuntime`.
11
11
  */
12
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Self
12
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Self
13
13
  /**
14
14
  Creates a JSI value representing this value in the given JSI runtime.
15
15
  */
16
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value
16
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value
17
17
  }
18
18
 
19
19
  internal extension JSIRepresentable {
20
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Self {
20
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Self {
21
21
  FatalError.unimplemented()
22
22
  }
23
23
 
24
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
24
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
25
25
  FatalError.unimplemented()
26
26
  }
27
27
  }
@@ -29,11 +29,11 @@ internal extension JSIRepresentable {
29
29
  // MARK: - Implementations
30
30
 
31
31
  extension Bool: JSIRepresentable {
32
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Bool {
32
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Bool {
33
33
  return value.getBool()
34
34
  }
35
35
 
36
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
36
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
37
37
  return facebook.jsi.Value(self)
38
38
  }
39
39
  }
@@ -41,19 +41,19 @@ extension Bool: JSIRepresentable {
41
41
  internal protocol JSIRepresentableNumber: JSIRepresentable {}
42
42
 
43
43
  extension JSIRepresentableNumber {
44
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Int where Self: FixedWidthInteger {
44
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Int where Self: FixedWidthInteger {
45
45
  return Int(value.getNumber())
46
46
  }
47
47
 
48
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Double where Self: BinaryFloatingPoint {
48
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Double where Self: BinaryFloatingPoint {
49
49
  return value.getNumber()
50
50
  }
51
51
 
52
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value where Self: FixedWidthInteger {
52
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value where Self: FixedWidthInteger {
53
53
  return facebook.jsi.Value(Double(self))
54
54
  }
55
55
 
56
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value where Self: BinaryFloatingPoint {
56
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value where Self: BinaryFloatingPoint {
57
57
  return facebook.jsi.Value(Double(self))
58
58
  }
59
59
  }
@@ -74,30 +74,30 @@ extension Float64: JSIRepresentableNumber {}
74
74
  extension CGFloat: JSIRepresentableNumber {}
75
75
 
76
76
  extension String: JSIRepresentable {
77
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> String {
77
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> String {
78
78
  return String(value.getString(runtime).utf8(runtime))
79
79
  }
80
80
 
81
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
81
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
82
82
  return facebook.jsi.Value(runtime, facebook.jsi.String.createFromUtf8(runtime, std.string(self)))
83
83
  }
84
84
  }
85
85
 
86
86
  extension Optional: JSIRepresentable where Wrapped: JSIRepresentable {
87
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Self {
87
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Self {
88
88
  if value.isNull() || value.isUndefined() {
89
89
  return nil
90
90
  }
91
91
  return Wrapped.fromJSIValue(value, in: runtime)
92
92
  }
93
93
 
94
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
94
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
95
95
  return self?.toJSIValue(in: runtime) ?? .null()
96
96
  }
97
97
  }
98
98
 
99
99
  extension Array: JSIRepresentable where Element: JSIRepresentable {
100
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Array<Element> {
100
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Array<Element> {
101
101
  let jsiArray = value.getObject(runtime).getArray(runtime)
102
102
  let size = jsiArray.size(runtime)
103
103
  var result: Self = []
@@ -110,7 +110,7 @@ extension Array: JSIRepresentable where Element: JSIRepresentable {
110
110
  return result
111
111
  }
112
112
 
113
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
113
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
114
114
  let jsiArray = facebook.jsi.Array(runtime, count)
115
115
 
116
116
  for index in 0..<count {
@@ -121,7 +121,7 @@ extension Array: JSIRepresentable where Element: JSIRepresentable {
121
121
  }
122
122
 
123
123
  extension Dictionary: JSIRepresentable where Key == String, Value: JSIRepresentable {
124
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> Dictionary<Key, Value> {
124
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> Dictionary<Key, Value> {
125
125
  let object = value.getObject(runtime)
126
126
  let propertyNames = object.getPropertyNames(runtime)
127
127
  let size = propertyNames.size(runtime)
@@ -137,7 +137,7 @@ extension Dictionary: JSIRepresentable where Key == String, Value: JSIRepresenta
137
137
  return result
138
138
  }
139
139
 
140
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
140
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
141
141
  let object = facebook.jsi.Object(runtime)
142
142
 
143
143
  for (key, value) in self {
@@ -91,11 +91,11 @@ public final class JavaScriptRef<T: JavaScriptType & ~Copyable>: JavaScriptType,
91
91
 
92
92
  extension JavaScriptRef: JavaScriptRepresentable where T: JavaScriptRepresentable & ~Copyable {}
93
93
  extension JavaScriptRef: JSIRepresentable where T: JSIRepresentable & ~Copyable {
94
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptRef {
94
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptRef {
95
95
  FatalError.unimplemented()
96
96
  }
97
97
 
98
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
98
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
99
99
  return take()?.toJSIValue(in: runtime) ?? .undefined()
100
100
  }
101
101
  }
@@ -22,14 +22,25 @@ internal import ExpoModulesJSI_Cxx
22
22
  */
23
23
  open class JavaScriptRuntime: Equatable, @unchecked Sendable {
24
24
  /**
25
- The underlying JSI runtime this `JavaScriptRuntime` points to.
26
- Note that `facebook.jsi.Runtime` is annotated with `SWIFT_UNSAFE_REFERENCE` in our copy of `jsi.h` header,
27
- so for the Swift compiler it is treated as a reference type (like `class` and not `struct`).
28
- This is important because the `facebook.jsi.Runtime`:
29
- - is an abstract class with many virtual methods. Swift/C++ interop does not support calling pure virtual methods on value types.
30
- - is non-copyable. As a value type, we would have to "borrow" it from React Native in an unsafe manner.
31
- */
32
- internal let pointee: facebook.jsi.Runtime
25
+ The underlying JSI runtime this `JavaScriptRuntime` points to, exposed as
26
+ `IRuntime` the abstract base interface that virtually all JSI value/object/
27
+ function methods take (`Value::getString`, `Object::setProperty`,
28
+ `Function::call`, …) since RN 0.86 split the API. Stored as the upcast result
29
+ of `runtimePointee` because Swift's C++ interop does not auto-upcast between
30
+ two `SwiftImportAs: reference` types.
31
+
32
+ Use ``runtimePointee`` instead when you specifically need a `jsi::Runtime&`
33
+ (e.g. constructing an `expo.RuntimeScheduler` whose binding lookup is typed on
34
+ `Runtime&` upstream).
35
+
36
+ Note that `facebook.jsi.IRuntime` and `facebook.jsi.Runtime` are imported as
37
+ reference types so for the Swift compiler they are treated like classes.
38
+ This is important because they:
39
+ - are abstract classes with many virtual methods. Swift/C++ interop does not support calling pure virtual methods on value types.
40
+ - are non-copyable. As value types, we would have to "borrow" them from React Native in an unsafe manner.
41
+ */
42
+ internal let pointee: facebook.jsi.IRuntime
43
+ internal let runtimePointee: facebook.jsi.Runtime
33
44
  internal let scheduler: expo.RuntimeScheduler
34
45
 
35
46
  /**
@@ -43,7 +54,8 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
43
54
  `init(unsafePointer:nativeScheduler:dispatch:)` instead.
44
55
  */
45
56
  internal init(_ runtime: facebook.jsi.Runtime) {
46
- self.pointee = runtime
57
+ self.runtimePointee = runtime
58
+ self.pointee = expo.iruntime(runtime)
47
59
  self.scheduler = expo.RuntimeScheduler()
48
60
  }
49
61
 
@@ -52,7 +64,9 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
52
64
  no React scheduler is wired up.
53
65
  */
54
66
  public init() {
55
- self.pointee = expo.createHermesRuntime()
67
+ let runtime = expo.createHermesRuntime()
68
+ self.runtimePointee = runtime
69
+ self.pointee = expo.iruntime(runtime)
56
70
  self.scheduler = expo.RuntimeScheduler()
57
71
  }
58
72
 
@@ -63,7 +77,8 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
63
77
  */
64
78
  public init(unsafePointer: UnsafeMutableRawPointer) {
65
79
  let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
66
- self.pointee = runtime
80
+ self.runtimePointee = runtime
81
+ self.pointee = expo.iruntime(runtime)
67
82
  self.scheduler = expo.RuntimeScheduler()
68
83
  }
69
84
 
@@ -85,7 +100,8 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
85
100
  ) {
86
101
  let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
87
102
  let fn = unsafeBitCast(dispatch, to: expo.RuntimeScheduler.ScheduleFn.self)
88
- self.pointee = runtime
103
+ self.runtimePointee = runtime
104
+ self.pointee = expo.iruntime(runtime)
89
105
  self.scheduler = expo.RuntimeScheduler(scheduler, fn)
90
106
  }
91
107
 
@@ -94,7 +110,7 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
94
110
  The pointer is valid only for the duration of the closure and must not be stored or escaped.
95
111
  */
96
112
  public func withUnsafePointee<R>(_ body: (UnsafeMutableRawPointer) throws -> R) rethrows -> R {
97
- return try body(Unmanaged<facebook.jsi.Runtime>.passUnretained(pointee).toOpaque())
113
+ return try body(Unmanaged<facebook.jsi.Runtime>.passUnretained(runtimePointee).toOpaque())
98
114
  }
99
115
 
100
116
  /**
@@ -308,11 +308,11 @@ extension JavaScriptBigInt: JavaScriptRepresentable {
308
308
  }
309
309
 
310
310
  extension JavaScriptBigInt: JSIRepresentable {
311
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptBigInt {
311
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptBigInt {
312
312
  fatalError("Not implemented: Use JavaScriptValue.getBigInt() instead")
313
313
  }
314
314
 
315
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
315
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
316
316
  return asJSIValue()
317
317
  }
318
318
  }
@@ -67,11 +67,11 @@ extension JavaScriptError: JavaScriptRepresentable {
67
67
  }
68
68
 
69
69
  extension JavaScriptError: JSIRepresentable {
70
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptError {
70
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptError {
71
71
  FatalError.unimplemented()
72
72
  }
73
73
 
74
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
74
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
75
75
  return asJSIValue()
76
76
  }
77
77
  }
@@ -130,11 +130,11 @@ extension JavaScriptFunction: JavaScriptRepresentable {
130
130
  }
131
131
 
132
132
  extension JavaScriptFunction: JSIRepresentable {
133
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptFunction {
133
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptFunction {
134
134
  FatalError.unimplemented()
135
135
  }
136
136
 
137
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
137
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
138
138
  return asJSIValue()
139
139
  }
140
140
  }
@@ -587,11 +587,11 @@ extension JavaScriptObject: JavaScriptRepresentable {
587
587
  }
588
588
 
589
589
  extension JavaScriptObject: JSIRepresentable {
590
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptObject {
590
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptObject {
591
591
  FatalError.unimplemented()
592
592
  }
593
593
 
594
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
594
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
595
595
  return asJSIValue()
596
596
  }
597
597
  }
@@ -704,11 +704,11 @@ extension JavaScriptValue: JavaScriptRepresentable {
704
704
  }
705
705
 
706
706
  extension JavaScriptValue: JSIRepresentable {
707
- static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.Runtime) -> JavaScriptValue {
707
+ static func fromJSIValue(_ value: borrowing facebook.jsi.Value, in runtime: facebook.jsi.IRuntime) -> JavaScriptValue {
708
708
  FatalError.unimplemented()
709
709
  }
710
710
 
711
- func toJSIValue(in runtime: facebook.jsi.Runtime) -> facebook.jsi.Value {
711
+ func toJSIValue(in runtime: facebook.jsi.IRuntime) -> facebook.jsi.Value {
712
712
  return facebook.jsi.Value(runtime, pointee)
713
713
  }
714
714
  }
@@ -23,7 +23,7 @@ TypedArrayKind getTypedArrayKindForName(const std::string &name) {
23
23
  return nameToKindMap.at(name);
24
24
  }
25
25
 
26
- TypedArrayKind getTypedArrayKind(jsi::Runtime &runtime, const jsi::Object &jsObj) {
26
+ TypedArrayKind getTypedArrayKind(jsi::IRuntime &runtime, const jsi::Object &jsObj) {
27
27
  auto constructorName = jsObj.getPropertyAsObject(runtime, "constructor")
28
28
  .getProperty(runtime, "name")
29
29
  .asString(runtime)
@@ -31,7 +31,7 @@ TypedArrayKind getTypedArrayKind(jsi::Runtime &runtime, const jsi::Object &jsObj
31
31
  return getTypedArrayKindForName(constructorName);
32
32
  }
33
33
 
34
- jsi::ArrayBuffer getTypedArrayBuffer(jsi::Runtime &runtime, const jsi::Object &jsObj) {
34
+ jsi::ArrayBuffer getTypedArrayBuffer(jsi::IRuntime &runtime, const jsi::Object &jsObj) {
35
35
  auto buffer = jsObj.getProperty(runtime, "buffer");
36
36
  if (buffer.isObject() && buffer.asObject(runtime).isArrayBuffer(runtime)) {
37
37
  return buffer.asObject(runtime).getArrayBuffer(runtime);
@@ -39,7 +39,7 @@ jsi::ArrayBuffer getTypedArrayBuffer(jsi::Runtime &runtime, const jsi::Object &j
39
39
  throw std::runtime_error("no ArrayBuffer attached");
40
40
  }
41
41
 
42
- bool isTypedArray(jsi::Runtime &runtime, const jsi::Object &jsObj) {
42
+ bool isTypedArray(jsi::IRuntime &runtime, const jsi::Object &jsObj) {
43
43
  jsi::Object ArrayBuffer = runtime
44
44
  .global()
45
45
  .getPropertyAsObject(runtime, "ArrayBuffer");
@@ -3,7 +3,8 @@
3
3
  #include <exception>
4
4
  #include <memory>
5
5
  #include <swift/bridging>
6
- #include <jsi/jsi.h>
6
+
7
+ #include "IRuntimeCompat.h"
7
8
 
8
9
  namespace expo {
9
10
 
@@ -62,7 +63,7 @@ public:
62
63
  Returns the underlying JavaScript value of the wrapped `jsi::JSError`.
63
64
  This is a JavaScript Error object with all its properties preserved.
64
65
  */
65
- inline jsi::Value asValue(jsi::Runtime &runtime) noexcept {
66
+ inline jsi::Value asValue(jsi::IRuntime &runtime) noexcept {
66
67
  return jsi::Value(runtime, jsError.value());
67
68
  }
68
69
 
@@ -72,7 +73,7 @@ public:
72
73
  using `getCurrent()`. The function returns `nullptr` when an exception occurs.
73
74
  */
74
75
  template <typename Result>
75
- inline static Result tryCatch(jsi::Runtime &runtime, Result(^block)(void)) {
76
+ inline static Result tryCatch(jsi::IRuntime &runtime, Result(^block)(void)) {
76
77
  try {
77
78
  return block();
78
79
  } catch (jsi::JSError e) {
@@ -116,7 +117,7 @@ public:
116
117
  Sets the current thread's error by creating a `jsi::JSError` from the given message.
117
118
  Called from Swift when a host function closure throws a plain error.
118
119
  */
119
- inline static void setCurrent(jsi::Runtime &runtime, const std::string &message) {
120
+ inline static void setCurrent(jsi::IRuntime &runtime, const std::string &message) {
120
121
  _current = std::make_unique<CppError>(jsi::JSError(runtime, message));
121
122
  }
122
123
 
@@ -4,10 +4,10 @@
4
4
 
5
5
  #include <string>
6
6
  #include <vector>
7
- #include <jsi/jsi.h>
8
7
 
9
8
  #include "CppError.h"
10
9
  #include "HostObjectCallbacks.h"
10
+ #include "IRuntimeCompat.h"
11
11
 
12
12
  namespace jsi = facebook::jsi;
13
13
 
@@ -47,7 +47,7 @@ public:
47
47
  return _callbacks.getPropertyNames();
48
48
  }
49
49
 
50
- inline static jsi::Object makeObject(jsi::Runtime &runtime, HostObjectCallbacks callbacks) {
50
+ inline static jsi::Object makeObject(jsi::IRuntime &runtime, HostObjectCallbacks callbacks) {
51
51
  return jsi::Object::createFromHostObject(runtime, std::make_shared<HostObject>(callbacks));
52
52
  }
53
53
 
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+ #ifdef __cplusplus
3
+
4
+ #include <jsi/jsi.h>
5
+
6
+ // React Native 0.86 split the JSI API into the abstract `jsi::IRuntime` base
7
+ // and the concrete `jsi::Runtime` derived class — most value/object/function
8
+ // methods now take `IRuntime&` instead of `Runtime&`. Older React Native
9
+ // versions (e.g. react-native-tvos 0.85) only have `jsi::Runtime`. Alias
10
+ // `IRuntime` to `Runtime` there so the same source compiles against both.
11
+ #if __has_include(<cxxreact/ReactNativeVersion.h>)
12
+ #include <cxxreact/ReactNativeVersion.h>
13
+ #endif
14
+
15
+ #if !defined(REACT_NATIVE_VERSION_MAJOR) || \
16
+ (REACT_NATIVE_VERSION_MAJOR == 0 && REACT_NATIVE_VERSION_MINOR < 86)
17
+ namespace facebook::jsi {
18
+ using IRuntime = Runtime;
19
+ } // namespace facebook::jsi
20
+ #endif
21
+
22
+ #endif // __cplusplus
@@ -3,10 +3,11 @@
3
3
  #pragma once
4
4
  #ifdef __cplusplus
5
5
 
6
- #include <jsi/jsi.h>
7
6
  #include <hermes/hermes.h>
7
+
8
8
  #include "HostFunctionClosure.h"
9
9
  #include "CppError.h"
10
+ #include "IRuntimeCompat.h"
10
11
  #include "MemoryBuffer.h"
11
12
  #include "NativeState.h"
12
13
  #include "TypedArray.h"
@@ -19,24 +20,35 @@ namespace jsi = facebook::jsi;
19
20
 
20
21
  namespace expo {
21
22
 
22
- inline jsi::Value valueFromFunction(jsi::Runtime &runtime, const jsi::Function &function) {
23
+ /**
24
+ Upcasts a `jsi::Runtime&` to a `jsi::IRuntime&`. RN 0.86 split the JSI API into
25
+ the abstract `IRuntime` base and concrete `Runtime` derived class — most
26
+ value/object/function methods now take `IRuntime&`. Swift's C++ interop does
27
+ not auto-upcast between two `SwiftImportAs: reference` types, so Swift can't
28
+ pass a `Runtime` reference where `IRuntime` is expected without this helper.
29
+ */
30
+ inline jsi::IRuntime &iruntime(jsi::Runtime &runtime) noexcept {
31
+ return runtime;
32
+ }
33
+
34
+ inline jsi::Value valueFromFunction(jsi::IRuntime &runtime, const jsi::Function &function) {
23
35
  return jsi::Value(runtime, function);
24
36
  }
25
37
 
26
38
  // `jsi::Object::setProperty` is a template function that Swift does not support. We need to provide specialized versions.
27
- inline void setProperty(jsi::Runtime &runtime, const jsi::Object &object, const char *name, const jsi::Value value) {
39
+ inline void setProperty(jsi::IRuntime &runtime, const jsi::Object &object, const char *name, const jsi::Value value) {
28
40
  object.setProperty(runtime, name, value);
29
41
  }
30
42
 
31
- inline void setProperty(jsi::Runtime &runtime, const jsi::Array &array, const char *name, const jsi::Value &value) {
43
+ inline void setProperty(jsi::IRuntime &runtime, const jsi::Array &array, const char *name, const jsi::Value &value) {
32
44
  array.setProperty(runtime, name, value);
33
45
  }
34
46
 
35
- inline void setValueAtIndex(jsi::Runtime &runtime, const jsi::Array &array, size_t index, const jsi::Value &value) {
47
+ inline void setValueAtIndex(jsi::IRuntime &runtime, const jsi::Array &array, size_t index, const jsi::Value &value) {
36
48
  array.setValueAtIndex(runtime, index, value);
37
49
  }
38
50
 
39
- inline void setArrayLength(jsi::Runtime &runtime, const jsi::Array &array, long length) {
51
+ inline void setArrayLength(jsi::IRuntime &runtime, const jsi::Array &array, long length) {
40
52
  auto oldLength = (int)array.size(runtime);
41
53
  auto newLength = (int)length;
42
54
 
@@ -52,15 +64,15 @@ inline void setArrayLength(jsi::Runtime &runtime, const jsi::Array &array, long
52
64
  }
53
65
  }
54
66
 
55
- inline jsi::Value getProperty(jsi::Runtime &runtime, const jsi::Array &array, const jsi::PropNameID &name) {
67
+ inline jsi::Value getProperty(jsi::IRuntime &runtime, const jsi::Array &array, const jsi::PropNameID &name) {
56
68
  return array.getProperty(runtime, name);
57
69
  }
58
70
 
59
- inline jsi::Value valueFromArray(jsi::Runtime &runtime, const jsi::Array &array) {
71
+ inline jsi::Value valueFromArray(jsi::IRuntime &runtime, const jsi::Array &array) {
60
72
  return jsi::Value(runtime, array);
61
73
  }
62
74
 
63
- inline const jsi::Value valueFromError(jsi::Runtime &runtime, const jsi::JSError &error) {
75
+ inline const jsi::Value valueFromError(jsi::IRuntime &runtime, const jsi::JSError &error) {
64
76
  return jsi::Value(runtime, error.value());
65
77
  }
66
78
 
@@ -68,7 +80,7 @@ inline std::shared_ptr<const jsi::Buffer> makeSharedStringBuffer(const std::stri
68
80
  return std::make_shared<jsi::StringBuffer>(source);
69
81
  }
70
82
 
71
- inline jsi::Function createHostFunction(jsi::Runtime &runtime, const jsi::PropNameID &propName, HostFunctionClosure *closure) {
83
+ inline jsi::Function createHostFunction(jsi::IRuntime &runtime, const jsi::PropNameID &propName, HostFunctionClosure *closure) {
72
84
  auto closurePtr = std::shared_ptr<HostFunctionClosure>(closure);
73
85
  return jsi::Function::createFromHostFunction(runtime, propName, 0, [closurePtr](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *_Nonnull args, size_t count) -> jsi::Value {
74
86
  auto result = closurePtr->call(thisValue, args, count);
@@ -82,7 +94,7 @@ inline jsi::Function createHostFunction(jsi::Runtime &runtime, const jsi::PropNa
82
94
  });
83
95
  }
84
96
 
85
- inline jsi::Function createHostFunction(jsi::Runtime &runtime, const char *name, HostFunctionClosure *closure) {
97
+ inline jsi::Function createHostFunction(jsi::IRuntime &runtime, const char *name, HostFunctionClosure *closure) {
86
98
  jsi::PropNameID propName = jsi::PropNameID::forAscii(runtime, name);
87
99
  return createHostFunction(runtime, propName, closure);
88
100
  }
@@ -93,31 +105,31 @@ inline jsi::Function createHostFunction(jsi::Runtime &runtime, const char *name,
93
105
  and by JS engines themselves. Wraps the templated `jsi::Object::isHostObject<T>` so the
94
106
  check is callable from Swift, where C++ function templates cannot be imported directly.
95
107
  */
96
- inline bool isHostObject(jsi::Runtime &runtime, const jsi::Object &object) {
108
+ inline bool isHostObject(jsi::IRuntime &runtime, const jsi::Object &object) {
97
109
  return object.isHostObject<jsi::HostObject>(runtime);
98
110
  }
99
111
 
100
112
  jsi::Runtime* createHermesRuntime();
101
113
 
102
- inline jsi::Value evaluateJavaScript(jsi::Runtime &runtime, const std::shared_ptr<const jsi::Buffer>& buffer, const std::string& sourceURL) {
114
+ inline jsi::Value evaluateJavaScript(jsi::IRuntime &runtime, const std::shared_ptr<const jsi::Buffer>& buffer, const std::string& sourceURL) {
103
115
  return expo::CppError::tryCatch(runtime, ^{
104
116
  return runtime.evaluateJavaScript(buffer, sourceURL);
105
117
  });
106
118
  }
107
119
 
108
- inline jsi::Value callFunction(jsi::Runtime &runtime, const jsi::Function &function, const jsi::Value *_Nullable args, size_t count) {
120
+ inline jsi::Value callFunction(jsi::IRuntime &runtime, const jsi::Function &function, const jsi::Value *_Nullable args, size_t count) {
109
121
  return expo::CppError::tryCatch(runtime, ^{
110
122
  return function.call(runtime, args, count);
111
123
  });
112
124
  }
113
125
 
114
- inline jsi::Value callFunctionWithThis(jsi::Runtime &runtime, const jsi::Function &function, const jsi::Object &jsThis, const jsi::Value *_Nullable args, size_t count) {
126
+ inline jsi::Value callFunctionWithThis(jsi::IRuntime &runtime, const jsi::Function &function, const jsi::Object &jsThis, const jsi::Value *_Nullable args, size_t count) {
115
127
  return expo::CppError::tryCatch(runtime, ^{
116
128
  return function.callWithThis(runtime, jsThis, args, count);
117
129
  });
118
130
  }
119
131
 
120
- inline jsi::Value callAsConstructor(jsi::Runtime &runtime, const jsi::Function &function, const jsi::Value *_Nullable args, size_t count) {
132
+ inline jsi::Value callAsConstructor(jsi::IRuntime &runtime, const jsi::Function &function, const jsi::Value *_Nullable args, size_t count) {
121
133
  return expo::CppError::tryCatch(runtime, ^{
122
134
  return function.callAsConstructor(runtime, args, count);
123
135
  });
@@ -129,28 +141,28 @@ inline jsi::Value callAsConstructor(jsi::Runtime &runtime, const jsi::Function &
129
141
  * Converts a `jsi::ArrayBuffer` to a `jsi::Value`. Needed because Swift/C++ interop
130
142
  * does not implicitly upcast `ArrayBuffer` to `Object` for the `Value` constructor.
131
143
  */
132
- inline jsi::Value valueFromArrayBuffer(jsi::Runtime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
144
+ inline jsi::Value valueFromArrayBuffer(jsi::IRuntime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
133
145
  return jsi::Value(runtime, arrayBuffer);
134
146
  }
135
147
 
136
148
  /**
137
149
  * Returns the size of the array buffer storage in bytes.
138
150
  */
139
- inline size_t arrayBufferSize(jsi::Runtime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
151
+ inline size_t arrayBufferSize(jsi::IRuntime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
140
152
  return arrayBuffer.size(runtime);
141
153
  }
142
154
 
143
155
  /**
144
156
  * Returns a pointer to the underlying data of the array buffer.
145
157
  */
146
- inline uint8_t *arrayBufferData(jsi::Runtime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
158
+ inline uint8_t *arrayBufferData(jsi::IRuntime &runtime, const jsi::ArrayBuffer &arrayBuffer) {
147
159
  return arrayBuffer.data(runtime);
148
160
  }
149
161
 
150
162
  /**
151
163
  * Creates a new array buffer of the given size with zero-initialized memory.
152
164
  */
153
- inline jsi::ArrayBuffer createArrayBuffer(jsi::Runtime &runtime, size_t size) {
165
+ inline jsi::ArrayBuffer createArrayBuffer(jsi::IRuntime &runtime, size_t size) {
154
166
  uint8_t *data = new uint8_t[size]();
155
167
  auto buffer = std::make_shared<MemoryBuffer>(data, size, [data]() { delete[] data; });
156
168
  return jsi::ArrayBuffer(runtime, std::move(buffer));
@@ -161,7 +173,7 @@ inline jsi::ArrayBuffer createArrayBuffer(jsi::Runtime &runtime, size_t size) {
161
173
  * The cleanup function is called (with the cleanup context) when the ArrayBuffer is deallocated.
162
174
  */
163
175
  inline jsi::ArrayBuffer createArrayBuffer(
164
- jsi::Runtime &runtime,
176
+ jsi::IRuntime &runtime,
165
177
  uint8_t *data,
166
178
  size_t size,
167
179
  void *_Nonnull cleanupContext,
@@ -179,20 +191,20 @@ inline jsi::ArrayBuffer createArrayBuffer(
179
191
 
180
192
  // MARK: - Native state
181
193
 
182
- inline bool hasNativeState(jsi::Runtime &runtime, const jsi::Object &object) {
194
+ inline bool hasNativeState(jsi::IRuntime &runtime, const jsi::Object &object) {
183
195
  return object.hasNativeState<expo::NativeState>(runtime);
184
196
  }
185
197
 
186
- inline void setNativeState(jsi::Runtime &runtime, const jsi::Object &object, expo::NativeState &nativeState) {
198
+ inline void setNativeState(jsi::IRuntime &runtime, const jsi::Object &object, expo::NativeState &nativeState) {
187
199
  std::shared_ptr<expo::NativeState> nativeStatePtr = std::shared_ptr<expo::NativeState>(&nativeState);
188
200
  object.setNativeState(runtime, nativeStatePtr);
189
201
  }
190
202
 
191
- inline void unsetNativeState(jsi::Runtime &runtime, const jsi::Object &object) {
203
+ inline void unsetNativeState(jsi::IRuntime &runtime, const jsi::Object &object) {
192
204
  object.setNativeState(runtime, nullptr);
193
205
  }
194
206
 
195
- inline expo::NativeState *_Nullable getNativeState(jsi::Runtime &runtime, const jsi::Object &object) {
207
+ inline expo::NativeState *_Nullable getNativeState(jsi::IRuntime &runtime, const jsi::Object &object) {
196
208
  if (!object.hasNativeState<expo::NativeState>(runtime)) {
197
209
  // JSI's implementation asserts if `hasNativeState` returns true, but we prefer to make it nullable.
198
210
  return nullptr;
@@ -4,7 +4,7 @@
4
4
 
5
5
  #pragma once
6
6
 
7
- #include <jsi/jsi.h>
7
+ #include "IRuntimeCompat.h"
8
8
 
9
9
  namespace jsi = facebook::jsi;
10
10
 
@@ -26,19 +26,19 @@ enum class TypedArrayKind {
26
26
  BigUint64Array = 11,
27
27
  };
28
28
 
29
- bool isTypedArray(jsi::Runtime &runtime, const jsi::Object &jsObj);
29
+ bool isTypedArray(jsi::IRuntime &runtime, const jsi::Object &jsObj);
30
30
 
31
31
  /**
32
32
  * Returns the `TypedArrayKind` of the given typed-array object, derived from its
33
33
  * `constructor.name` (e.g. `Uint8Array`, `Float32Array`).
34
34
  */
35
- TypedArrayKind getTypedArrayKind(jsi::Runtime &runtime, const jsi::Object &jsObj);
35
+ TypedArrayKind getTypedArrayKind(jsi::IRuntime &runtime, const jsi::Object &jsObj);
36
36
 
37
37
  /**
38
38
  * Returns the underlying `ArrayBuffer` backing the given typed-array object.
39
39
  * Throws if the object has no attached ArrayBuffer.
40
40
  */
41
- jsi::ArrayBuffer getTypedArrayBuffer(jsi::Runtime &runtime, const jsi::Object &jsObj);
41
+ jsi::ArrayBuffer getTypedArrayBuffer(jsi::IRuntime &runtime, const jsi::Object &jsObj);
42
42
 
43
43
  } // namespace expo
44
44
 
@@ -15,10 +15,11 @@
15
15
  # - Cleans .swiftinterface files for cross-compiler compatibility
16
16
  #
17
17
  # Usage:
18
- # PODS_ROOT=/path/to/Pods ./build-xcframework.sh [--clean]
18
+ # ./build-xcframework.sh [--clean]
19
19
  #
20
20
  # Environment:
21
- # PODS_ROOT (required) Path to the CocoaPods Pods directory
21
+ # PODS_ROOT Path to the CocoaPods Pods directory. Defaults to
22
+ # $EXPO_ROOT_DIR/apps/bare-expo/ios/Pods (set by direnv).
22
23
  # PLATFORM_NAME (optional) Build for a specific platform (e.g. iphoneos, iphonesimulator).
23
24
  # When unset, builds for both iphoneos and iphonesimulator.
24
25
 
@@ -36,6 +37,8 @@ BUILD_PRODUCTS_PATH="${DERIVED_DATA_PATH}/Build/Products"
36
37
 
37
38
  source "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
38
39
 
40
+ resolve_pods_root "$PACKAGE_DIR"
41
+
39
42
  CLEAN=false
40
43
 
41
44
  while [[ $# -gt 0 ]]; do
@@ -74,6 +77,10 @@ SOURCE_FILES=(
74
77
  "${PACKAGE_DIR}/scripts/build-xcframework.sh"
75
78
  "${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
76
79
  "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
80
+ # JSI headers we compile against. `cat` follows the symlinks CocoaPods
81
+ # installs into Pods/Headers/Public so the real header contents get hashed.
82
+ "${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi.h"
83
+ "${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi-inl.h"
77
84
  )
78
85
 
79
86
  compute_hash() {
@@ -29,26 +29,11 @@ set -eo pipefail
29
29
 
30
30
  PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
31
31
 
32
- # --- PODS_ROOT ---
33
- #
34
- # Prefer an explicit PODS_ROOT (e.g. to test against a different app's Pods).
35
- # Otherwise fall back to bare-expo under EXPO_ROOT_DIR (set by the repo's
36
- # direnv config). Without either, point at a likely repo root computed from
37
- # this script's location so the script still works outside a direnv shell.
38
-
39
- if [[ -z "${PODS_ROOT:-}" ]]; then
40
- : "${EXPO_ROOT_DIR:=$(cd "${PACKAGE_DIR}/../../.." && pwd)}"
41
- PODS_ROOT="${EXPO_ROOT_DIR}/apps/bare-expo/ios/Pods"
42
- fi
43
- if [[ ! -d "$PODS_ROOT" ]]; then
44
- echo "error: PODS_ROOT does not exist: $PODS_ROOT" >&2
45
- echo " Run \`pod install\` in apps/bare-expo/ios first, or set PODS_ROOT explicitly." >&2
46
- exit 1
47
- fi
48
- PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
49
- # Export so xcodebuild forwards it to SwiftPM's manifest evaluation, where
50
- # Package.swift reads it via resolvePodsRoot().
51
- export PODS_ROOT
32
+ source "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
33
+
34
+ # Resolve PODS_ROOT with fallback logic (from xcframework-helpers.sh).
35
+ # Must be passed to subprocess calls, not exported, to avoid polluting the environment.
36
+ resolve_pods_root "$PACKAGE_DIR"
52
37
 
53
38
  # --- Symlink xcframeworks for SwiftPM binary targets ---
54
39
 
@@ -79,7 +64,7 @@ link_xcframework "ReactNativeDependencies" \
79
64
 
80
65
  # --- Generate the jsi module map ---
81
66
 
82
- "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
67
+ env PODS_ROOT="$PODS_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
83
68
 
84
69
  # --- Pick a simulator destination ---
85
70
 
@@ -113,7 +98,7 @@ fi
113
98
  # --- Run the tests ---
114
99
 
115
100
  cd "$PACKAGE_DIR"
116
- exec xcodebuild test \
101
+ exec env PODS_ROOT="$PODS_ROOT" xcodebuild test \
117
102
  -scheme ExpoModulesJSI \
118
103
  -destination "$DESTINATION" \
119
104
  -derivedDataPath "${PACKAGE_DIR}/.DerivedData" \
@@ -4,6 +4,24 @@
4
4
  # canonical slice metadata and the Info.plist writer used by both scripts so
5
5
  # the manifest is produced from a single place.
6
6
 
7
+ # resolve_pods_root PACKAGE_DIR
8
+ # Sets PODS_ROOT, preferring an explicit value (e.g. from CocoaPods build phase)
9
+ # over a fallback to bare-expo under EXPO_ROOT_DIR. Exits if PODS_ROOT doesn't exist.
10
+ resolve_pods_root() {
11
+ local package_dir="$1"
12
+
13
+ if [[ -z "${PODS_ROOT:-}" ]]; then
14
+ : "${EXPO_ROOT_DIR:=$(cd "${package_dir}/../../.." && pwd)}"
15
+ PODS_ROOT="${EXPO_ROOT_DIR}/apps/bare-expo/ios/Pods"
16
+ fi
17
+ if [[ ! -d "$PODS_ROOT" ]]; then
18
+ echo "error: PODS_ROOT does not exist: $PODS_ROOT" >&2
19
+ echo " Run \`pod install\` in apps/bare-expo/ios first, or set PODS_ROOT explicitly." >&2
20
+ exit 1
21
+ fi
22
+ PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
23
+ }
24
+
7
25
  # All slice IDs known to the xcframework, mapped to their plist metadata.
8
26
  # CocoaPods reads Info.plist at `pod install` time and generates a per-slice
9
27
  # copy script; any slice missing from this table will be skipped by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-jsi",
3
- "version": "56.0.3",
3
+ "version": "56.0.4",
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": "42013232893cb2aa71ab218e9b422d4a8476b3f0",
44
+ "gitHead": "51c27fce31a5b3a877a4b05d832dabf4a99db5e1",
45
45
  "scripts": {
46
46
  "build": "apple/scripts/build-xcframework.sh",
47
47
  "test": "apple/scripts/test.sh"