expo-modules-core 1.1.1 → 1.2.0

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 (52) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/android/CMakeLists.txt +9 -25
  3. package/android/ExpoModulesCorePlugin.gradle +0 -1
  4. package/android/build.gradle +13 -22
  5. package/android/legacy/CMakeLists.txt +2 -0
  6. package/android/src/fabric/CMakeLists.txt +12 -9
  7. package/android/src/main/cpp/JavaReferencesCache.cpp +6 -0
  8. package/android/src/main/cpp/JavaReferencesCache.h +20 -6
  9. package/android/src/main/cpp/JavaScriptModuleObject.cpp +9 -9
  10. package/android/src/main/cpp/JavaScriptModuleObject.h +5 -3
  11. package/android/src/main/cpp/JavaScriptRuntime.cpp +4 -0
  12. package/android/src/main/cpp/MethodMetadata.cpp +13 -2
  13. package/android/src/main/cpp/types/CppType.h +12 -11
  14. package/android/src/main/cpp/types/FrontendConverter.cpp +23 -0
  15. package/android/src/main/cpp/types/FrontendConverter.h +15 -0
  16. package/android/src/main/cpp/types/FrontendConverterProvider.cpp +1 -0
  17. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +3 -0
  18. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +8 -2
  19. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +35 -24
  20. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
  21. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +4 -0
  22. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +1 -4
  23. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +30 -0
  24. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +5 -1
  25. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +1 -0
  26. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +6 -0
  27. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +2 -0
  28. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +2 -0
  29. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +56 -6
  30. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +73 -1
  31. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +20 -16
  32. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +2 -2
  33. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  34. package/build/NativeViewManagerAdapter.native.js +19 -1
  35. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  36. package/build/SyntheticPlatformEmitter.d.ts +1 -1
  37. package/build/SyntheticPlatformEmitter.d.ts.map +1 -1
  38. package/build/SyntheticPlatformEmitter.js +1 -1
  39. package/build/SyntheticPlatformEmitter.js.map +1 -1
  40. package/ios/Fabric/ExpoFabricView.swift +6 -6
  41. package/ios/Fabric/ExpoFabricViewObjC.h +1 -1
  42. package/ios/Fabric/ExpoFabricViewObjC.mm +3 -9
  43. package/ios/Swift/ExpoBridgeModule.swift +23 -2
  44. package/ios/Swift/Views/ViewModuleWrapper.swift +7 -9
  45. package/package.json +2 -2
  46. package/src/NativeViewManagerAdapter.native.tsx +23 -2
  47. package/src/SyntheticPlatformEmitter.ts +1 -1
  48. package/build/SyntheticPlatformEmitter.web.d.ts +0 -6
  49. package/build/SyntheticPlatformEmitter.web.d.ts.map +0 -1
  50. package/build/SyntheticPlatformEmitter.web.js +0 -6
  51. package/build/SyntheticPlatformEmitter.web.js.map +0 -1
  52. package/src/SyntheticPlatformEmitter.web.ts +0 -5
@@ -1,6 +1,5 @@
1
1
  package expo.modules.kotlin.modules
2
2
 
3
- import expo.modules.kotlin.ConcatIterator
4
3
  import expo.modules.kotlin.activityresult.AppContextActivityResultCaller
5
4
  import expo.modules.kotlin.events.EventListener
6
5
  import expo.modules.kotlin.events.EventName
@@ -20,7 +19,5 @@ class ModuleDefinitionData(
20
19
  val asyncFunctions = objectDefinition.asyncFunctions
21
20
  val eventsDefinition = objectDefinition.eventsDefinition
22
21
  val properties = objectDefinition.properties
23
-
24
- val functions
25
- get() = ConcatIterator(syncFunctions.values.iterator(), asyncFunctions.values.iterator())
22
+ val functions = objectDefinition.functions
26
23
  }
@@ -14,6 +14,7 @@
14
14
 
15
15
  package expo.modules.kotlin.objects
16
16
 
17
+ import com.facebook.react.bridge.Arguments
17
18
  import expo.modules.kotlin.Promise
18
19
  import expo.modules.kotlin.events.EventsDefinition
19
20
  import expo.modules.kotlin.functions.AsyncFunction
@@ -21,6 +22,9 @@ import expo.modules.kotlin.functions.AsyncFunctionBuilder
21
22
  import expo.modules.kotlin.functions.AsyncFunctionComponent
22
23
  import expo.modules.kotlin.functions.AsyncFunctionWithPromiseComponent
23
24
  import expo.modules.kotlin.functions.SyncFunctionComponent
25
+ import expo.modules.kotlin.jni.JavaScriptModuleObject
26
+ import expo.modules.kotlin.modules.Module
27
+ import expo.modules.kotlin.modules.ModuleDefinitionBuilder
24
28
  import expo.modules.kotlin.types.toAnyType
25
29
  import kotlin.reflect.typeOf
26
30
 
@@ -350,3 +354,29 @@ open class ObjectDefinitionBuilder {
350
354
  }
351
355
  }
352
356
  }
