expo-modules-core 56.0.7 → 56.0.9

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 (35) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +4 -6
  4. package/android/src/main/cpp/JSIContext.h +0 -9
  5. package/android/src/main/cpp/JavaCallback.cpp +78 -47
  6. package/android/src/main/cpp/JavaCallback.h +1 -11
  7. package/android/src/main/cpp/JavaScriptValue.h +0 -1
  8. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.cpp +10 -0
  9. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.h +9 -9
  10. package/android/src/main/cpp/decorators/JSPropertiesDecorator.h +2 -0
  11. package/android/src/main/cpp/fabric/AndroidExpoViewState.h +2 -0
  12. package/android/src/main/cpp/fabric/ExpoComponentDescriptorFactory.cpp +31 -0
  13. package/android/src/main/cpp/fabric/ExpoComponentDescriptorFactory.h +23 -0
  14. package/android/src/main/cpp/fabric/FabricComponentsRegistry.cpp +3 -25
  15. package/android/src/main/cpp/fabric/FabricComponentsRegistry.h +0 -17
  16. package/android/src/main/cpp/fabric/NativeStatePropsGetter.cpp +1 -0
  17. package/android/src/main/cpp/fabric/NativeStatePropsGetter.h +0 -1
  18. package/android/src/main/java/expo/modules/kotlin/devtools/cdp/CdpNetworkTypes.kt +3 -2
  19. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObject.kt +26 -4
  20. package/android/src/main/java/expo/modules/kotlin/views/OnAttachAfterDetachmentListener.kt +41 -0
  21. package/build/polyfill/CoreModule.d.ts.map +1 -1
  22. package/build/ts-declarations/SharedRef.d.ts.map +1 -1
  23. package/ios/Core/ExpoModulesMacros.swift +64 -5
  24. package/ios/Core/ModuleHolder.swift +19 -1
  25. package/ios/Core/Modules/ModuleDefinition.swift +7 -0
  26. package/ios/Core/Protocols/AnyModule.swift +12 -0
  27. package/ios/Core/SharedObjects/SharedObject.swift +64 -43
  28. package/ios/DevTools/CdpNetworkTypes.swift +1 -1
  29. package/package.json +4 -4
  30. package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  31. package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  32. package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  33. package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  34. package/src/polyfill/CoreModule.ts +3 -3
  35. package/src/ts-declarations/SharedRef.ts +3 -3
