expo-modules-core 56.0.5 → 56.0.7

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/cpp/Exceptions.cpp +0 -3
  4. package/android/src/main/cpp/Exceptions.h +1 -4
  5. package/android/src/main/cpp/ExpoHeader.pch +0 -2
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +1 -2
  7. package/android/src/main/cpp/ExpoModulesHostObject.h +1 -3
  8. package/android/src/main/cpp/JNIDeallocator.h +1 -1
  9. package/android/src/main/cpp/JNIFunctionBody.cpp +0 -1
  10. package/android/src/main/cpp/JNIFunctionBody.h +1 -2
  11. package/android/src/main/cpp/JNIInjector.cpp +1 -1
  12. package/android/src/main/cpp/JNIUtils.cpp +1 -1
  13. package/android/src/main/cpp/JNIUtils.h +1 -6
  14. package/android/src/main/cpp/JSIContext.cpp +1 -5
  15. package/android/src/main/cpp/JSIContext.h +1 -6
  16. package/android/src/main/cpp/JSITypeConverter.h +1 -5
  17. package/android/src/main/cpp/JSharedObject.h +1 -1
  18. package/android/src/main/cpp/JavaCallback.cpp +8 -13
  19. package/android/src/main/cpp/JavaCallback.h +1 -7
  20. package/android/src/main/cpp/JavaReferencesCache.cpp +1 -2
  21. package/android/src/main/cpp/JavaReferencesCache.h +1 -4
  22. package/android/src/main/cpp/JavaScriptArrayBuffer.h +1 -4
  23. package/android/src/main/cpp/JavaScriptFunction.h +1 -4
  24. package/android/src/main/cpp/JavaScriptModuleObject.h +1 -5
  25. package/android/src/main/cpp/JavaScriptObject.h +1 -5
  26. package/android/src/main/cpp/JavaScriptRuntime.cpp +0 -1
  27. package/android/src/main/cpp/JavaScriptRuntime.h +1 -2
  28. package/android/src/main/cpp/JavaScriptTypedArray.h +1 -4
  29. package/android/src/main/cpp/JavaScriptValue.h +1 -5
  30. package/android/src/main/cpp/JavaScriptWeakObject.h +1 -5
  31. package/android/src/main/cpp/MethodMetadata.cpp +1 -6
  32. package/android/src/main/cpp/MethodMetadata.h +1 -9
  33. package/android/src/main/cpp/NativeArrayBuffer.h +1 -4
  34. package/android/src/main/cpp/RuntimeHolder.h +1 -2
  35. package/android/src/main/cpp/ThreadSafeJNIGlobalRef.h +1 -1
  36. package/android/src/main/cpp/concepts/jni.h +4 -4
  37. package/android/src/main/cpp/concepts/jni_deref.h +1 -1
  38. package/android/src/main/cpp/concepts/jsi.h +1 -1
  39. package/android/src/main/cpp/decorators/JSClassesDecorator.h +1 -3
  40. package/android/src/main/cpp/decorators/JSConstantsDecorator.cpp +1 -4
  41. package/android/src/main/cpp/decorators/JSConstantsDecorator.h +1 -3
  42. package/android/src/main/cpp/decorators/JSDecorator.h +1 -2
  43. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.h +1 -4
  44. package/android/src/main/cpp/decorators/JSFunctionsDecorator.cpp +1 -2
  45. package/android/src/main/cpp/decorators/JSFunctionsDecorator.h +1 -5
  46. package/android/src/main/cpp/decorators/JSObjectDecorator.h +1 -1
  47. package/android/src/main/cpp/decorators/JSPropertiesDecorator.cpp +1 -2
  48. package/android/src/main/cpp/decorators/JSPropertiesDecorator.h +1 -3
  49. package/android/src/main/cpp/fabric/AndroidExpoViewProps.h +1 -1
  50. package/android/src/main/cpp/fabric/AndroidExpoViewState.h +1 -3
  51. package/android/src/main/cpp/fabric/FabricComponentsRegistry.h +1 -1
  52. package/android/src/main/cpp/fabric/NativeStatePropsGetter.h +1 -2
  53. package/android/src/main/cpp/installers/MainRuntimeInstaller.h +1 -2
  54. package/android/src/main/cpp/installers/WorkletRuntimeInstaller.h +1 -2
  55. package/android/src/main/cpp/javaclasses/Collections.h +1 -1
  56. package/android/src/main/cpp/javaclasses/JSRunnable.h +1 -1
  57. package/android/src/main/cpp/types/AnyType.cpp +0 -1
  58. package/android/src/main/cpp/types/AnyType.h +1 -2
  59. package/android/src/main/cpp/types/ExpectedType.h +1 -1
  60. package/android/src/main/cpp/types/FrontendConverter.cpp +1 -1
  61. package/android/src/main/cpp/types/FrontendConverter.h +1 -3
  62. package/android/src/main/cpp/types/FrontendConverterProvider.h +1 -5
  63. package/android/src/main/cpp/types/JNIToJSIConverter.h +13 -16
  64. package/android/src/main/cpp/worklets/Serializable.h +2 -2
  65. package/android/src/main/cpp/worklets/Worklet.h +2 -5
  66. package/android/src/main/cpp/worklets/WorkletJSCallInvoker.h +2 -4
  67. package/android/src/main/cpp/worklets/WorkletNativeRuntime.h +2 -2
  68. package/ios/Core/AppContext.swift +26 -4
  69. package/ios/Core/DynamicTypes/DynamicEncodableType.swift +5 -4
  70. package/ios/Core/JSValueEncoder.swift +255 -96
  71. package/ios/JS/EXReactSchedulerDispatch.h +28 -0
  72. package/ios/JS/EXReactSchedulerDispatch.mm +19 -0
  73. package/package.json +3 -3
  74. package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  75. package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  76. package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  77. package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