357
+
358
+ inline fun ModuleDefinitionBuilder.Object(block: ObjectDefinitionBuilder.() -> Unit): JavaScriptModuleObject {
359
+ return module!!.Object(block)
360
+ }
361
+
362
+ inline fun Module.Object(block: ObjectDefinitionBuilder.() -> Unit): JavaScriptModuleObject {
363
+ val objectData = ObjectDefinitionBuilder().also(block).buildObject()
364
+ return JavaScriptModuleObject("[Anonymous Object]")
365
+ .apply {
366
+ val constants = objectData.constantsProvider()
367
+ val convertedConstants = Arguments.makeNativeMap(constants)
368
+ exportConstants(convertedConstants)
369
+
370
+ objectData
371
+ .functions
372
+ .forEach { function ->
373
+ function.attachToJSObject(appContext, this)
374
+ }
375
+
376
+ objectData
377
+ .properties
378
+ .forEach { (_, prop) ->
379
+ prop.attachToJSObject(this)
380
+ }
381
+ }
382
+ }
@@ -1,5 +1,6 @@
1
1
  package expo.modules.kotlin.objects
2
2
 
3
+ import expo.modules.kotlin.ConcatIterator
3
4
  import expo.modules.kotlin.events.EventsDefinition
4
5
  import expo.modules.kotlin.functions.BaseAsyncFunctionComponent
5
6
  import expo.modules.kotlin.functions.SyncFunctionComponent
@@ -10,4 +11,7 @@ class ObjectDefinitionData(
10
11
  val asyncFunctions: Map<String, BaseAsyncFunctionComponent>,
11
12
  val eventsDefinition: EventsDefinition?,
12
13
  val properties: Map<String, PropertyComponent>
13
- )
14
+ ) {
15
+ val functions
16
+ get() = ConcatIterator(syncFunctions.values.iterator(), asyncFunctions.values.iterator())
17
+ }
@@ -39,6 +39,7 @@ object JSTypeConverter {
39
39
  is Uri -> value.toJSValue()
40
40
  is File -> value.toJSValue()
41
41
  is Pair<*, *> -> value.toJSValue(containerProvider)
42
+ is Long -> value.toDouble()
42
43
  else -> value
43
44
  }
44
45
  }
@@ -135,6 +135,9 @@ object TypeConverterProviderImpl : TypeConverterProvider {
135
135
  val intTypeConverter = createTrivialTypeConverter(
136
136
  isOptional, ExpectedType(CppType.INT)
137
137
  ) { it.asDouble().toInt() }
138
+ val longTypeConverter = createTrivialTypeConverter(
139
+ isOptional, ExpectedType(CppType.LONG)
140
+ ) { it.asDouble().toLong() }
138
141
  val doubleTypeConverter = createTrivialTypeConverter(
139
142
  isOptional, ExpectedType(CppType.DOUBLE)
140
143
  ) { it.asDouble() }
@@ -149,6 +152,9 @@ object TypeConverterProviderImpl : TypeConverterProvider {
149
152
  Int::class.createType(nullable = isOptional) to intTypeConverter,
150
153
  java.lang.Integer::class.createType(nullable = isOptional) to intTypeConverter,
151
154
 
155
+ Long::class.createType(nullable = isOptional) to longTypeConverter,
156
+ java.lang.Long::class.createType(nullable = isOptional) to longTypeConverter,
157
+
152
158
  Double::class.createType(nullable = isOptional) to doubleTypeConverter,
153
159
  java.lang.Double::class.createType(nullable = isOptional) to doubleTypeConverter,
154
160
 
@@ -7,4 +7,6 @@ abstract class AnyViewProp(
7
7
  val name: String
8
8
  ) {
9
9
  abstract fun set(prop: Dynamic, onView: View)
10
+
11
+ abstract val isNullable: Boolean
10
12
  }
@@ -20,4 +20,6 @@ class ConcreteViewProp<ViewType : View, PropType>(
20
20
  setter(onView as ViewType, propType.convert(prop) as PropType)
21
21
  }
22
22
  }
23
+
24
+ override val isNullable: Boolean = propType.kType.isMarkedNullable
23
25
  }
@@ -5,15 +5,17 @@ package expo.modules.kotlin.views
5
5
 
6
6
  import android.content.Context
7
7
  import android.view.View
8
+ import android.view.ViewGroup
8
9
  import expo.modules.kotlin.AppContext
9
10
  import expo.modules.kotlin.modules.DefinitionMarker
10
11
  import expo.modules.kotlin.types.toAnyType
11
12
  import kotlin.reflect.KClass
13
+ import kotlin.reflect.KFunction
12
14
  import kotlin.reflect.full.primaryConstructor
13
15
  import kotlin.reflect.typeOf
14
16
 
15
17
  @DefinitionMarker