package/CHANGELOG.md CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 56.0.9 — 2026-05-15
14
+
15
+ ### 🎉 New features
16
+
17
+ - Added single-payload overloads for `SharedObject.emit` on iOS and Android. The iOS API also accepts an already-converted `JavaScriptValue` payload to skip the native-to-JS conversion step. ([#45596](https://github.com/expo/expo/pull/45596) by [@tsapeta](https://github.com/tsapeta))
18
+
19
+ ## 56.0.8 — 2026-05-13
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - [Android] Avoid remounting Jetpack Compose views during same-frame React Native reparenting. ([#45711](https://github.com/expo/expo/pull/45711) by [@mvincentong](https://github.com/mvincentong))
24
+ - [Android] Fixed `associatedCookies` in `Network.requestWillBeSentExtraInfo` CDP events serializing as a JSON object instead of an array. ([#45720](https://github.com/expo/expo/pull/45720) by [@huntie](https://github.com/huntie))
25
+
13
26
  ## 56.0.7 — 2026-05-13
14
27
 
15
28
  ### 🐛 Bug fixes
@@ -21,6 +34,7 @@
21
34
  ### 💡 Others
22
35
 
23
36
  - [iOS] `AppContext.setRuntime` now takes the native React `RuntimeScheduler` pointer and a dispatch trampoline alongside the runtime pointer. ([#45636](https://github.com/expo/expo/pull/45636) by [@tsapeta](https://github.com/tsapeta))
37
+ - Deprecated `SharedObject.emit(event:arguments:)` (iOS) and the `vararg` `emit` (Android) in favor of the new single-payload overloads. Existing single-argument call sites keep working unchanged. ([#45596](https://github.com/expo/expo/pull/45596) by [@tsapeta](https://github.com/tsapeta))
24
38
 
25
39
  ## 56.0.5 — 2026-05-08
26
40
 
@@ -27,7 +27,7 @@ if (shouldIncludeCompose) {
27
27
  }
28
28
 
29
29
  group = 'host.exp.exponent'
30
- version = '56.0.7'
30
+ version = '56.0.9'
31
31
 
32
32
  def isExpoModulesCoreTests = {
33
33
  Gradle gradle = getGradle()
@@ -94,7 +94,7 @@ android {
94
94
  defaultConfig {
95
95
  consumerProguardFiles 'proguard-rules.pro'
96
96
  versionCode 1
97
- versionName "56.0.7"
97
+ versionName "56.0.9"
98
98
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
99
99
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
100
100
 
@@ -193,13 +193,11 @@ abstract class ExpoComposeView<T : ComposeProps>(
193
193
  Content()
194
194
  }
195
195
  }
196
- it.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
197
- override fun onViewAttachedToWindow(v: View) {
196
+ it.addOnAttachStateChangeListener(
197
+ OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
198
198
  it.disposeComposition()
199
- }
200
-
201
- override fun onViewDetachedFromWindow(v: View) = Unit
202
- })
199
+ })
200
+ )
203
201
  }
204
202
  addView(composeView)
205
203
  }
@@ -7,22 +7,13 @@
7
7
  #include "JavaScriptModuleObject.h"
8
8
  #include "JavaScriptValue.h"
9
9
  #include "JavaScriptObject.h"
10
- #include "JavaReferencesCache.h"
11
10
  #include "JSReferencesCache.h"
12
11
  #include "JNIDeallocator.h"
13
12
  #include "ThreadSafeJNIGlobalRef.h"
14
13
  #include "javaclasses/JSRunnable.h"
15
14
 
16
- #include <ReactCommon/CallInvokerHolder.h>
17
15
  #include <ReactCommon/CallInvoker.h>
18
16
 
19
- #if IS_NEW_ARCHITECTURE_ENABLED
20
-
21
- #include <ReactCommon/RuntimeExecutor.h>
22
- #include <react/jni/JRuntimeExecutor.h>
23
-
24
- #endif
25
-
26
17
  namespace jni = facebook::jni;
27
18
  namespace jsi = facebook::jsi;
28
19
  namespace react = facebook::react;
@@ -60,10 +60,8 @@ jni::local_ref<JavaCallback::javaobject> JavaCallback::newInstance(
60
60
  return object;
61
61
  }
62
62
 
63
- template<typename T>
64
- void JavaCallback::invokeJSFunction(
65
- ArgsConverter<typename std::remove_const<T>::type> argsConverter,
66
- T arg
63
+ void JavaCallback::invokeWithResolver(
64
+ std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction)> resolver
67
65
  ) {
68
66
  const auto strongCallbackContext = this->callbackContext.lock();
69
67
  // The context were deallocated before the callback was invoked.
@@ -80,8 +78,7 @@ void JavaCallback::invokeJSFunction(
80
78
  jsInvoker->invokeAsync(
81
79
  [
82
80
  context = callbackContext,
83
- argsConverter = std::move(argsConverter),
84
- arg = std::move(arg)
81
+ resolver = std::move(resolver)
85
82
  ]() -> void {
86
83
  auto strongContext = context.lock();
87
84
  // The context were deallocated before the callback was invoked.
@@ -98,25 +95,11 @@ void JavaCallback::invokeJSFunction(
98
95
  jsi::Function &jsFunction = strongContext->resolveHolder.value();
99
96
  jsi::Runtime &rt = strongContext->rt;
100
97
 
101
- argsConverter(rt, jsFunction, std::move(arg));
98
+ resolver(rt, jsFunction);
102
99
  strongContext->invalidate();
103
100
  });
104
101
  }
105
102
 
106
- template<class T>
107
- void JavaCallback::invokeJSFunction(T arg) {
108
- invokeJSFunction<T>(
109
- [](
110
- jsi::Runtime &rt,
111
- jsi::Function &jsFunction,
112
- T arg
113
- ) {
114
- jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::forward<T>(arg)));
115
- },
116
- arg
117
- );
118
- }
119
-
120
103
  template<class T>
121
104
  void JavaCallback::invokeJSFunctionForArray(T &arg) {
122
105
  size_t size = arg->size();
@@ -125,38 +108,51 @@ void JavaCallback::invokeJSFunctionForArray(T &arg) {
125
108
  rawArray.size = size;
126
109
  rawArray.data = std::move(region);
127
110
 
128
- invokeJSFunction<decltype(rawArray)>(
129
- std::move(rawArray)
111
+ invokeWithResolver(
112
+ [rawArray = std::move(rawArray)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
113
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(rawArray)));
114
+ }
130
115
  );
131
116
  }
132
117
 
133
118
  void JavaCallback::invoke() {
134
- invokeJSFunction<nullptr_t>(
135
- [](
136
- jsi::Runtime &rt,
137
- jsi::Function &jsFunction,
138
- nullptr_t arg
139
- ) {
119
+ invokeWithResolver(
120
+ [](jsi::Runtime &rt, jsi::Function &jsFunction) {
140
121
  jsFunction.call(rt, {jsi::Value::null()});
141
- },
142
- nullptr
122
+ }
143
123
  );
144
124
  }
145
125
 
146
126
  void JavaCallback::invokeBool(bool result) {
147
- invokeJSFunction(result);
127
+ invokeWithResolver(
128
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
129
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
130
+ }
131
+ );
148
132
  }
149
133
 
150
134
  void JavaCallback::invokeInt(int result) {
151
- invokeJSFunction(result);
135
+ invokeWithResolver(
136
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
137
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
138
+ }
139
+ );
152
140
  }
153
141
 
154
142
  void JavaCallback::invokeDouble(double result) {
155
- invokeJSFunction(result);
143
+ invokeWithResolver(
144
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
145
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
146
+ }
147
+ );
156
148
  }
157
149
 
158
150
  void JavaCallback::invokeFloat(float result) {
159
- invokeJSFunction(result);
151
+ invokeWithResolver(
152
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
153
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
154
+ }
155
+ );
160
156
  }
161
157
 
162
158
  void JavaCallback::invokeString(jni::alias_ref<jstring> result) {
@@ -164,41 +160,76 @@ void JavaCallback::invokeString(jni::alias_ref<jstring> result) {
164
160
  const char *rawValue = env->GetStringUTFChars(result.get(), nullptr);
165
161
  std::string parsedResult = rawValue;
166
162
  env->ReleaseStringUTFChars(result.get(), rawValue);
167
- invokeJSFunction(parsedResult);
163
+ invokeWithResolver(
164
+ [parsedResult = std::move(parsedResult)](jsi::Runtime &rt, jsi::Function &jsFunction) {
165
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, parsedResult));
166
+ }
167
+ );
168
168
  }
169
169
 
170
170
  void JavaCallback::invokeCollection(jni::alias_ref<jni::JCollection<jobject>> result) {
171
- invokeJSFunction<
172
- jni::global_ref<jni::JCollection<jobject>>
173
- >(jni::make_global(result));
171
+ jni::global_ref<jni::JCollection<jobject>> globalResult = jni::make_global(result);
172
+ invokeWithResolver(
173
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
174
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
175
+ }
176
+ );
174
177
  }
175
178
 
176
179
  void JavaCallback::invokeMap(jni::alias_ref<jni::JMap<jstring, jobject>> result) {
177
- invokeJSFunction<
178
- jni::global_ref<jni::JMap<jstring, jobject>>
179
- >(jni::make_global(result));
180
+ jni::global_ref<jni::JMap<jstring, jobject>> globalResult = jni::make_global(result);
181
+ invokeWithResolver(
182
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
183
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
184
+ }
185
+ );
180
186
  }