@@ -3,12 +3,21 @@
3
3
  import ExpoModulesJSI
4
4
 
5
5
  /**
6
- Encodes `Encodable` objects or values to `JavaScriptValue`. This implementation is incomplete,
7
- but it supports basic use cases with structs defined by the user and when the default `Encodable` implementation is used.
6
+ Encodes `Encodable` values to `JavaScriptValue`.
7
+
8
+ For any property whose static type is known to the dynamic-type registry
9
+ (anything routed via the `~` operator: arrays, dictionaries, optionals,
10
+ `RawRepresentable` enums, `Convertible`s, `JavaScriptValue` and so on),
11
+ the encoder takes the fast path through `castToJS`, preserving full
12
+ element-type metadata.
13
+
14
+ Only types the registry doesn't recognize (i.e. plain `Encodable` structs and
15
+ classes) fall back to Swift's `Encodable` machinery via `value.encode(to:)`.
8
16
  */
9
17
  internal final class JSValueEncoder: Encoder {
18
+ private let appContext: AppContext
10
19
  private let runtime: JavaScriptRuntime
11
- private let valueHolder = JSValueHolder()
20
+ private let valueHolder: JSValueHolder
12
21
 
13
22
  /**
14
23
  The result of encoding to `JavaScriptValue`. Use this property after running `encode(to:)` on the encodable.
@@ -18,43 +27,106 @@ internal final class JSValueEncoder: Encoder {
18
27
  }
19
28
 
20
29
  /**
21
- Initializes the encoder with the given runtime in which the value will be created.
30
+ Initializes the encoder with the given app context. Throws if the runtime has been lost.
31
+ */
32
+ convenience init(appContext: AppContext) throws {
33
+ try self.init(appContext: appContext, runtime: appContext.runtime)
34
+ }
35
+
36
+ /**
37
+ Initializes the encoder with the given app context and an explicit runtime.
38
+ Use this when produced JS values must live in a runtime other than the
39
+ one currently held by the app context.
22
40
  */
23
- init(runtime: JavaScriptRuntime) {
41
+ convenience init(appContext: AppContext, runtime: JavaScriptRuntime) {
42
+ self.init(
43
+ appContext: appContext,
44
+ runtime: runtime,
45
+ codingPath: [],
46
+ valueHolder: JSValueHolder()
47
+ )
48
+ }
49
+
50
+ fileprivate init(
51
+ appContext: AppContext,
52
+ runtime: JavaScriptRuntime,
53
+ codingPath: [any CodingKey],
54
+ valueHolder: JSValueHolder
55
+ ) {
56
+ self.appContext = appContext
24
57
  self.runtime = runtime
58
+ self.codingPath = codingPath
59
+ self.valueHolder = valueHolder
25
60
  }
26
61
 
27
62
  // MARK: - Encoder
28
63
 
29
- // We don't use `codingPath` and `userInfo`, but they are required by the protocol.
30
- let codingPath: [any CodingKey] = []
64
+ let codingPath: [any CodingKey]
31
65
  let userInfo: [CodingUserInfoKey: Any] = [:]
32
66
 
33
- /**
34
- Returns an encoding container appropriate for holding multiple values keyed by the given key type.
35
- */
36
67
  func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
37
- let container = JSObjectEncodingContainer<Key>(to: valueHolder, runtime: runtime)
68
+ let container = JSObjectEncodingContainer<Key>(
69
+ to: valueHolder,
70
+ appContext: appContext,
71
+ runtime: runtime,
72
+ codingPath: codingPath
73
+ )
38
74
  return KeyedEncodingContainer(container)
39
75
  }
40
76
 
41
- /**
42
- Returns an encoding container appropriate for holding multiple unkeyed values.
43
- */
44
77
  func unkeyedContainer() -> any UnkeyedEncodingContainer {
45
- return JSArrayEncodingContainer(to: valueHolder, runtime: runtime)
78
+ return JSArrayEncodingContainer(
79
+ to: valueHolder,
80
+ appContext: appContext,
81
+ runtime: runtime,
82
+ codingPath: codingPath
83
+ )
46
84
  }
47
85
 
48
- /**
49
- Returns an encoding container appropriate for holding a single primitive value, including optionals.
50
- */
51
86
  func singleValueContainer() -> any SingleValueEncodingContainer {
52
- return JSValueEncodingContainer(to: valueHolder, runtime: runtime)
87
+ return JSValueEncodingContainer(
88
+ to: valueHolder,
89
+ appContext: appContext,
90
+ runtime: runtime,
91
+ codingPath: codingPath
92
+ )
53
93
  }
54
94
  }