16
- class ViewDefinitionBuilder<T : View>(private val viewType: KClass<T>) {
18
+ class ViewDefinitionBuilder<T : View>(@PublishedApi internal val viewType: KClass<T>) {
17
19
  @PublishedApi
18
20
  internal var props = mutableMapOf<String, AnyViewProp>()
19
21
 
@@ -41,20 +43,57 @@ class ViewDefinitionBuilder<T : View>(private val viewType: KClass<T>) {
41
43
  /**
42
44
  * Creates view's lifecycle listener that is called right after the view isn't longer used by React Native.
43
45
  */
44
- inline fun <reified ViewType : View> OnViewDestroys(noinline body: (view: ViewType) -> Unit) {
46
+ @Suppress("UNCHECKED_CAST")
47
+ inline fun OnViewDestroys(crossinline body: (view: T) -> Unit) {
48
+ onViewDestroys = {
49
+ body(it as T)
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Creates view's lifecycle listener that is called right after the view isn't longer used by React Native.
55
+ */
56
+ @JvmName("OnViewDestroysGeneric")
57
+ inline fun <reified ViewType : T> OnViewDestroys(noinline body: (view: ViewType) -> Unit) {
45
58
  onViewDestroys = { body(it as ViewType) }
46
59
  }
47
60
 
48
61
  /**
49
62
  * Defines the view lifecycle method that is called when the view finished updating all props.
50
63
  */
51
- inline fun <reified ViewType : View> OnViewDidUpdateProps(noinline body: (view: ViewType) -> Unit) {
64
+ @Suppress("UNCHECKED_CAST")
65
+ inline fun OnViewDidUpdateProps(crossinline body: (view: T) -> Unit) {
66
+ onViewDidUpdateProps = {
67
+ body(it as T)
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Defines the view lifecycle method that is called when the view finished updating all props.
73
+ */
74
+ @JvmName("OnViewDidUpdatePropsGeneric")
75
+ inline fun <reified ViewType : T> OnViewDidUpdateProps(noinline body: (view: ViewType) -> Unit) {
52
76
  onViewDidUpdateProps = { body(it as ViewType) }
53
77
  }
54
78
 
55
79
  /**
56
80
  * Creates a view prop that defines its name and setter.
57
81
  */
82
+ inline fun <reified PropType> Prop(
83
+ name: String,
84
+ noinline body: (view: T, prop: PropType) -> Unit
85
+ ) {
86
+ props[name] = ConcreteViewProp(
87
+ name,
88
+ typeOf<PropType>().toAnyType(),
89
+ body
90
+ )
91
+ }
92
+
93
+ /**
94
+ * Creates a view prop that defines its name and setter.
95
+ */
96
+ @JvmName("PropGeneric")
58
97
  inline fun <reified ViewType : View, reified PropType> Prop(
59
98
  name: String,
60
99
  noinline body: (view: ViewType, prop: PropType) -> Unit
@@ -84,16 +123,17 @@ class ViewDefinitionBuilder<T : View>(private val viewType: KClass<T>) {
84
123
  /**
85
124
  * Creates the group view definition that scopes group view-related definitions.
86
125
  */
87
- inline fun GroupView(body: ViewGroupDefinitionBuilder.() -> Unit) {
126
+ inline fun <reified ParentType : ViewGroup> GroupView(body: ViewGroupDefinitionBuilder<ParentType>.() -> Unit) {
127
+ assert(viewType == ParentType::class) { "Provided type and view type have to be the same." }
88
128
  require(viewGroupDefinition == null) { "The viewManager definition may have exported only one groupView definition." }
89
129
 
90
- val groupViewDefinitionBuilder = ViewGroupDefinitionBuilder()
130
+ val groupViewDefinitionBuilder = ViewGroupDefinitionBuilder<ParentType>()
91
131
  body.invoke(groupViewDefinitionBuilder)
92
132
  viewGroupDefinition = groupViewDefinitionBuilder.build()
93
133
  }
94
134
 
95
135
  private fun createViewFactory(): (Context, AppContext) -> View = viewFactory@{ context: Context, appContext: AppContext ->
96
- val primaryConstructor = requireNotNull(viewType.primaryConstructor) { "$viewType doesn't have a primary constructor" }
136
+ val primaryConstructor = requireNotNull(getPrimaryConstructor()) { "$viewType doesn't have a primary constructor" }
97
137
  val args = primaryConstructor.parameters
98
138
 
99
139
  if (args.isEmpty()) {
@@ -121,4 +161,14 @@ class ViewDefinitionBuilder<T : View>(private val viewType: KClass<T>) {
121
161
 
122
162
  return@viewFactory primaryConstructor.call(context, appContext)
123
163
  }
164
+
165
+ private fun getPrimaryConstructor(): KFunction<T>? {
166
+ val kotlinContractor = viewType.primaryConstructor
167
+ if (kotlinContractor != null) {
168
+ return kotlinContractor
169
+ }
170
+
171
+ // Add compatibility with Java
172
+ return viewType.constructors.firstOrNull()
173
+ }
124
174
  }
@@ -7,7 +7,79 @@ import android.view.ViewGroup
7
7
  import expo.modules.kotlin.modules.DefinitionMarker
8
8
 
9
9
  @DefinitionMarker
10
- class ViewGroupDefinitionBuilder {
10
+ class ViewGroupDefinitionBuilder<ParentType : ViewGroup> {
11
+ @PublishedApi
12
+ internal var addViewAction: AddViewAction? = null
13
+
14
+ @PublishedApi
15
+ internal var getChildAtAction: GetChildAtAction? = null
16
+
17
+ @PublishedApi
18
+ internal var getChildCountAction: GetChildCountAction? = null
19
+
20
+ @PublishedApi
21
+ internal var removeViewAction: RemoveViewAction? = null
22
+
23
+ @PublishedApi
24
+ internal var removeViewAtAction: RemoveViewAtAction? = null
25
+
26
+ fun build() = ViewGroupDefinition(
27
+ addViewAction,
28
+ getChildAtAction,
29
+ getChildCountAction,
30
+ removeViewAction,
31
+ removeViewAtAction
32
+ )
33
+
34
+ @Suppress("UNCHECKED_CAST")
35
+ inline fun <reified ChildViewType : View> AddChildView(
36
+ crossinline body: (parent: ParentType, child: ChildViewType, index: Int) -> Unit
37
+ ) {
38
+ addViewAction = { parent, child, index ->
39
+ body(parent as ParentType, child as ChildViewType, index)
40
+ }
41
+ }
42
+
43
+ @Suppress("UNCHECKED_CAST")
44
+ inline fun GetChildCount(
45
+ crossinline body: (view: ParentType) -> Int
46
+ ) {
47
+ getChildCountAction = { view ->
48
+ body(view as ParentType)
49
+ }
50
+ }
51
+
52
+ @Suppress("UNCHECKED_CAST")
53
+ inline fun <reified ChildViewType : View> GetChildViewAt(
54
+ crossinline body: (view: ParentType, index: Int) -> ChildViewType?
55
+ ) {
56
+ getChildAtAction = { view, index ->
57
+ body(view as ParentType, index)
58
+ }
59
+ }
60
+
61
+ @Suppress("UNCHECKED_CAST")
62
+ inline fun RemoveChildViewAt(
63
+ crossinline body: (view: ParentType, index: Int) -> Unit
64
+ ) {
65
+ removeViewAtAction = { view, index ->
66
+ body(view as ParentType, index)
67
+ }
68
+ }
69
+
70
+ @Suppress("UNCHECKED_CAST")
71
+ inline fun <reified ChildViewType : View> RemoveChildView(
72
+ noinline body: (parent: ParentType, child: ChildViewType) -> Unit
73
+ ) {
74
+ removeViewAction = { view, child ->
75
+ body(view as ParentType, child as ChildViewType)
76
+ }
77
+ }
78
+ }
79
+
80
+ @Deprecated("Use `ViewGroupDefinitionBuilder` instead.")
81
+ @DefinitionMarker
82
+ class ViewGroupDefinitionLegacyBuilder {
11
83
  @PublishedApi
12
84
  internal var addViewAction: AddViewAction? = null
13
85
 
@@ -8,6 +8,7 @@ import com.facebook.react.bridge.ReadableMap
8
8
  import expo.modules.adapters.react.NativeModulesProxy
9
9
  import expo.modules.core.ViewManager
10
10
  import expo.modules.kotlin.AppContext
11
+ import expo.modules.kotlin.DynamicNull
11
12
  import expo.modules.kotlin.exception.CodedException
12
13
  import expo.modules.kotlin.exception.UnexpectedException
13
14
  import expo.modules.kotlin.logger
@@ -36,24 +37,27 @@ class ViewManagerDefinition(
36
37
  }
37
38
 
38
39
  fun setProps(propsToSet: ReadableMap, onView: View) {
39
- val iterator = propsToSet.keySetIterator()
40
- while (iterator.hasNextKey()) {
41
- val key = iterator.nextKey()
42
- val propDelegate = props[key] ?: continue
43
- propsToSet.getDynamic(key).recycle {
44
- try {
45
- propDelegate.set(this, onView)
46
- } catch (exception: Throwable) {
47
- logger.error("❌ Cannot set the '$key' prop on the '${viewType.simpleName}'", exception)
48
-
49
- handleException(
50
- onView,
51
- when (exception) {
52
- is CodedException -> exception
53
- else -> UnexpectedException(exception)
40
+ props.forEach { (name, propDelegate) ->
41
+ try {
42
+ if (propsToSet.hasKey(name)) {
43
+ propsToSet
44
+ .getDynamic(name)
45
+ .recycle {
46
+ propDelegate.set(this, onView)
54
47
  }
55
- )
48
+ } else if (propDelegate.isNullable) {
49
+ propDelegate.set(DynamicNull, onView)
56
50
  }
51
+ } catch (exception: Throwable) {
52
+ logger.error("❌ Cannot set the '$name' prop on the '${viewType.simpleName}'", exception)
53
+
54
+ handleException(
55
+ onView,
56
+ when (exception) {
57
+ is CodedException -> exception
58
+ else -> UnexpectedException(exception)
59
+ }
60
+ )
57
61
  }
58
62
  }
59
63
  }
@@ -122,10 +122,10 @@ class ViewManagerDefinitionBuilder {
122
122
  /**
123
123
  * Creates the group view definition that scopes group view-related definitions.
124
124
  */
125
- inline fun GroupView(body: ViewGroupDefinitionBuilder.() -> Unit) {
125
+ inline fun GroupView(body: ViewGroupDefinitionLegacyBuilder.() -> Unit) {
126
126
  require(viewGroupDefinition == null) { "The viewManager definition may have exported only one groupView definition." }
127
127
 
128
- val groupViewDefinitionBuilder = ViewGroupDefinitionBuilder()
128
+ val groupViewDefinitionBuilder = ViewGroupDefinitionLegacyBuilder()
129
129
  body.invoke(groupViewDefinitionBuilder)
130
130
  viewGroupDefinition = groupViewDefinitionBuilder.build()
131
131
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NativeViewManagerAdapter.native.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAgB1B;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CA0BpF"}
1
+ {"version":3,"file":"NativeViewManagerAdapter.native.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAqC1B;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CA0BpF"}
@@ -1,5 +1,23 @@
1
1
  import React from 'react';
2
2
  import { NativeModules, requireNativeComponent } from 'react-native';
3
+ /**
4
+ * A map that caches registered native components.
5
+ */
6
+ const nativeComponentsCache = new Map();
7
+ /**
8
+ * Requires a React Native component from cache if possible. This prevents
9
+ * "Tried to register two views with the same name" errors on fast refresh, but
10
+ * also when there are multiple versions of the same package with native component.
11
+ */
12
+ function requireCachedNativeComponent(viewName) {
13
+ const cachedNativeComponent = nativeComponentsCache.get(viewName);
14
+ if (!cachedNativeComponent) {
15
+ const nativeComponent = requireNativeComponent(viewName);
16
+ nativeComponentsCache.set(viewName, nativeComponent);
17
+ return nativeComponent;
18
+ }
19
+ return cachedNativeComponent;
20
+ }
3
21
  /**
4
22
  * A drop-in replacement for `requireNativeComponent`.
5
23
  */
@@ -13,7 +31,7 @@ export function requireNativeViewManager(viewName) {
13
31
  // Set up the React Native native component, which is an adapter to the universal module's view
14
32
  // manager
15
33
  const reactNativeViewName = `ViewManagerAdapter_${viewName}`;
16
- const ReactNativeComponent = requireNativeComponent(reactNativeViewName);
34
+ const ReactNativeComponent = requireCachedNativeComponent(reactNativeViewName);
17
35
  const proxiedPropsNames = viewManagerConfig?.propsNames ?? [];
18
36
  // Define a component for universal-module authors to access their native view manager
19
37
  const NativeComponentAdapter = React.forwardRef((props, ref) => {
@@ -1 +1 @@
1
- {"version":3,"file":"NativeViewManagerAdapter.native.js","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAerE;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAI,QAAgB;IAC1D,MAAM,EAAE,oBAAoB,EAAE,GAAG,aAAa,CAAC,oBAAoB,CAAC;IACpE,MAAM,iBAAiB,GAAG,oBAAoB,EAAE,CAAC,QAAQ,CAAC,CAAC;IAE3D,IAAI,OAAO,IAAI,CAAC,iBAAiB,EAAE;QACjC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CACV,6CAA6C,QAAQ,8IAA8I,wBAAwB,IAAI,CAChO,CAAC;KACH;IAED,+FAA+F;IAC/F,UAAU;IACV,MAAM,mBAAmB,GAAG,sBAAsB,QAAQ,EAAE,CAAC;IAC7D,MAAM,oBAAoB,GACxB,sBAAsB,CAA2B,mBAAmB,CAAC,CAAC;IACxE,MAAM,iBAAiB,GAAG,iBAAiB,EAAE,UAAU,IAAI,EAAE,CAAC;IAE9D,sFAAsF;IACtF,MAAM,sBAAsB,GAAG,KAAK,CAAC,UAAU,CAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAClE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACpD,OAAO,oBAAC,oBAAoB,OAAK,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,GAAI,CAAC;IAC9F,CAAC,CAA2B,CAAC;IAC7B,sBAAsB,CAAC,WAAW,GAAG,WAAW,QAAQ,GAAG,CAAC;IAC5D,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED,SAAS,IAAI,CAAC,KAA0B,EAAE,SAAmB;IAC3D,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IAC5B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;QAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;KACzB;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,IAAI,CAAC,KAA0B,EAAE,SAAmB;IAC3D,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QACrC,IAAI,IAAI,IAAI,KAAK,EAAE;YACjB,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;SAC1B;QACD,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC","sourcesContent":["import React from 'react';\nimport { NativeModules, requireNativeComponent } from 'react-native';\n\n// To make the transition from React Native's `requireNativeComponent` to Expo's\n// `requireNativeViewManager` as easy as possible, `requireNativeViewManager` is a drop-in\n// replacement for `requireNativeComponent`.\n//\n// For each view manager, we create a wrapper component that accepts all of the props available to\n// the author of the universal module. This wrapper component splits the props into two sets: props\n// passed to React Native's View (ex: style, testID) and custom view props, which are passed to the\n// adapter view component in a prop called `proxiedProperties`.\n\ntype NativeExpoComponentProps = {\n proxiedProperties: object;\n};\n\n/**\n * A drop-in replacement for `requireNativeComponent`.\n */\nexport function requireNativeViewManager<P>(viewName: string): React.ComponentType<P> {\n const { viewManagersMetadata } = NativeModules.NativeUnimoduleProxy;\n const viewManagerConfig = viewManagersMetadata?.[viewName];\n\n if (__DEV__ && !viewManagerConfig) {\n const exportedViewManagerNames = Object.keys(viewManagersMetadata).join(', ');\n console.warn(\n `The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by expo-modules-core. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`\n );\n }\n\n // Set up the React Native native component, which is an adapter to the universal module's view\n // manager\n const reactNativeViewName = `ViewManagerAdapter_${viewName}`;\n const ReactNativeComponent =\n requireNativeComponent<NativeExpoComponentProps>(reactNativeViewName);\n const proxiedPropsNames = viewManagerConfig?.propsNames ?? [];\n\n // Define a component for universal-module authors to access their native view manager\n const NativeComponentAdapter = React.forwardRef<any>((props, ref) => {\n const nativeProps = omit(props, proxiedPropsNames);\n const proxiedProps = pick(props, proxiedPropsNames);\n return <ReactNativeComponent {...nativeProps} proxiedProperties={proxiedProps} ref={ref} />;\n }) as React.ComponentType<P>;\n NativeComponentAdapter.displayName = `Adapter<${viewName}>`;\n return NativeComponentAdapter;\n}\n\nfunction omit(props: Record<string, any>, propNames: string[]) {\n const copied = { ...props };\n for (const propName of propNames) {\n delete copied[propName];\n }\n return copied;\n}\n\nfunction pick(props: Record<string, any>, propNames: string[]) {\n return propNames.reduce((prev, curr) => {\n if (curr in props) {\n prev[curr] = props[curr];\n }\n return prev;\n }, {});\n}\n"]}
1
+ {"version":3,"file":"NativeViewManagerAdapter.native.js","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAiB,MAAM,cAAc,CAAC;AAepF;;GAEG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAA8B,CAAC;AAEpE;;;;GAIG;AACH,SAAS,4BAA4B,CAAQ,QAAgB;IAC3D,MAAM,qBAAqB,GAAG,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAElE,IAAI,CAAC,qBAAqB,EAAE;QAC1B,MAAM,eAAe,GAAG,sBAAsB,CAAQ,QAAQ,CAAC,CAAC;QAChE,qBAAqB,CAAC,GAAG,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;QACrD,OAAO,eAAe,CAAC;KACxB;IACD,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAI,QAAgB;IAC1D,MAAM,EAAE,oBAAoB,EAAE,GAAG,aAAa,CAAC,oBAAoB,CAAC;IACpE,MAAM,iBAAiB,GAAG,oBAAoB,EAAE,CAAC,QAAQ,CAAC,CAAC;IAE3D,IAAI,OAAO,IAAI,CAAC,iBAAiB,EAAE;QACjC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CACV,6CAA6C,QAAQ,8IAA8I,wBAAwB,IAAI,CAChO,CAAC;KACH;IAED,+FAA+F;IAC/F,UAAU;IACV,MAAM,mBAAmB,GAAG,sBAAsB,QAAQ,EAAE,CAAC;IAC7D,MAAM,oBAAoB,GACxB,4BAA4B,CAA2B,mBAAmB,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,iBAAiB,EAAE,UAAU,IAAI,EAAE,CAAC;IAE9D,sFAAsF;IACtF,MAAM,sBAAsB,GAAG,KAAK,CAAC,UAAU,CAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAClE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACpD,OAAO,oBAAC,oBAAoB,OAAK,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,GAAI,CAAC;IAC9F,CAAC,CAA2B,CAAC;IAC7B,sBAAsB,CAAC,WAAW,GAAG,WAAW,QAAQ,GAAG,CAAC;IAC5D,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED,SAAS,IAAI,CAAC,KAA0B,EAAE,SAAmB;IAC3D,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IAC5B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;QAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;KACzB;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,IAAI,CAAC,KAA0B,EAAE,SAAmB;IAC3D,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QACrC,IAAI,IAAI,IAAI,KAAK,EAAE;YACjB,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;SAC1B;QACD,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC","sourcesContent":["import React from 'react';\nimport { NativeModules, requireNativeComponent, HostComponent } from 'react-native';\n\n// To make the transition from React Native's `requireNativeComponent` to Expo's\n// `requireNativeViewManager` as easy as possible, `requireNativeViewManager` is a drop-in\n// replacement for `requireNativeComponent`.\n//\n// For each view manager, we create a wrapper component that accepts all of the props available to\n// the author of the universal module. This wrapper component splits the props into two sets: props\n// passed to React Native's View (ex: style, testID) and custom view props, which are passed to the\n// adapter view component in a prop called `proxiedProperties`.\n\ntype NativeExpoComponentProps = {\n proxiedProperties: object;\n};\n\n/**\n * A map that caches registered native components.\n */\nconst nativeComponentsCache = new Map<string, HostComponent<any>>();\n\n/**\n * Requires a React Native component from cache if possible. This prevents\n * \"Tried to register two views with the same name\" errors on fast refresh, but\n * also when there are multiple versions of the same package with native component.\n */\nfunction requireCachedNativeComponent<Props>(viewName: string): HostComponent<Props> {\n const cachedNativeComponent = nativeComponentsCache.get(viewName);\n\n if (!cachedNativeComponent) {\n const nativeComponent = requireNativeComponent<Props>(viewName);\n nativeComponentsCache.set(viewName, nativeComponent);\n return nativeComponent;\n }\n return cachedNativeComponent;\n}\n\n/**\n * A drop-in replacement for `requireNativeComponent`.\n */\nexport function requireNativeViewManager<P>(viewName: string): React.ComponentType<P> {\n const { viewManagersMetadata } = NativeModules.NativeUnimoduleProxy;\n const viewManagerConfig = viewManagersMetadata?.[viewName];\n\n if (__DEV__ && !viewManagerConfig) {\n const exportedViewManagerNames = Object.keys(viewManagersMetadata).join(', ');\n console.warn(\n `The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by expo-modules-core. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`\n );\n }\n\n // Set up the React Native native component, which is an adapter to the universal module's view\n // manager\n const reactNativeViewName = `ViewManagerAdapter_${viewName}`;\n const ReactNativeComponent =\n requireCachedNativeComponent<NativeExpoComponentProps>(reactNativeViewName);\n const proxiedPropsNames = viewManagerConfig?.propsNames ?? [];\n\n // Define a component for universal-module authors to access their native view manager\n const NativeComponentAdapter = React.forwardRef<any>((props, ref) => {\n const nativeProps = omit(props, proxiedPropsNames);\n const proxiedProps = pick(props, proxiedPropsNames);\n return <ReactNativeComponent {...nativeProps} proxiedProperties={proxiedProps} ref={ref} />;\n }) as React.ComponentType<P>;\n NativeComponentAdapter.displayName = `Adapter<${viewName}>`;\n return NativeComponentAdapter;\n}\n\nfunction omit(props: Record<string, any>, propNames: string[]) {\n const copied = { ...props };\n for (const propName of propNames) {\n delete copied[propName];\n }\n return copied;\n}\n\nfunction pick(props: Record<string, any>, propNames: string[]) {\n return propNames.reduce((prev, curr) => {\n if (curr in props) {\n prev[curr] = props[curr];\n }\n return prev;\n }, {});\n}\n"]}
@@ -2,5 +2,5 @@
2
2
  * This emitter is used for sending synthetic native events to listeners
3
3
  * registered in the API layer with `NativeEventEmitter`.
4
4
  */
5
- export { default } from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';
5
+ export { DeviceEventEmitter as default } from 'react-native';
6
6
  //# sourceMappingURL=SyntheticPlatformEmitter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SyntheticPlatformEmitter.d.ts","sourceRoot":"","sources":["../src/SyntheticPlatformEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,2DAA2D,CAAC"}
1
+ {"version":3,"file":"SyntheticPlatformEmitter.d.ts","sourceRoot":"","sources":["../src/SyntheticPlatformEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -2,5 +2,5 @@
2
2
  * This emitter is used for sending synthetic native events to listeners
3
3
  * registered in the API layer with `NativeEventEmitter`.
4
4
  */
5
- export { default } from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';
5
+ export { DeviceEventEmitter as default } from 'react-native';
6
6
  //# sourceMappingURL=SyntheticPlatformEmitter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SyntheticPlatformEmitter.js","sourceRoot":"","sources":["../src/SyntheticPlatformEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,2DAA2D,CAAC","sourcesContent":["/**\n * This emitter is used for sending synthetic native events to listeners\n * registered in the API layer with `NativeEventEmitter`.\n */\nexport { default } from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';\n"]}
1
+ {"version":3,"file":"SyntheticPlatformEmitter.js","sourceRoot":"","sources":["../src/SyntheticPlatformEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,IAAI,OAAO,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * This emitter is used for sending synthetic native events to listeners\n * registered in the API layer with `NativeEventEmitter`.\n */\nexport { DeviceEventEmitter as default } from 'react-native';\n"]}
@@ -51,17 +51,17 @@ public class ExpoFabricView: ExpoFabricViewObjC {
51
51
 
52
52
  // MARK: - ExpoFabricViewInterface
53
53
 
54
- public override func updateProp(_ propName: String, withValue value: Any) {
55
- guard let view = contentView else {
54
+ public override func updateProps(_ props: [String: Any]) {
55
+ guard let view = contentView, let propsDict = viewManagerPropDict else {
56
56
  return
57
57
  }
58
- if let _ = moduleHolder, let prop = viewManagerPropDict?[propName] {
58
+ for (key, prop) in propsDict {
59
+ let newValue = props[key] as Any
60
+
59
61
  // TODO: @tsapeta: Figure out better way to rethrow errors from here.
60
62
  // Adding `throws` keyword to the function results in different
61
63
  // method signature in Objective-C. Maybe just call `RCTLogError`?
62
- try? prop.set(value: value, onView: view)
63
- } else if let _ = legacyViewManager {
64
- legacyViewManager?.updateProp(propName, withValue: value, on: view)
64
+ try? prop.set(value: Conversions.fromNSObject(newValue), onView: view)
65
65
  }
66
66
  }
67
67
 
@@ -33,7 +33,7 @@
33
33
 
34
34
  - (void)dispatchEvent:(nonnull NSString *)eventName payload:(nullable id)payload;
35
35
 
36
- - (void)updateProp:(nonnull NSString *)propName withValue:(nonnull id)value;
36
+ - (void)updateProps:(nonnull NSDictionary<NSString *, id> *)props;
37
37
 
38
38
  - (void)viewDidUpdateProps;
39
39
 
@@ -140,15 +140,9 @@ static std::unordered_map<std::string, ExpoViewComponentDescriptor::Flavor> _com
140
140
  - (void)updateProps:(const facebook::react::Props::Shared &)props oldProps:(const facebook::react::Props::Shared &)oldProps
141
141
  {
142
142
  const auto &newViewProps = *std::static_pointer_cast<ExpoViewProps const>(props);
143
- auto proxiedProperties = newViewProps.proxiedProperties;
144
- if (proxiedProperties.isObject()) {
145
- for (auto& item : proxiedProperties.items()) {
146
- NSString *name = [NSString stringWithCString:item.first.c_str() encoding:NSUTF8StringEncoding];
147
- id value = convertFollyDynamicToId(item.second);
148
- [self updateProp:name withValue:value];
149
- }
150
- }
143
+ NSDictionary<NSString *, id> *proxiedProperties = convertFollyDynamicToId(newViewProps.proxiedProperties);
151
144
 
145
+ [self updateProps:proxiedProperties];
152
146
  [super updateProps:props oldProps:oldProps];
153
147
  [self viewDidUpdateProps];
154
148
  }
@@ -170,7 +164,7 @@ static std::unordered_map<std::string, ExpoViewComponentDescriptor::Flavor> _com
170
164
 
171
165
  #pragma mark - Methods to override in Swift
172
166
 
173
- - (void)updateProp:(nonnull NSString *)propName withValue:(nonnull id)value
167
+ - (void)updateProps:(nonnull NSDictionary<NSString *, id> *)props
174
168
  {
175
169
  // Implemented in `ExpoFabricView.swift`
176
170
  }
@@ -41,12 +41,33 @@ public final class ExpoBridgeModule: NSObject, RCTBridgeModule {
41
41
  public var bridge: RCTBridge! {
42
42
  didSet {
43
43
  appContext.reactBridge = bridge
44
- bridge.dispatchBlock({ [weak self] in
44
+
45
+ let attachRuntime = { [weak self] in
45
46
  guard let self = self, let bridge = self.appContext.reactBridge else {
46
47
  return
47
48
  }
48
49
  self.appContext.runtime = EXJavaScriptRuntimeManager.runtime(fromBridge: bridge)
49
- }, queue: RCTJSThread)
50
+ }
51
+
52
+ if bridge.responds(to: Selector(("runtime"))) {
53
+ // Getting the `runtime` on a different thread than JS is considered to be dangerous.
54
+ // However, we just checking if it exists. We don't do anything with it.
55
+ let result = bridge.perform(Selector(("runtime")))
56
+ if result == nil {
57
+ // When exporting expo modules using `extraModulesForBridge` (e.g. in Expo Go),
58
+ // the runtime won't be initiated before the bridge didSet method is called.
59
+ // Therefore, we need to wait for the main thread to complete its initialization by dispatching on it first.
60
+ bridge.dispatchBlock({ [weak self] in
61
+ guard let self = self, let bridge = self.appContext.reactBridge else {
62
+ return
63
+ }
64
+ bridge.dispatchBlock(attachRuntime, queue: RCTJSThread)
65
+ }, queue: DispatchQueue.main)
66
+ return
67
+ }
68
+ }
69
+
70
+ bridge.dispatchBlock(attachRuntime, queue: RCTJSThread)
50
71
  }
51
72
  }
52
73
 
@@ -109,15 +109,13 @@ public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtoc
109
109
  }
110
110
  let props = viewManager.propsDict()
111
111
 
112
- for (key, value) in json {
113
- if let prop = props[key] {
114
- let value = Conversions.fromNSObject(value)
115
-
116
- // TODO: @tsapeta: Figure out better way to rethrow errors from here.
117
- // Adding `throws` keyword to the function results in different
118
- // method signature in Objective-C. Maybe just call `RCTLogError`?
119
- try? prop.set(value: value, onView: view)
120
- }
112
+ for (key, prop) in props {
113
+ let newValue = json[key] as Any
114
+
115
+ // TODO: @tsapeta: Figure out better way to rethrow errors from here.
116
+ // Adding `throws` keyword to the function results in different
117
+ // method signature in Objective-C. Maybe just call `RCTLogError`?
118
+ try? prop.set(value: Conversions.fromNSObject(newValue), onView: view)
121
119
  }
122
120
  viewManager.callLifecycleMethods(withType: .didUpdateProps, forView: view)
123
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -42,5 +42,5 @@
42
42
  "@testing-library/react-hooks": "^7.0.1",
43
43
  "expo-module-scripts": "^3.0.0"
44
44
  },
45
- "gitHead": "c2bfd01edc16bd6aecb317c67f475e9332459e2a"
45
+ "gitHead": "1815e2eaad8c753588c7b1eb74420174a28e01f4"
46
46
  }