181
187
 
182
188
  void
183
189
  JavaCallback::invokeWritableArray(jni::alias_ref<react::WritableNativeArray::javaobject> result) {
184
- invokeJSFunction(result->cthis()->consume());
190
+ auto consumed = result->cthis()->consume();
191
+ invokeWithResolver(
192
+ [consumed = std::move(consumed)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
193
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(consumed)));
194
+ }
195
+ );
185
196
  }
186
197
 
187
198
  void JavaCallback::invokeWritableMap(jni::alias_ref<react::WritableNativeMap::javaobject> result) {
188
- invokeJSFunction(result->cthis()->consume());
199
+ auto consumed = result->cthis()->consume();
200
+ invokeWithResolver(
201
+ [consumed = std::move(consumed)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
202
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(consumed)));
203
+ }
204
+ );
189
205
  }
190
206
 
191
207
  void JavaCallback::invokeSharedObject(jni::alias_ref<JSharedObject::javaobject> result) {
192
- invokeJSFunction(jni::make_global(result));
208
+ auto globalResult = jni::make_global(result);
209
+ invokeWithResolver(
210
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
211
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
212
+ }
213
+ );
193
214
  }
194
215
 
195
216
  void JavaCallback::invokeJavaScriptArrayBuffer(
196
217
  jni::alias_ref<JavaScriptArrayBuffer::javaobject> result) {
197
- invokeJSFunction(jni::make_global(result));
218
+ auto globalResult = jni::make_global(result);
219
+ invokeWithResolver(
220
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
221
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
222
+ }
223
+ );
198
224
  }
199
225
 
200
226
  void JavaCallback::invokeNativeArrayBuffer(jni::alias_ref<NativeArrayBuffer::javaobject> result) {
201
- invokeJSFunction(jni::make_global(result));
227
+ auto globalResult = jni::make_global(result);
228
+ invokeWithResolver(
229
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
230
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
231
+ }
232
+ );
202
233
  }
203
234
 
204
235
  void JavaCallback::invokeIntArray(jni::alias_ref<jni::JArrayInt> result) {
@@ -90,19 +90,9 @@ private:
90
90
  void invokeDoubleArray(jni::alias_ref<jni::JArrayDouble> result);
91
91
  void invokeFloatArray(jni::alias_ref<jni::JArrayFloat> result);
92
92
 
93
- template<class T>
94
- using ArgsConverter = std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction, T arg)>;
95
-
96
- template<class T>
97
- inline void invokeJSFunction(
98
- ArgsConverter<typename std::remove_const<T>::type> argsConverter,
99
- T arg
100
- );
93
+ void invokeWithResolver(std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction)> resolver);
101
94
 
102
95
  template<class T>
103
96
  void invokeJSFunctionForArray(T &arg);
104
-
105
- template<class T>
106
- inline void invokeJSFunction(T arg);
107
97
  };
108
98
  } // namespace expo
@@ -5,7 +5,6 @@
5
5
  #include "ExpoHeader.pch"
6
6
  #include "JSIObjectWrapper.h"
7
7
  #include "JavaScriptTypedArray.h"
8
- #include "JavaScriptArrayBuffer.h"
9
8
  #include "JNIDeallocator.h"
10
9
 
11
10
  namespace jni = facebook::jni;
@@ -2,11 +2,21 @@
2
2
 
3
3
  #include "JSDecoratorsBridgingObject.h"
4
4
 
5
+ #include "JSFunctionsDecorator.h"
6
+ #include "JSPropertiesDecorator.h"
7
+ #include "JSConstantsDecorator.h"
8
+ #include "JSObjectDecorator.h"
5
9
  #include "JSClassesDecorator.h"
6
10
  #include "../types/ReturnType.h"
7
11
 