55
95
 
96
+ // MARK: - Shared encoding logic
97
+
56
98
  /**
57
- An object that holds a JS value that could be overriden by the encoding container.
99
+ Encodes a single value into a `JavaScriptValue`, routing through the dynamic-type
100
+ registry whenever possible and falling back to `Encodable` only for plain types.
101
+ */
102
+ private func encodeUsingDynamicType<ValueType: Encodable>(
103
+ _ value: ValueType,
104
+ appContext: AppContext,
105
+ runtime: JavaScriptRuntime,
106
+ codingPath: [any CodingKey]
107
+ ) throws -> JavaScriptValue {
108
+ let dynamicType = ~ValueType.self
109
+
110
+ if !(dynamicType is DynamicEncodableType) {
111
+ return try dynamicType.castToJS(value, appContext: appContext, in: runtime)
112
+ }
113
+
114
+ // Plain Encodable type the registry doesn't recognize — recurse via Encodable.
115
+ let holder = JSValueHolder()
116
+ let encoder = JSValueEncoder(
117
+ appContext: appContext,
118
+ runtime: runtime,
119
+ codingPath: codingPath,
120
+ valueHolder: holder
121
+ )
122
+ try value.encode(to: encoder)
123
+ return holder.value
124
+ }
125
+
126
+ // MARK: - Containers
127
+
128
+ /**
129
+ An object that holds a JS value, mutated by an encoding container as it makes progress.
58
130
  */
59
131
  private final class JSValueHolder {
60
132
  var value: JavaScriptValue = .undefined
@@ -64,146 +136,233 @@ private final class JSValueHolder {
64
136
  Single value container used to encode primitive values, including optionals.
65
137
  */
66
138
  private struct JSValueEncodingContainer: SingleValueEncodingContainer {
67
- private weak var runtime: JavaScriptRuntime?
139
+ private let appContext: AppContext
140
+ private let runtime: JavaScriptRuntime
68
141
  private let valueHolder: JSValueHolder
142
+ let codingPath: [any CodingKey]
69
143
 
70
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime?) {
71
- self.runtime = runtime
144
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
72
145
  self.valueHolder = valueHolder
146
+ self.appContext = appContext
147
+ self.runtime = runtime
148
+ self.codingPath = codingPath
73
149
  }
74
150
 
75
- // MARK: - SingleValueEncodingContainer
76
-
77
- // Unused, but required by the protocol.
78
- let codingPath: [any CodingKey] = []
79
-
80
151
  mutating func encodeNil() throws {
81
- self.valueHolder.value = .null
152
+ valueHolder.value = .null
82
153
  }
83
154
 
84
155
  mutating func encode<ValueType: Encodable>(_ value: ValueType) throws {
85
- guard let runtime else {
86
- // Do nothing when the runtime is already deallocated
87
- return
88
- }
89
- let jsValue = JavaScriptValue.from(value, runtime: runtime)
90
-
91
- // If the given value couldn't be converted to JavaScriptValue, try to encode it farther.
92
- // It might be the case when the default implementation of `Encodable` has chosen the single value container
93
- // for an optional type that should rather use keyed or unkeyed container when unwrapped.
94
- if jsValue.isUndefined() {
95
- let encoder = JSValueEncoder(runtime: runtime)
96
- try value.encode(to: encoder)
97
- self.valueHolder.value = encoder.value
98
- return
99
- }
100
- self.valueHolder.value = jsValue
156
+ valueHolder.value = try encodeUsingDynamicType(
157
+ value,
158
+ appContext: appContext,
159
+ runtime: runtime,
160
+ codingPath: codingPath
161
+ )
101
162
  }
102
163
  }
103
164
 
104
165
  /**
105
166
  Keyed container that encodes to a JavaScript object.
106
167
 
107
- - Note: This is a `class` (rather than `struct` like `JSArrayEncodingContainer` below)
108
- because it holds a non-copyable `JavaScriptObject`. A struct property of a non-copyable
109
- type makes the containing struct non-copyable too, which conflicts with
110
- `KeyedEncodingContainerProtocol`'s expectation of a copyable conformer. Using a class
111
- sidesteps the constraint via reference semantics.
168
+ - Note: This is a `class` (not a `struct`) because it holds a non-copyable
169
+ `JavaScriptObject`. A struct property of a non-copyable type makes the
170
+ containing struct non-copyable too, which conflicts with
171
+ `KeyedEncodingContainerProtocol`'s expectation of a copyable conformer.
172
+ Using a class sidesteps the constraint via reference semantics.
112
173
  */
113
- private class JSObjectEncodingContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
114
- private weak var runtime: JavaScriptRuntime?
174
+ private final class JSObjectEncodingContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
175
+ private let appContext: AppContext
176
+ private let runtime: JavaScriptRuntime
115
177
  private let valueHolder: JSValueHolder
116
- private var object: JavaScriptObject
178
+ private let object: JavaScriptObject
179
+ let codingPath: [any CodingKey]
117
180
 
118
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime) {
181
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
119
182
  let object = runtime.createObject()
120
183
  valueHolder.value = object.asValue()
121
184
 
185
+ self.appContext = appContext
122
186
  self.runtime = runtime
123
- self.object = object
124
187
  self.valueHolder = valueHolder
188
+ self.object = object
189
+ self.codingPath = codingPath
125
190
  }