8
12
  namespace expo {
9
13
 
14
+ JSDecoratorsBridgingObject::~JSDecoratorsBridgingObject() = default;
15
+
16
+ JSClassesDecorator* JSDecoratorsBridgingObject::getClassDecorator() const {
17
+ return classDecorator.get();
18
+ }
19
+
10
20
  jni::local_ref<jni::HybridClass<JSDecoratorsBridgingObject>::jhybriddata>
11
21
  JSDecoratorsBridgingObject::initHybrid(jni::alias_ref<jhybridobject> jThis) {
12
22
  return makeCxxInstance();
@@ -8,18 +8,20 @@
8
8
  #include "../JNIFunctionBody.h"
9
9
  #include "../types/ExpectedType.h"
10
10
 
11
- #include "JSFunctionsDecorator.h"
12
- #include "JSPropertiesDecorator.h"
13
- #include "JSConstantsDecorator.h"
14
- #include "JSObjectDecorator.h"
15
- #include "JSClassesDecorator.h"
16
-
17
11
  namespace jni = facebook::jni;
18
12
 
19
13
  namespace expo {
20
14
 
15
+ class JSFunctionsDecorator;
16
+ class JSPropertiesDecorator;
17
+ class JSConstantsDecorator;
18
+ class JSObjectDecorator;
19
+ class JSClassesDecorator;
20
+
21
21
  class JSDecoratorsBridgingObject : public jni::HybridClass<JSDecoratorsBridgingObject> {
22
22
  public:
23
+ ~JSDecoratorsBridgingObject();
24
+
23
25
  static auto constexpr
24
26
  kJavaDescriptor = "Lexpo/modules/kotlin/jni/decorators/JSDecoratorsBridgingObject;";
25
27
  static auto constexpr TAG = "JSDecoratorsBridgingObject";
@@ -87,9 +89,7 @@ public:
87
89
  /**
88
90
  * Returns the class decorator, or nullptr if none was registered.
89
91
  */
90
- JSClassesDecorator* getClassDecorator() const {
91
- return classDecorator.get();
92
- }
92
+ JSClassesDecorator* getClassDecorator() const;
93
93
 
94
94
  private:
95
95
  friend HybridBase;
@@ -1,5 +1,7 @@
1
1
  // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
2
 
3
+ #pragma once
4
+
3
5
  #include "../ExpoHeader.pch"
4
6
 
5
7
  #include "../MethodMetadata.h"
@@ -1,3 +1,5 @@
1
+ #pragma once
2
+
1
3
  #include "../ExpoHeader.pch"
2
4
  #include "ExpoViewState.h"
3
5
 
@@ -0,0 +1,31 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #include "ExpoComponentDescriptorFactory.h"
4
+ #include "AndroidExpoViewComponentDescriptor.h"
5
+
6
+ namespace react = facebook::react;
7
+
8
+ namespace expo {
9
+
10
+ StatePropMapType statePropMap = {};
11
+
12
+ react::ComponentDescriptor::Unique concreteExpoComponentDescriptorConstructor(
13
+ const react::ComponentDescriptorParameters &parameters
14
+ ) {
15
+ auto descriptor = std::make_unique<AndroidExpoViewComponentDescriptor>(
16
+ parameters,
17
+ react::RawPropsParser(/*useRawPropsJsiValue=*/true)
18
+ );
19
+
20
+ if (statePropMap.contains(std::static_pointer_cast<std::string const>(parameters.flavor))) {
21
+ descriptor->setStateProps(
22
+ statePropMap.at(
23
+ std::static_pointer_cast<std::string const>(parameters.flavor)
24
+ )
25
+ );
26
+ }
27
+
28
+ return descriptor;
29
+ }
30
+
31
+ } // namespace expo
@@ -0,0 +1,23 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #pragma once
4
+
5
+ #include <react/renderer/core/ComponentDescriptor.h>
6
+ #include "../types/FrontendConverter.h"
7
+
8
+ namespace react = facebook::react;
9
+
10
+ namespace expo {
11
+
12
+ using StatePropMapType = std::unordered_map<
13
+ react::ComponentDescriptor::Flavor,
14
+ std::unordered_map<std::string, std::shared_ptr<FrontendConverter>>
15
+ >;
16
+
17
+ extern StatePropMapType statePropMap;
18
+
19
+ react::ComponentDescriptor::Unique concreteExpoComponentDescriptorConstructor(
20
+ const react::ComponentDescriptorParameters &parameters
21
+ );
22
+
23
+ } // namespace expo
@@ -1,38 +1,16 @@
1
1
  // Copyright 2018-present 650 Industries. All rights reserved.
2
2
 
3
3
  #include "FabricComponentsRegistry.h"
4
+ #include "ExpoComponentDescriptorFactory.h"
4
5
  #include "../types/FrontendConverterProvider.h"
6
+ #include <CoreComponentsRegistry.h>
7
+ #include <react/renderer/componentregistry/ComponentDescriptorProvider.h>
5
8
 
6
9
  namespace jni = facebook::jni;
7
10
  namespace react = facebook::react;
8
11
 
9
12
  namespace expo {
10
13
 
11
- #pragma clang diagnostic push
12
- #pragma clang diagnostic ignored "-Wextern-initializer"
13
- // Clang think that we don't need to initialize the extern variable here, but we do.
14
- extern StatePropMapType statePropMap = {};
15
- #pragma clang diagnostic pop
16
-
17
- AndroidExpoViewComponentDescriptor::Unique concreteExpoComponentDescriptorConstructor(
18
- const react::ComponentDescriptorParameters &parameters
19
- ) {
20
- auto descriptor = std::make_unique<AndroidExpoViewComponentDescriptor>(
21
- parameters,
22
- react::RawPropsParser(/*useRawPropsJsiValue=*/true)
23
- );
24
-
25
- if (statePropMap.contains(std::static_pointer_cast<std::string const>(parameters.flavor))) {
26
- descriptor->setStateProps(
27
- statePropMap.at(
28
- std::static_pointer_cast<std::string const>(parameters.flavor)
29
- )
30
- );
31
- }
32
-
33
- return descriptor;
34
- }
35
-
36
14
  // static
37
15
  void FabricComponentsRegistry::registerNatives() {
38
16
  registerHybrid({
@@ -3,29 +3,12 @@
3
3
  #pragma once
4
4
 
5
5
  #include "../ExpoHeader.pch"
6
- #include <CoreComponentsRegistry.h>
7
- #include <react/renderer/componentregistry/ComponentDescriptorProvider.h>
8
-
9
6
  #include "../types/ExpectedType.h"
10
- #include "../types/FrontendConverter.h"
11
- #include "AndroidExpoViewComponentDescriptor.h"
12
7
 
13
8
  namespace jni = facebook::jni;
14
- namespace react = facebook::react;
15
9
 
16
10
  namespace expo {
17
11
 
18
- typedef std::unordered_map<
19
- AndroidExpoViewComponentDescriptor::Flavor,
20
- std::unordered_map<std::string, std::shared_ptr<FrontendConverter>>
21
- > StatePropMapType;
22
-
23
- extern StatePropMapType statePropMap;
24
-
25
- AndroidExpoViewComponentDescriptor::Unique concreteExpoComponentDescriptorConstructor(
26
- const react::ComponentDescriptorParameters &parameters
27
- );
28
-
29
12
  class FabricComponentsRegistry : public jni::HybridClass<FabricComponentsRegistry> {
30
13
  public:
31
14
  static auto constexpr
@@ -1,5 +1,6 @@
1
1
  #include "NativeStatePropsGetter.h"
2
2
  #include "AndroidExpoViewState.h"
3
+ #include <react/fabric/StateWrapperImpl.h>
3
4
 
4
5
  #include <react/renderer/core/ConcreteState.h>
5
6
 
@@ -1,7 +1,6 @@
1
1
  #pragma once
2
2
 
3
3
  #include "../ExpoHeader.pch"
4
- #include <react/fabric/StateWrapperImpl.h>
5
4
 
6
5
  namespace jni = facebook::jni;
7
6
  namespace react = facebook::react;
@@ -5,6 +5,7 @@ package expo.modules.kotlin.devtools.cdp
5
5
  import expo.modules.kotlin.devtools.ExpoNetworkInspectOkHttpNetworkInterceptor
6
6
  import expo.modules.kotlin.devtools.toSingleMap
7
7
  import okio.Buffer
8
+ import org.json.JSONArray
8
9
  import org.json.JSONObject
9
10
  import java.math.BigDecimal
10
11
 
@@ -155,7 +156,7 @@ data class RequestWillBeSentParams(
155
156
 
156
157
  data class RequestWillBeSentExtraInfoParams(
157
158
  val requestId: RequestId,
158
- val associatedCookies: Map<String, String> = emptyMap(),
159
+ val associatedCookies: List<Any> = emptyList(),
159
160
  val headers: Headers,
160
161
  val connectTiming: ConnectTiming
161
162
  ) : JsonSerializable {
@@ -168,7 +169,7 @@ data class RequestWillBeSentExtraInfoParams(
168
169
  override fun toJSONObject(): JSONObject {
169
170
  return JSONObject().apply {
170
171
  put("requestId", requestId)
171
- put("associatedCookies", JSONObject(associatedCookies))
172
+ put("associatedCookies", JSONArray(associatedCookies))
172
173
  put("headers", JSONObject(headers))
173
174
  put("connectTiming", connectTiming.toJSONObject())
174
175
  }
@@ -41,20 +41,42 @@ open class SharedObject(runtime: Runtime? = null) {
41
41
  )
42
42
  }
43
43
 
44
- fun emit(eventName: String, vararg args: Any?) {
44
+ /**
45
+ * Emits an event with no payload to the associated JavaScript object.
46
+ */
47
+ fun emit(event: String) {
48
+ emitInternal(event, emptyArray())
49
+ }
50
+
51
+ /**
52
+ * Emits an event with a single payload to the associated JavaScript object.
53
+ */
54
+ fun emit(event: String, payload: Any?) {
55
+ emitInternal(event, arrayOf(payload))
56
+ }
57
+
58
+ @Deprecated(
59
+ "Multi-argument event emission is deprecated. Use `emit(event)` or `emit(event, payload)` and pass a single payload (typically a Map/Bundle) instead.",
60
+ ReplaceWith("emit(event, args)")
61
+ )
62
+ fun emit(event: String, vararg args: Any?) {
63
+ emitInternal(event, args)
64
+ }
65
+
66
+ private fun emitInternal(event: String, payload: Array<out Any?>) {
45
67
  val jsObject = getJavaScriptObject() ?: return
46
68
  val jniInterop = runtime?.jsiContext ?: return
47
69
  try {
48
70
  JNIUtils.emitEvent(
49
71
  jsObject,
50
72
  jniInterop,
51
- eventName,
52
- args
73
+ event,
74
+ payload
53
75
  .map { JSTypeConverterProvider.convertToJSValue(it, useExperimentalConverter = true) }
54
76
  .toTypedArray()
55
77
  )
56
78
  } catch (e: Throwable) {
57
- logger.error("Unable to send event '$eventName' by shared object of type ${this::class.java.simpleName}", e)
79
+ logger.error("Unable to send event '$event' by shared object of type ${this::class.java.simpleName}", e)
58
80
  }
59
81
  }
60
82
 
@@ -0,0 +1,41 @@
1
+ package expo.modules.kotlin.views
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.view.View
6
+
7
+ /**
8
+ * Runs a callback only when a view is attached after staying detached through one main-loop turn.
9
+ * React Native can synchronously detach and reattach native children during view reparenting, and
10
+ * those moves should not be treated like a full detach.
11
+ */
12
+ internal class OnAttachAfterDetachmentListener(
13
+ private val onAttachAfterDetachment: () -> Unit,
14
+ private val post: (Runnable) -> Unit = { runnable ->
15
+ Handler(Looper.getMainLooper()).post(runnable)
16
+ }
17
+ ) : View.OnAttachStateChangeListener {
18
+ private var shouldRunOnAttach = false
19
+ private var detachGeneration = 0
20
+
21
+ override fun onViewAttachedToWindow(v: View) {
22
+ detachGeneration += 1
23
+
24
+ if (shouldRunOnAttach) {
25
+ shouldRunOnAttach = false
26
+ onAttachAfterDetachment()
27
+ }
28
+ }
29
+
30
+ override fun onViewDetachedFromWindow(v: View) {
31
+ val generation = ++detachGeneration
32
+
33
+ post(
34
+ Runnable {
35
+ if (detachGeneration == generation && !v.isAttachedToWindow) {
36
+ shouldRunOnAttach = true
37
+ }
38
+ }
39
+ )
40
+ }
41
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"CoreModule.d.ts","sourceRoot":"","sources":["../../src/polyfill/CoreModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,IAAI,gBAAgB,EAChC,iBAAiB,EAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE/E,qBAAa,YAAY,CAAC,UAAU,SAAS,SAAS,CAAE,YAAW,gBAAgB;IACjF,OAAO,CAAC,SAAS,CAAC,CAAuC;IAEzD,WAAW,CAAC,SAAS,SAAS,MAAM,UAAU,EAC5C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,iBAAiB;IAuBpB,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAC/C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,IAAI;IAOP,kBAAkB,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAQlF,IAAI,CAAC,SAAS,SAAS,MAAM,UAAU,EACrC,SAAS,EAAE,SAAS,EACpB,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GACzC,IAAI;IAeP,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM;IAI/E,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAE9E,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;CAC9E;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,cAAc,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,OAAO,IAAI,IAAI;CAGhB;AAED,qBAAa,SAAS,CAClB,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAErD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,aAAa,CAAC,cAAc,CAAC;IAExC,aAAa,EAAE,MAAM,CAAa;CACnC"}
1
+ {"version":3,"file":"CoreModule.d.ts","sourceRoot":"","sources":["../../src/polyfill/CoreModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,IAAI,gBAAgB,EAChC,iBAAiB,EAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE/E,qBAAa,YAAY,CAAC,UAAU,SAAS,SAAS,CAAE,YAAW,gBAAgB;IACjF,OAAO,CAAC,SAAS,CAAC,CAAuC;IAEzD,WAAW,CAAC,SAAS,SAAS,MAAM,UAAU,EAC5C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,iBAAiB;IAuBpB,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAC/C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,IAAI;IAOP,kBAAkB,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAQlF,IAAI,CAAC,SAAS,SAAS,MAAM,UAAU,EACrC,SAAS,EAAE,SAAS,EACpB,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GACzC,IAAI;IAeP,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM;IAI/E,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAE9E,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;CAC9E;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,cAAc,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,OAAO,IAAI,IAAI;CAGhB;AAED,qBAAa,SAAS,CACpB,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAEnD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,aAAa,CAAC,cAAc,CAAC;IAExC,aAAa,EAAE,MAAM,CAAa;CACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SharedRef.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/SharedRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS,CAC1B,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAErD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,YAAY,CAAC,UAAU,CAAC;IAEnC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,cAAc,CAAC;IAE7C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"SharedRef.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/SharedRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS,CAC5B,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAEnD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,YAAY,CAAC,UAAU,CAAC;IAEnC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,cAAc,CAAC;IAE7C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB"}
@@ -1,8 +1,6 @@
1
-
2
- /**
3
- Declares macro signatures whose implementations are provided by the `@expo/expo-modules-macros-plugin` binary.
4
- Keep in sync with `@expo/expo-modules-macros-plugin/apple/Sources/ExpoModulesOptimized/ExpoModulesOptimized.swift`.
5
- */
1
+ // Declares macro signatures whose implementations are provided by the `ExpoModulesMacros` compiler
2
+ // plugin shipped in the `@expo/expo-modules-macros-plugin` package. Keep the `#externalMacro`
3
+ // module/type names below in sync with the macro implementations in that package.
6
4
 
7
5
  // MARK: - Macro declarations
8
6
 
@@ -25,3 +23,64 @@
25
23
  @attached(peer, names: arbitrary)
26
24
  public macro OptimizedFunction() =
27
25
  #externalMacro(module: "ExpoModulesMacros", type: "OptimizedFunctionAttachedMacro")
26
+
27
+ /// Marker macro applied to module / shared-object members that should be exposed to JavaScript.
28
+ /// The accompanying `@ExpoModule` and `@SharedObject` macros discover `@JS`-marked declarations
29
+ /// and generate the matching `Function` / `AsyncFunction` / `Property` / `Constructor` registrations.
30
+ ///
31
+ /// Usage:
32
+ ///
33
+ /// @JS
34
+ /// func greet(name: String) -> String { ... }
35
+ ///
36
+ /// @JS("doWork")
37
+ /// func performWork() async throws { ... }
38
+ ///
39
+ /// @JS
40
+ /// var status: String { "ok" }
41
+ @attached(peer)
42
+ public macro JS(_ jsName: String? = nil) =
43
+ #externalMacro(module: "ExpoModulesMacros", type: "JSMacro")
44
+
45
+ /// Member macro applied to a `Module` subclass. Scans the class body for declarations
46
+ /// marked with `@JS` and synthesizes a framework-internal `_exposedDefinition()` method.
47
+ /// `expo-modules-core` calls it automatically and merges the result into the module's
48
+ /// definition, so the user doesn't have to reference it from `definition()`.
49
+ ///
50
+ /// Usage:
51
+ ///
52
+ /// @ExpoModule
53
+ /// public final class MyModule: Module {
54
+ /// public func definition() -> ModuleDefinition {
55
+ /// Name("MyModule")
56
+ /// }
57
+ ///
58
+ /// @JS
59
+ /// func greet(name: String) -> String { "Hi, \(name)" }
60
+ /// }
61
+ @attached(member, names: named(_exposedDefinition), named(appContext), named(init))
62
+ public macro ExpoModule(_ name: String? = nil, classes: [Any.Type] = []) =
63
+ #externalMacro(module: "ExpoModulesMacros", type: "ExpoModuleMacro")
64
+
65
+ /// Member macro applied to a `SharedObject` subclass. Scans the class body for declarations
66
+ /// marked with `@JS` (including a single `@JS init(...)` for the JS constructor) and
67
+ /// synthesizes a `_exposedClassDefinition()` static method returning a `ClassDefinition`.
68
+ /// The companion `@ExpoModule(classes: [Foo.self])` wires the class into the module's
69
+ /// exposed surface.
70
+ ///
71
+ /// Usage:
72
+ ///
73
+ /// @SharedObject
74
+ /// final class Cache: SharedObject {
75
+ /// @JS
76
+ /// init(name: String) { self.name = name }
77
+ ///
78
+ /// @JS
79
+ /// func get(_ key: String) -> String? { ... }
80
+ ///
81
+ /// @JS
82
+ /// var size: Int { 42 }
83
+ /// }
84
+ @attached(member, names: named(_exposedClassDefinition))
85
+ public macro SharedObject(_ name: String? = nil) =
86
+ #externalMacro(module: "ExpoModulesMacros", type: "SharedObjectMacro")
@@ -46,10 +46,28 @@ public final class ModuleHolder {
46
46
  self.appContext = appContext
47
47
  self._name = name
48
48
  self.module = module
49
- self.definition = module.definition()
49
+ self.definition = ModuleHolder.buildDefinition(for: module)
50
50
  post(event: .moduleCreate)
51
51
  }
52
52
 
53
+ /// Combines the user-authored definition with the entries synthesized by the
54
+ /// `@ExpoModule` macro on this module's class (if any). The macro emits a
55
+ /// `_exposedDefinition()` method returning an `[AnyDefinition]` array of the
56
+ /// `Function` / `Property` / `Constructor` entries it generated from `@JS`
57
+ /// members. Those entries are prepended to the user's definitions and the
58
+ /// whole list is fed back through `ModuleDefinition.init` so the merged
59
+ /// result is rebucketed (into `functions`, `properties`, etc.) just like a
60
+ /// hand-written definition. Modules that don't use the macro fall through
61
+ /// the empty-exposed fast path and return the user's definition unchanged.
62
+ private static func buildDefinition(for module: AnyModule) -> ModuleDefinition {
63
+ let userDefinition = module.definition()
64
+ let exposed = module._exposedDefinition()
65
+ if exposed.isEmpty {
66
+ return userDefinition
67
+ }
68
+ return ModuleDefinition(definitions: exposed + userDefinition.rawDefinitions)
69
+ }
70
+
53
71
  // MARK: Constants
54
72
 
55
73
  /**
@@ -29,10 +29,17 @@ public final class ModuleDefinition: ObjectDefinition {
29
29
 
30
30
  let eventObservers: [AnyEventObservingDefinition]
31
31
 
32
+ /// The raw list of definitions used to construct this module. Retained so
33
+ /// that `ModuleHolder` can merge in the entries synthesized by the
34
+ /// `@ExpoModule` macro without losing the user-authored ones.
35
+ let rawDefinitions: [AnyDefinition]
36
+
32
37
  /**
33
38
  Initializer that is called by the `ModuleDefinitionBuilder` results builder.
34
39
  */
35
40
  override init(definitions: [AnyDefinition]) {
41
+ self.rawDefinitions = definitions
42
+
36
43
  self.name = definitions
37
44
  .compactMap { $0 as? ModuleNameDefinition }
38
45
  .last?
@@ -14,4 +14,16 @@ public protocol AnyModule: AnyObject, AnyArgument {
14
14
  */
15
15
  @ModuleDefinitionBuilder
16
16
  func definition() -> ModuleDefinition
17
+
18
+ /// Returns definitions synthesized from `@JS`-annotated members by the `@ExpoModule` macro.
19
+ /// Framework-internal: the leading underscore signals this is not part of the public API and
20
+ /// should only be called by `expo-modules-core` itself. Modules that don't use the macro fall
21
+ /// back to the default empty implementation.
22
+ func _exposedDefinition() -> [AnyDefinition]
23
+ }
24
+
25
+ public extension AnyModule {
26
+ func _exposedDefinition() -> [AnyDefinition] {
27
+ return []
28
+ }
17
29
  }
@@ -66,66 +66,87 @@ open class SharedObject: AnySharedObject {
66
66
  public func getJavaScriptObject() -> JavaScriptObject? {
67
67
  return appContext?.sharedObjectRegistry.toJavaScriptObject(self)
68
68
  }
69
- }
70
69
 
71
- // Unfortunately the `emit` function needs to be defined in the extension.
72
- // When put in the class, pack expansion is crashing with `EXC_BAD_ACCESS` code.
73
- // See https://github.com/apple/swift/issues/72381 for more details.
74
- public extension SharedObject { // swiftlint:disable:this no_grouping_extension
75
- // Parameter packs feature requires Swift 5.9 (Xcode 15.0), but some CIs and EAS images may still use older versions.
76
- // As of April 29, all submissions must be made with Xcode 15, so hopefully we can remove this condition soon.
77
- // No one should use <15.0 these days.
78
- #if swift(>=5.9)
79
70
  /**
80
- Schedules an event with the given name and arguments to be emitted to the associated JavaScript object.
71
+ Schedules an event with the given name and a pre-converted JavaScript payload to be emitted
72
+ to the associated JavaScript object. This is the lowest-level emit overload — use it when the
73
+ value is already a `JavaScriptValue` to skip the native-to-JS conversion step.
81
74
  */
82
- func emit<each A: AnyArgument>(event: String, arguments: repeat each A) {
75
+ public func emit(event: String, payload: JavaScriptValue) {
83
76
  guard let appContext, let runtime = try? appContext.runtime else {
84
77
  log.warn("Trying to send event '\(event)' to \(type(of: self)), but the JS runtime has been lost")
85
78
  return
86
79
  }
80
+ guard let jsValue = getJavaScriptValue() else {
81
+ log.warn("Trying to send event '\(event)' to JS, but the JS object is no longer associated with the native instance")
82
+ return
83
+ }
84
+ runtime.schedule {
85
+ dispatch(event: event, payload: payload, to: jsValue, in: runtime)
86
+ }
87
+ }
87
88
 
88
- // Collect arguments and their dynamic types from parameter pack
89
- var argumentPairs: [(AnyArgument, AnyDynamicType)] = []
90
- repeat argumentPairs.append((each arguments, ~(each A).self))
89
+ /**
90
+ Schedules an event with the given name to be emitted to the associated JavaScript object.
91
+ */
92
+ public func emit(event: String) {
93
+ emit(event: event, payload: .undefined)
94
+ }
91
95
 
92
- // Schedule the event to be asynchronously emitted from the runtime's thread
93
- runtime.schedule { [weak self, weak appContext] in
94
- guard let appContext, let runtime = try? appContext.runtime, let jsValue = self?.getJavaScriptValue() else {
95
- log.warn("Trying to send event '\(event)' to \(type(of: self)), but the JS object is no longer associated with the native instance")
96
+ /**
97
+ Schedules an event with the given name and payload to be emitted to the associated JavaScript object.
98
+ */
99
+ public func emit<P: AnyArgument>(event: String, payload: sending P) {
100
+ guard let appContext, let runtime = try? appContext.runtime else {
101
+ log.warn("Trying to send event '\(event)' to \(type(of: self)), but the JS runtime has been lost")
102
+ return
103
+ }
104
+ guard let jsValue = getJavaScriptValue() else {
105
+ log.warn("Trying to send event '\(event)' to JS, but the JS object is no longer associated with the native instance")
106
+ return
107
+ }
108
+ runtime.schedule { [weak appContext] in
109
+ guard let appContext else {
96
110
  return
97
111
  }
98
-
99
- // Convert native arguments to JS, just like function results
100
- let jsArguments: [JavaScriptValue]
101
112
  do {
102
- jsArguments = try argumentPairs.map { argument, dynamicType in
103
- try dynamicType.convertToJS(argument, appContext: appContext)
104
- }
113
+ let jsPayload = try (~P.self).castToJS(payload, appContext: appContext, in: runtime)
114
+ dispatch(event: event, payload: jsPayload, to: jsValue, in: runtime)
105
115
  } catch {
106
- log.warn("Failed to convert arguments for event '\(event)' on \(type(of: self)); the event will not be emitted: \(error)")
116
+ log.warn("Failed to convert payload for event '\(event)' on \(P.self); the event will not be emitted: \(error)")
107
117
  return
108
118
  }
119
+ }
120
+ }
109
121
 
110
- let argumentsBuffer = JavaScriptValuesBuffer.copying(in: runtime, values: jsArguments)
111
-
112
- runtime.withUnsafePointee { runtimePtr in
113
- jsValue.withUnsafePointee { objectPtr in
114
- JSUtils.emitEvent(
115
- event,
116
- runtimePointer: runtimePtr,
117
- objectPointer: objectPtr,
118
- argumentsPointer: argumentsBuffer.rawBaseAddress,
119
- argumentCount: UInt(argumentsBuffer.count)
120
- )
121
- }
122
+ /**
123
+ Backwards-compatible overload that forwards to `emit(event:payload:)`. Existing single-argument
124
+ call sites keep working unchanged; the parameter has been renamed to `payload` to make the
125
+ single-payload semantics explicit, so callers should migrate the label.
126
+ */
127
+ @available(*, deprecated, renamed: "emit(event:payload:)", message: "Use `emit(event:payload:)` and pass a single value (typically a dictionary). Multi-argument event emission is no longer supported.")
128
+ public func emit<P: AnyArgument>(event: String, arguments: sending P) {
129
+ emit(event: event, payload: arguments)
130
+ }
131
+ }
132
+
133
+ /**
134
+ Sends a pre-converted event payload to the given JavaScript object via the JSI emitter helper.
135
+ Must run on the JS thread; the public `emit` overloads schedule onto the runtime before calling in.
136
+ */
137
+ @JavaScriptActor
138
+ private func dispatch(event: String, payload: JavaScriptValue, to value: JavaScriptValue, in runtime: JavaScriptRuntime) {
139
+ runtime.withUnsafePointee { runtimePtr in
140
+ value.withUnsafePointee { objectPtr in
141
+ payload.withUnsafePointee { payloadPtr in
142
+ JSUtils.emitEvent(
143
+ event,
144
+ runtimePointer: runtimePtr,
145
+ objectPointer: objectPtr,
146
+ argumentsPointer: payloadPtr,
147
+ argumentCount: 1
148
+ )
122
149
  }
123
150
  }
124
151
  }
125
- #else // swift(>=5.9)
126
- @available(*, unavailable, message: "Unavailable in Xcode <15.0")
127
- public func emit(event: String, arguments: AnyArgument...) {
128
- fatalError("Emitting events to JS requires at least Xcode 15.0")
129
- }
130
- #endif // swift(<5.9)
131
152
  }
@@ -110,7 +110,7 @@ struct CdpNetwork {
110
110
 
111
111
  struct RequestWillBeSentExtraInfoParams: EventParams {
112
112
  let requestId: RequestId
113
- var associatedCookies = [String: String]()
113
+ var associatedCookies = [String]()
114
114
  let headers: Headers
115
115
  let connectTiming: ConnectTiming
116
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "56.0.7",
3
+ "version": "56.0.9",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -46,8 +46,8 @@
46
46
  "preset": "expo-module-scripts"
47
47
  },
48
48
  "dependencies": {
49
- "@expo/expo-modules-macros-plugin": "~0.0.8",
50
- "expo-modules-jsi": "~56.0.3",
49
+ "@expo/expo-modules-macros-plugin": "~0.0.9",
50
+ "expo-modules-jsi": "~56.0.5",
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": "40f0a6f6711d93762e0506b37e6e077e4bd9a541",
69
+ "gitHead": "f26be3dd9396bf7c399a1d607865d0fabdbc0d64",
70
70
  "scripts": {
71
71
  "build": "expo-module build",
72
72
  "clean": "expo-module clean",
@@ -100,9 +100,9 @@ export class SharedObject<TEventsMap extends Record<never, never>>
100
100
  }
101
101
 
102
102
  export class SharedRef<
103
- TNativeRefType extends string = 'unknown',
104
- TEventsMap extends EventsMap = Record<never, never>,
105
- >
103
+ TNativeRefType extends string = 'unknown',
104
+ TEventsMap extends EventsMap = Record<never, never>,
105
+ >
106
106
  extends SharedObject<TEventsMap>
107
107
  implements SharedRefType<TNativeRefType>
108
108
  {
@@ -11,9 +11,9 @@ import type { SharedObject } from './SharedObject';
11
11
  * directly to the image view from `expo-image` without any additional writes and reads from the file system.
12
12
  */
13
13
  export declare class SharedRef<
14
- TNativeRefType extends string = 'unknown',
15
- TEventsMap extends EventsMap = Record<never, never>,
16
- >
14
+ TNativeRefType extends string = 'unknown',
15
+ TEventsMap extends EventsMap = Record<never, never>,
16
+ >
17
17
  extends SharedObject<TEventsMap>
18
18
  implements SharedObject<TEventsMap>
19
19
  {