126
191
 
127
- // MARK: - KeyedEncodingContainerProtocol
128
-
129
- // Unused, but required by the protocol.
130
- var codingPath: [any CodingKey] = []
131
-
132
192
  func encodeNil(forKey key: Key) throws {
133
193
  object.setProperty(key.stringValue, value: JavaScriptValue.null)
134
194
  }
135
195
 
136
196
  func encode<ValueType: Encodable>(_ value: ValueType, forKey key: Key) throws {
137
- guard let runtime else {
138
- // Do nothing when the runtime is already deallocated
139
- return
197
+ let encoded = try encodeUsingDynamicType(
198
+ value,
199
+ appContext: appContext,
200
+ runtime: runtime,
201
+ codingPath: codingPath + [key]
202
+ )
203
+ object.setProperty(key.stringValue, value: encoded)
204
+ }
205
+
206
+ // The default `KeyedEncodingContainerProtocol.encodeIfPresent` does nothing for
207
+ // nil values, which leaves the JS object without the key entirely — so `'label' in obj`
208
+ // returns false and consumers can't distinguish "absent" from "explicitly null".
209
+ // Override every overload (one per primitive plus a generic `Encodable` one) so nil
210
+ // optional fields produce an explicit `null` instead.
211
+ func encodeIfPresent<ValueType: Encodable>(_ value: ValueType?, forKey key: Key) throws {
212
+ if let value {
213
+ try encode(value, forKey: key)
214
+ } else {
215
+ try encodeNil(forKey: key)
216
+ }
217
+ }
218
+
219
+ func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
220
+ func encodeIfPresent(_ value: String?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
221
+ func encodeIfPresent(_ value: Double?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
222
+ func encodeIfPresent(_ value: Float?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
223
+ func encodeIfPresent(_ value: Int?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
224
+ func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
225
+ func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
226
+ func encodeIfPresent(_ value: Int32?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
227
+ func encodeIfPresent(_ value: Int64?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
228
+ func encodeIfPresent(_ value: UInt?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
229
+ func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
230
+ func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
231
+ func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
232
+ func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
233
+
234
+ private func encodeOptional<ValueType: Encodable>(_ value: ValueType?, forKey key: Key) throws {
235
+ if let value {
236
+ try encode(value, forKey: key)
237
+ } else {
238
+ try encodeNil(forKey: key)
140
239
  }
141
- let encoder = JSValueEncoder(runtime: runtime)
142
- try value.encode(to: encoder)
143
- object.setProperty(key.stringValue, value: encoder.value)
144
240
  }
145
241
 
146
242
  func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
147
- fatalError("JSValueEncoder does not support nested containers")
243
+ let holder = JSValueHolder()
244
+ let container = JSObjectEncodingContainer<NestedKey>(
245
+ to: holder,
246
+ appContext: appContext,
247
+ runtime: runtime,
248
+ codingPath: codingPath + [key]
249
+ )
250
+ object.setProperty(key.stringValue, value: holder.value)
251
+ return KeyedEncodingContainer(container)
148
252
  }
149
253
 
150
254
  func nestedUnkeyedContainer(forKey key: Key) -> any UnkeyedEncodingContainer {
151
- fatalError("JSValueEncoder does not support nested containers")
255
+ let holder = JSValueHolder()
256
+ let container = JSArrayEncodingContainer(
257
+ to: holder,
258
+ appContext: appContext,
259
+ runtime: runtime,
260
+ codingPath: codingPath + [key]
261
+ )
262
+ object.setProperty(key.stringValue, value: holder.value)
263
+ return container
152
264
  }
153
265
 
154
266
  func superEncoder() -> any Encoder {
155
- fatalError("superEncoder() is not implemented in JSValueEncoder")
267
+ fatalError("JSValueEncoder does not support superEncoder()")
156
268
  }
157
269
 
158
270
  func superEncoder(forKey key: Key) -> any Encoder {
159
- return self.superEncoder()
271
+ fatalError("JSValueEncoder does not support superEncoder(forKey:)")
160
272
  }
161
273
  }
162
274
 
163
275
  /**
164
276
  Unkeyed container that encodes values to a JavaScript array.
277
+
278
+ Like `JSObjectEncodingContainer`, this is a `class` so it can hold the
279
+ non-copyable `JavaScriptArray` directly without forcing the container itself
280
+ to be non-copyable.
165
281
  */
166
- private struct JSArrayEncodingContainer: UnkeyedEncodingContainer {
167
- private weak var runtime: JavaScriptRuntime?
282
+ private final class JSArrayEncodingContainer: UnkeyedEncodingContainer {
283
+ private let appContext: AppContext
284
+ private let runtime: JavaScriptRuntime
168
285
  private let valueHolder: JSValueHolder
169
- private var items: [JavaScriptValue] = []
286
+ private let array: JavaScriptArray
287
+ let codingPath: [any CodingKey]
288
+ var count: Int = 0
170
289
 
171
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime) {
290
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
291
+ let array = runtime.createArray()
292
+ valueHolder.value = array.asValue()
293
+
294
+ self.appContext = appContext
172
295
  self.runtime = runtime
173
296
  self.valueHolder = valueHolder
297
+ self.array = array
298
+ self.codingPath = codingPath
174
299
  }
175
300
 
176
- // MARK: - UnkeyedEncodingContainer
177
-
178
- // Unused, but required by the protocol.
179
- var codingPath: [any CodingKey] = []
180
- var count: Int = 0
301
+ func encodeNil() throws {
302
+ array[count] = JavaScriptValue.null
303
+ count += 1
304
+ }
181
305
 
182
- mutating func encodeNil() throws {
183
- items.append(JavaScriptValue.null)
306
+ func encode<ValueType: Encodable>(_ value: ValueType) throws {
307
+ let encoded = try encodeUsingDynamicType(
308
+ value,
309
+ appContext: appContext,
310
+ runtime: runtime,
311
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
312
+ )
313
+ array[count] = encoded
314
+ count += 1
184
315
  }
185
316
 
186
- mutating func encode<ValueType: Encodable>(_ value: ValueType) throws {
187
- guard let runtime else {
188
- // Do nothing when the runtime is already deallocated
189
- return
190
- }
191
- let encoder = JSValueEncoder(runtime: runtime)
192
- try value.encode(to: encoder)
317
+ func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
318
+ let holder = JSValueHolder()
319
+ let container = JSObjectEncodingContainer<NestedKey>(
320
+ to: holder,
321
+ appContext: appContext,
322
+ runtime: runtime,
323
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
324
+ )
325
+ array[count] = holder.value
326
+ count += 1
327
+ return KeyedEncodingContainer(container)
328
+ }
193
329
 
194
- items.append(encoder.value)
195
- valueHolder.value = .representing(value: items, in: runtime)
330
+ func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
331
+ let holder = JSValueHolder()
332
+ let container = JSArrayEncodingContainer(
333
+ to: holder,
334
+ appContext: appContext,
335
+ runtime: runtime,
336
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
337
+ )
338
+ array[count] = holder.value
339
+ count += 1
340
+ return container
196
341
  }
197
342
 
198
- mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
199
- fatalError("JSValueEncoder does not support nested containers")
343
+ func superEncoder() -> any Encoder {
344
+ fatalError("JSValueEncoder does not support superEncoder()")
200
345
  }
346
+ }
347
+
348
+ // MARK: - Helpers
349
+
350
+ /**
351
+ A coding key carrying just a string and an integer index. Used to extend
352
+ `codingPath` with array indices in the unkeyed container, since unkeyed
353
+ containers have no associated `Key` type to draw from.
354
+ */
355
+ private struct AnyCodingKey: CodingKey {
356
+ let stringValue: String
357
+ let intValue: Int?
201
358
 
202
- mutating func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
203
- fatalError("JSValueEncoder does not support nested containers")
359
+ init(stringValue: String) {
360
+ self.stringValue = stringValue
361
+ self.intValue = Int(stringValue)
204
362
  }
205
363
 
206
- mutating func superEncoder() -> any Encoder {
207
- fatalError("superEncoder() is not implemented in JSValueEncoder")
364
+ init(intValue: Int) {
365
+ self.stringValue = String(intValue)
366
+ self.intValue = intValue
208
367
  }
209
368
  }
@@ -0,0 +1,28 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ #pragma once
4
+
5
+ #ifdef __cplusplus
6
+
7
+ namespace expo {
8
+
9
+ /**
10
+ Trampoline that `ExpoModulesJSI` calls to dispatch work onto the JS thread.
11
+ Casts the `nativeScheduler` pointer back to a `react::RuntimeScheduler *` and
12
+ calls `scheduleTask` on it. The signature matches `expo::RuntimeScheduler::ScheduleFn`
13
+ declared in the xcframework's `RuntimeScheduler.h`.
14
+
15
+ Lives in ExpoModulesCore (rather than in the xcframework) so that
16
+ ExpoModulesJSI.framework's prebuilt binary doesn't need to link against
17
+ React-runtimescheduler — important for source-built RN, where those symbols
18
+ are hidden after link and unreachable via -undefined dynamic_lookup.
19
+
20
+ Hosts that initialize their own runtime (e.g. ExpoReactNativeFactory, Expo Go)
21
+ pass `&expo::dispatchOnReactScheduler` as the `dispatch` argument to
22
+ `AppContext.setRuntime`.
23
+ */
24
+ void dispatchOnReactScheduler(void *nativeScheduler, int priority, void (^callback)()) noexcept;
25
+
26
+ } // namespace expo
27
+
28
+ #endif // __cplusplus
@@ -0,0 +1,19 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ #import "EXReactSchedulerDispatch.h"
4
+
5
+ #import <react/renderer/runtimescheduler/RuntimeScheduler.h>
6
+
7
+ namespace expo {
8
+
9
+ void dispatchOnReactScheduler(void *nativeScheduler, int priority, void (^callback)()) noexcept
10
+ {
11
+ auto *scheduler = static_cast<facebook::react::RuntimeScheduler *>(nativeScheduler);
12
+ scheduler->scheduleTask(
13
+ static_cast<facebook::react::SchedulerPriority>(priority),
14
+ [callback](facebook::jsi::Runtime &) {
15
+ callback();
16
+ });
17
+ }
18
+
19
+ } // namespace expo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "56.0.5",
3
+ "version": "56.0.7",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@expo/expo-modules-macros-plugin": "~0.0.8",
50
- "expo-modules-jsi": "~56.0.2",
50
+ "expo-modules-jsi": "~56.0.3",
51
51
  "invariant": "^2.2.4"
52
52
  },
53
53
  "peerDependencies": {
@@ -66,7 +66,7 @@
66
66
  "@types/invariant": "^2.2.33",
67
67
  "expo-module-scripts": "56.0.2"
68
68
  },
69
- "gitHead": "a30353e69ca0d72b9fac5830abc631feda1ba3ae",
69
+ "gitHead": "40f0a6f6711d93762e0506b37e6e077e4bd9a541",
70
70
  "scripts": {
71
71
  "build": "expo-module build",
72
72
  "clean": "expo-module clean",