expo-modules-core 1.5.7 → 1.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,13 +10,21 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.5.8 — 2023-07-23
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fork `uuid@3.4.0` and move into `expo-modules-core`. Remove the original dependency. ([#23249](https://github.com/expo/expo/pull/23249) by [@alanhughes](https://github.com/alanjhughes))
18
+ - Fixed the `ErrorViewGroup` cannot be cast to module view class exception on Android. ([#23651](https://github.com/expo/expo/pull/23651) by [@lukmccall](https://github.com/lukmccall))
19
+ - [iOS] Fix allowed orientations set in `Info.plist` being ignored when no delegates requested a different orientation. ([#23593](https://github.com/expo/expo/pull/23593) by [@behenate](https://github.com/behenate))
20
+
13
21
  ## 1.5.7 — 2023-07-12
14
22
 
15
23
  ### 🐛 Bug fixes
16
24
 
17
25
  - Fixed regressions and crashes in the dev client introduced by [#23405](https://github.com/expo/expo/pull/23405). ([#23491](https://github.com/expo/expo/pull/23491) by [@kudo](https://github.com/kudo))
18
26
 
19
- ## 1.5.6 2023-07-10
27
+ ## 1.5.6 - 2023-07-10
20
28
 
21
29
  ### 🐛 Bug fixes
22
30
 
@@ -24,14 +32,14 @@
24
32
  - Fixed `SoLoader` does not work on Android. ([#23415](https://github.com/expo/expo/pull/23415) by [@kudo](https://github.com/kudo))
25
33
  - Fixed slower boot time on Android. ([#23345](https://github.com/expo/expo/pull/23345) by [@lukmccall](https://github.com/lukmccall))
26
34
 
27
- ## 1.5.5 2023-07-07
35
+ ## 1.5.5 - 2023-07-07
28
36
 
29
37
  ### 🐛 Bug fixes
30
38
 
31
39
  - Improved the OkHttp network inspector stability on Android. ([#23350](https://github.com/expo/expo/pull/23350) by [@kudo](https://github.com/kudo))
32
- - [iOS] Fix conversion to `URL` type that failed despite receiving a string that contained a valid URL. ([#23331](https://github.com/expo/expo/pull/23331) by [@alanhughes](https://github.com/alanjhughes))
40
+ - [iOS] Fix conversion to `URL` type that failed despite receiving a string that contained a valid URL. ([#23331](https://github.com/expo/expo/pull/23331) by [@alanhughes](https://github.com/alanjhughes)) ([#23331](https://github.com/expo/expo/pull/23331) by [@alanjhughes](https://github.com/alanjhughes))
33
41
 
34
- ## 1.5.4 2023-07-04
42
+ ## 1.5.4 - 2023-07-04
35
43
 
36
44
  ### 🐛 Bug fixes
37
45
 
@@ -6,7 +6,7 @@ apply plugin: 'maven-publish'
6
6
  apply plugin: "de.undercouch.download"
7
7
 
8
8
  group = 'host.exp.exponent'
9
- version = '1.5.7'
9
+ version = '1.5.8'
10
10
 
11
11
  buildscript {
12
12
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -160,7 +160,7 @@ android {
160
160
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
161
161
  consumerProguardFiles 'proguard-rules.pro'
162
162
  versionCode 1
163
- versionName "1.5.7"
163
+ versionName "1.5.8"
164
164
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
165
165
 
166
166
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -279,6 +279,8 @@ dependencies {
279
279
  implementation "androidx.activity:activity-ktx:1.7.1" // androidx.appcompat:appcompat:1.4.1 depends on version 1.2.3, so we enforce higher one here
280
280
  implementation "androidx.fragment:fragment-ktx:1.5.7" // androidx.appcomapt:appcompat:1.4.1 depends on version 1.3.4, so we enforce higher one here
281
281
 
282
+ implementation("androidx.tracing:tracing-ktx:1.1.0")
283
+
282
284
  //noinspection GradleDynamicVersion
283
285
  implementation 'com.facebook.react:react-native:+'
284
286
 
@@ -40,6 +40,7 @@ import expo.modules.kotlin.jni.JSIInteropModuleRegistry
40
40
  import expo.modules.kotlin.modules.Module
41
41
  import expo.modules.kotlin.providers.CurrentActivityProvider
42
42
  import expo.modules.kotlin.sharedobjects.SharedObjectRegistry
43
+ import expo.modules.kotlin.tracing.trace
43
44
  import kotlinx.coroutines.CoroutineName
44
45
  import kotlinx.coroutines.CoroutineScope
45
46
  import kotlinx.coroutines.Dispatchers
@@ -120,26 +121,28 @@ class AppContext(
120
121
  * Initializes a JSI part of the module registry.
121
122
  * It will be a NOOP if the remote debugging was activated.
122
123
  */
123
- fun installJSIInterop() = synchronized<Unit>(this) {
124
- try {
125
- jsiInterop = JSIInteropModuleRegistry(this)
126
- val reactContext = reactContextHolder.get() ?: return
127
- val jsContextProvider = legacyModule<JavaScriptContextProvider>() ?: return
128
- val jsContextHolder = jsContextProvider.javaScriptContextRef
129
- val catalystInstance = reactContext.catalystInstance ?: return
130
- jsContextHolder
131
- .takeIf { it != 0L }
132
- ?.let {
133
- jsiInterop.installJSI(
134
- it,
135
- jniDeallocator,
136
- jsContextProvider.jsCallInvokerHolder,
137
- catalystInstance.nativeCallInvokerHolder as CallInvokerHolderImpl
138
- )
139
- logger.info("✅ JSI interop was installed")
140
- }
141
- } catch (e: Throwable) {
142
- logger.error("❌ Cannot install JSI interop: $e", e)
124
+ fun installJSIInterop() = synchronized(this) {
125
+ trace("AppContext.installJSIInterop") {
126
+ try {
127
+ jsiInterop = JSIInteropModuleRegistry(this)
128
+ val reactContext = reactContextHolder.get() ?: return@trace
129
+ val jsContextProvider = legacyModule<JavaScriptContextProvider>() ?: return@trace
130
+ val jsContextHolder = jsContextProvider.javaScriptContextRef
131
+ val catalystInstance = reactContext.catalystInstance ?: return@trace
132
+ jsContextHolder
133
+ .takeIf { it != 0L }
134
+ ?.let {
135
+ jsiInterop.installJSI(
136
+ it,
137
+ jniDeallocator,
138
+ jsContextProvider.jsCallInvokerHolder,
139
+ catalystInstance.nativeCallInvokerHolder as CallInvokerHolderImpl
140
+ )
141
+ logger.info("✅ JSI interop was installed")
142
+ }
143
+ } catch (e: Throwable) {
144
+ logger.error("❌ Cannot install JSI interop: $e", e)
145
+ }
143
146
  }
144
147
  }
145
148
 
@@ -271,7 +274,7 @@ class AppContext(
271
274
  internal val errorManager: ErrorManagerModule?
272
275
  get() = registry.getModule()
273
276
 
274
- internal fun onDestroy() {
277
+ internal fun onDestroy() = trace("AppContext.onDestroy") {
275
278
  reactContextHolder.get()?.removeLifecycleEventListener(reactLifecycleDelegate)
276
279
  registry.post(EventName.MODULE_DESTROY)
277
280
  registry.cleanUp()
@@ -7,6 +7,7 @@ import expo.modules.adapters.react.NativeModulesProxy
7
7
  import expo.modules.kotlin.defaultmodules.NativeModulesProxyModuleName
8
8
  import expo.modules.kotlin.exception.CodedException
9
9
  import expo.modules.kotlin.exception.UnexpectedException
10
+ import expo.modules.kotlin.tracing.trace
10
11
  import expo.modules.kotlin.views.GroupViewManagerWrapper
11
12
  import expo.modules.kotlin.views.SimpleViewManagerWrapper
12
13
  import expo.modules.kotlin.views.ViewManagerWrapperDelegate
@@ -42,55 +43,61 @@ class KotlinInteropModuleRegistry(
42
43
  }
43
44
  }
44
45
 
45
- fun exportedModulesConstants(): Map<ModuleName, ModuleConstants> {
46
- return registry
47
- // prevent infinite recursion - exclude NativeProxyModule constants
48
- .filter { holder -> holder.name != NativeModulesProxyModuleName }
49
- .associate { holder ->
50
- holder.name to holder.definition.constantsProvider()
46
+ fun exportedModulesConstants(): Map<ModuleName, ModuleConstants> =
47
+ trace("KotlinInteropModuleRegistry.exportedModulesConstants") {
48
+ registry
49
+ // prevent infinite recursion - exclude NativeProxyModule constants
50
+ .filter { holder -> holder.name != NativeModulesProxyModuleName }
51
+ .associate { holder ->
52
+ holder.name to holder.definition.constantsProvider()
53
+ }
54
+ }
55
+
56
+ fun exportMethods(exportKey: (String, List<ModuleMethodInfo>) -> Unit = { _, _ -> }): Map<ModuleName, List<ModuleMethodInfo>> =
57
+ trace("KotlinInteropModuleRegistry.exportMethods") {
58
+ registry.associate { holder ->
59
+ val methodsInfo = holder
60
+ .definition
61
+ .asyncFunctions
62
+ .map { (name, method) ->
63
+ mapOf(
64
+ "name" to name,
65
+ "argumentsCount" to method.argsCount
66
+ )
67
+ }
68
+ exportKey(holder.name, methodsInfo)
69
+ holder.name to methodsInfo
51
70
  }
52
- }
71
+ }
53
72
 
54
- fun exportMethods(exportKey: (String, List<ModuleMethodInfo>) -> Unit = { _, _ -> }): Map<ModuleName, List<ModuleMethodInfo>> {
55
- return registry.associate { holder ->
56
- val methodsInfo = holder
57
- .definition
58
- .asyncFunctions
59
- .map { (name, method) ->
60
- mapOf(
61
- "name" to name,
62
- "argumentsCount" to method.argsCount
63
- )
73
+ fun exportViewManagers(): List<ViewManager<*, *>> =
74
+ trace("KotlinInteropModuleRegistry.exportViewManagers") {
75
+ registry
76
+ .filter { it.definition.viewManagerDefinition != null }
77
+ .map {
78
+ val wrapperDelegate = ViewManagerWrapperDelegate(it)
79
+ when (it.definition.viewManagerDefinition!!.getViewManagerType()) {
80
+ expo.modules.core.ViewManager.ViewManagerType.SIMPLE -> SimpleViewManagerWrapper(wrapperDelegate)
81
+ expo.modules.core.ViewManager.ViewManagerType.GROUP -> GroupViewManagerWrapper(wrapperDelegate)
82
+ }
64
83
  }
65
- exportKey(holder.name, methodsInfo)
66
- holder.name to methodsInfo
67
84
  }
68
- }
69
85
 
70
- fun exportViewManagers(): List<ViewManager<*, *>> {
71
- return registry
72
- .filter { it.definition.viewManagerDefinition != null }
73
- .map {
74
- val wrapperDelegate = ViewManagerWrapperDelegate(it)
75
- when (it.definition.viewManagerDefinition!!.getViewManagerType()) {
76
- expo.modules.core.ViewManager.ViewManagerType.SIMPLE -> SimpleViewManagerWrapper(wrapperDelegate)
77
- expo.modules.core.ViewManager.ViewManagerType.GROUP -> GroupViewManagerWrapper(wrapperDelegate)
86
+ fun viewManagersMetadata(): Map<String, Map<String, Any>> =
87
+ trace("KotlinInteropModuleRegistry.viewManagersMetadata") {
88
+ registry
89
+ .filter { it.definition.viewManagerDefinition != null }
90
+ .associate { holder ->
91
+ holder.name to mapOf(
92
+ "propsNames" to (holder.definition.viewManagerDefinition?.propsNames ?: emptyList())
93
+ )
78
94
  }
79
- }
80
- }
81
-
82
- fun viewManagersMetadata(): Map<String, Map<String, Any>> {
83
- return registry
84
- .filter { it.definition.viewManagerDefinition != null }
85
- .associate { holder ->
86
- holder.name to mapOf(
87
- "propsNames" to (holder.definition.viewManagerDefinition?.propsNames ?: emptyList())
88
- )
89
- }
90
- }
95
+ }
91
96
 
92
97
  fun extractViewManagersDelegateHolders(viewManagers: List<ViewManager<*, *>>): List<ViewWrapperDelegateHolder> =
93
- viewManagers.filterIsInstance<ViewWrapperDelegateHolder>()
98
+ trace("KotlinInteropModuleRegistry.extractViewManagersDelegateHolders") {
99
+ viewManagers.filterIsInstance<ViewWrapperDelegateHolder>()
100
+ }
94
101
 
95
102
  /**
96
103
  * Since React Native v0.55, {@link com.facebook.react.ReactPackage#createViewManagers(ReactApplicationContext)}
@@ -100,15 +107,16 @@ class KotlinInteropModuleRegistry(
100
107
  * the instance that was bound with the prop method won't be the same as the instance returned by module registry.
101
108
  * To fix that we need to update all modules holder in exported view managers.
102
109
  */
103
- fun updateModuleHoldersInViewManagers(viewWrapperHolders: List<ViewWrapperDelegateHolder>) {
104
- viewWrapperHolders
105
- .map { it.viewWrapperDelegate }
106
- .forEach { holderWrapper ->
107
- holderWrapper.moduleHolder = requireNotNull(registry.getModuleHolder(holderWrapper.moduleHolder.name)) {
108
- "Cannot update the module holder for ${holderWrapper.moduleHolder.name}."
110
+ fun updateModuleHoldersInViewManagers(viewWrapperHolders: List<ViewWrapperDelegateHolder>) =
111
+ trace("KotlinInteropModuleRegistry.updateModuleHoldersInViewManagers") {
112
+ viewWrapperHolders
113
+ .map { it.viewWrapperDelegate }
114
+ .forEach { holderWrapper ->
115
+ holderWrapper.moduleHolder = requireNotNull(registry.getModuleHolder(holderWrapper.moduleHolder.name)) {
116
+ "Cannot update the module holder for ${holderWrapper.moduleHolder.name}."
117
+ }
109
118
  }
110
- }
111
- }
119
+ }
112
120
 
113
121
  fun onDestroy() {
114
122
  appContext.onDestroy()
@@ -11,6 +11,7 @@ import expo.modules.kotlin.exception.MethodNotFoundException
11
11
  import expo.modules.kotlin.exception.exceptionDecorator
12
12
  import expo.modules.kotlin.jni.JavaScriptModuleObject
13
13
  import expo.modules.kotlin.modules.Module
14
+ import expo.modules.kotlin.tracing.trace
14
15
  import kotlinx.coroutines.launch
15
16
  import kotlin.reflect.KClass
16
17
 
@@ -23,38 +24,40 @@ class ModuleHolder(val module: Module) {
23
24
  * Cached instance of HybridObject used by CPP to interact with underlying [expo.modules.kotlin.modules.Module] object.
24
25
  */
25
26
  val jsObject by lazy {
26
- val appContext = module.appContext
27
- val jniDeallocator = appContext.jniDeallocator
27
+ trace("$name.jsObject") {
28
+ val appContext = module.appContext
29
+ val jniDeallocator = appContext.jniDeallocator
28
30
 
29
- JavaScriptModuleObject(jniDeallocator, name).apply {
30
- initUsingObjectDefinition(appContext, definition.objectDefinition)
31
+ JavaScriptModuleObject(jniDeallocator, name).apply {
32
+ initUsingObjectDefinition(appContext, definition.objectDefinition)
31
33
 
32
- val viewFunctions = definition.viewManagerDefinition?.asyncFunctions
33
- if (viewFunctions?.isNotEmpty() == true) {
34
- val viewPrototype = JavaScriptModuleObject(jniDeallocator, "${name}_${definition.viewManagerDefinition?.viewType?.name}")
35
- appContext.jniDeallocator.addReference(viewPrototype)
34
+ val viewFunctions = definition.viewManagerDefinition?.asyncFunctions
35
+ if (viewFunctions?.isNotEmpty() == true) {
36
+ val viewPrototype = JavaScriptModuleObject(jniDeallocator, "${name}_${definition.viewManagerDefinition?.viewType?.name}")
37
+ appContext.jniDeallocator.addReference(viewPrototype)
36
38
 
37
- viewFunctions.forEach { function ->
38
- function.attachToJSObject(appContext, viewPrototype)
39
- }
39
+ viewFunctions.forEach { function ->
40
+ function.attachToJSObject(appContext, viewPrototype)
41
+ }
40
42
 
41
- registerViewPrototype(viewPrototype)
42
- }
43
+ registerViewPrototype(viewPrototype)
44
+ }
43
45
 
44
- definition.classData.forEach { clazz ->
45
- val clazzModuleObject = JavaScriptModuleObject(jniDeallocator, clazz.name)
46
- .initUsingObjectDefinition(module.appContext, clazz.objectDefinition)
47
- appContext.jniDeallocator.addReference(clazzModuleObject)
48
-
49
- val constructor = clazz.constructor
50
- registerClass(
51
- clazz.name,
52
- clazzModuleObject,
53
- constructor.takesOwner,
54
- constructor.argsCount,
55
- constructor.getCppRequiredTypes().toTypedArray(),
56
- constructor.getJNIFunctionBody(clazz.name, appContext)
57
- )
46
+ definition.classData.forEach { clazz ->
47
+ val clazzModuleObject = JavaScriptModuleObject(jniDeallocator, clazz.name)
48
+ .initUsingObjectDefinition(module.appContext, clazz.objectDefinition)
49
+ appContext.jniDeallocator.addReference(clazzModuleObject)
50
+
51
+ val constructor = clazz.constructor
52
+ registerClass(
53
+ clazz.name,
54
+ clazzModuleObject,
55
+ constructor.takesOwner,
56
+ constructor.argsCount,
57
+ constructor.getCppRequiredTypes().toTypedArray(),
58
+ constructor.getJNIFunctionBody(clazz.name, appContext)
59
+ )
60
+ }
58
61
  }
59
62
  }
60
63
  }
@@ -2,6 +2,7 @@ package expo.modules.kotlin
2
2
 
3
3
  import expo.modules.kotlin.events.EventName
4
4
  import expo.modules.kotlin.modules.Module
5
+ import expo.modules.kotlin.tracing.trace
5
6
  import kotlinx.coroutines.CoroutineName
6
7
  import kotlinx.coroutines.CoroutineScope
7
8
  import kotlinx.coroutines.Dispatchers
@@ -16,7 +17,7 @@ class ModuleRegistry(
16
17
  @PublishedApi
17
18
  internal val registry = mutableMapOf<String, ModuleHolder>()
18
19
 
19
- fun register(module: Module) {
20
+ fun register(module: Module) = trace("ModuleRegistry.register(${module.javaClass})") {
20
21
  module._appContext = requireNotNull(appContext.get()) { "Cannot create a module for invalid app context." }
21
22
 
22
23
  val holder = ModuleHolder(module)
@@ -3,6 +3,7 @@ package expo.modules.kotlin.modules
3
3
  import android.os.Bundle
4
4
  import expo.modules.kotlin.AppContext
5
5
  import expo.modules.kotlin.providers.AppContextProvider
6
+ import expo.modules.kotlin.tracing.trace
6
7
  import kotlinx.coroutines.CoroutineScope
7
8
 
8
9
  abstract class Module : AppContextProvider {
@@ -35,6 +36,6 @@ abstract class Module : AppContextProvider {
35
36
  }
36
37
 
37
38
  @Suppress("FunctionName")
38
- inline fun Module.ModuleDefinition(block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
39
- return ModuleDefinitionBuilder(this).also(block).buildModule()
39
+ inline fun Module.ModuleDefinition(crossinline block: ModuleDefinitionBuilder.() -> Unit): ModuleDefinitionData {
40
+ return trace("${this.javaClass}.ModuleDefinition") { ModuleDefinitionBuilder(this).also(block).buildModule() }
40
41
  }
@@ -0,0 +1,13 @@
1
+ package expo.modules.kotlin.tracing
2
+
3
+ import androidx.tracing.Trace
4
+
5
+ /**
6
+ * Wrap the specified [block] in calls to [Trace.beginSection] (with expo tag and the supplied [blockName])
7
+ * and [Trace.endSection].
8
+ *
9
+ * @param blockName A name of the code section to appear in the trace.
10
+ * @param block A block of code which is being traced.
11
+ */
12
+ inline fun <T> trace(blockName: String, crossinline block: () -> T) =
13
+ androidx.tracing.trace("[ExpoModulesCore] $blockName", block)
@@ -0,0 +1,21 @@
1
+ package expo.modules.kotlin.views
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+
7
+ /**
8
+ * A NOOP view, which is used when an error occurs.
9
+ */
10
+ open class ErrorView(context: Context) : View(context)
11
+
12
+ /**
13
+ * A NOOP view group, which is used when an error occurs.
14
+ */
15
+ class ErrorGroupView(context: Context) : ViewGroup(context) {
16
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) = Unit
17
+ }
18
+
19
+ fun View.isErrorView(): Boolean {
20
+ return this is ErrorView || this is ErrorGroupView
21
+ }
@@ -336,9 +336,9 @@ class ViewDefinitionBuilder<T : View>(
336
336
  )
337
337
 
338
338
  return if (ViewGroup::class.java.isAssignableFrom(viewClass.java)) {
339
- ErrorViewGroup(context)
339
+ ErrorGroupView(context)
340
340
  } else {
341
- View(context)
341
+ ErrorView(context)
342
342
  }
343
343
  }
344
344
 
@@ -4,16 +4,11 @@ import android.content.Context
4
4
  import android.view.View
5
5
  import android.view.ViewGroup
6
6
  import com.facebook.react.bridge.ReactContext
7
- import com.facebook.react.bridge.ReadableMap
8
7
  import expo.modules.adapters.react.NativeModulesProxy
9
8
  import expo.modules.core.ViewManager
10
9
  import expo.modules.kotlin.AppContext
11
- import expo.modules.kotlin.DynamicNull
12
10
  import expo.modules.kotlin.exception.CodedException
13
- import expo.modules.kotlin.exception.toCodedException
14
11
  import expo.modules.kotlin.functions.BaseAsyncFunctionComponent
15
- import expo.modules.kotlin.logger
16
- import expo.modules.kotlin.recycle
17
12
 
18
13
  class ViewManagerDefinition(
19
14
  private val viewFactory: (Context, AppContext) -> View,
@@ -38,29 +33,6 @@ class ViewManagerDefinition(
38
33
  }
39
34
  }
40
35
 
41
- fun setProps(propsToSet: ReadableMap, onView: View) {
42
- props.forEach { (name, propDelegate) ->
43
- try {
44
- if (propsToSet.hasKey(name)) {
45
- propsToSet
46
- .getDynamic(name)
47
- .recycle {
48
- propDelegate.set(this, onView)
49
- }
50
- } else if (propDelegate.isNullable) {
51
- propDelegate.set(DynamicNull, onView)
52
- }
53
- } catch (exception: Throwable) {
54
- logger.error("❌ Cannot set the '$name' prop on the '${viewType.simpleName}'", exception)
55
-
56
- handleException(
57
- onView,
58
- exception.toCodedException()
59
- )
60
- }
61
- }
62
- }
63
-
64
36
  fun handleException(view: View, exception: CodedException) {
65
37
  val reactContext = (view.context as? ReactContext) ?: return
66
38
  val nativeModulesProxy = reactContext
@@ -7,9 +7,9 @@ import com.facebook.react.common.MapBuilder
7
7
  import expo.modules.core.utilities.ifNull
8
8
  import expo.modules.kotlin.ModuleHolder
9
9
  import expo.modules.kotlin.events.normalizeEventName
10
- import expo.modules.kotlin.exception.CodedException
11
10
  import expo.modules.kotlin.exception.OnViewDidUpdatePropsException
12
11
  import expo.modules.kotlin.exception.exceptionDecorator
12
+ import expo.modules.kotlin.exception.toCodedException
13
13
  import expo.modules.kotlin.logger
14
14
  import expo.modules.kotlin.viewevent.ViewEventDelegate
15
15
  import kotlin.reflect.full.declaredMemberProperties
@@ -42,9 +42,16 @@ class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder) {
42
42
  exceptionDecorator({ OnViewDidUpdatePropsException(view.javaClass.kotlin, it) }) {
43
43
  it.invoke(view)
44
44
  }
45
- } catch (exception: CodedException) {
46
- logger.error("❌ Error occurred when invoking 'onViewDidUpdateProps' on '${view.javaClass.simpleName}'", exception)
47
- definition.handleException(view, exception)
45
+ } catch (exception: Throwable) {
46
+ // The view wasn't constructed correctly, so errors are expected.
47
+ // We can ignore them.
48
+ if (view.isErrorView()) {
49
+ return@let
50
+ }
51
+
52
+ val codedException = exception.toCodedException()
53
+ logger.error("❌ Error occurred when invoking 'onViewDidUpdateProps' on '${view.javaClass.simpleName}'", codedException)
54
+ definition.handleException(view, codedException)
48
55
  }
49
56
  }
50
57
  }
@@ -65,15 +72,47 @@ class ViewManagerWrapperDelegate(internal var moduleHolder: ModuleHolder) {
65
72
  while (iterator.hasNextKey()) {
66
73
  val key = iterator.nextKey()
67
74
  expoProps[key]?.let { expoProp ->
68
- expoProp.set(propsMap.getDynamic(key), view)
69
- handledProps.add(key)
75
+ try {
76
+ expoProp.set(propsMap.getDynamic(key), view)
77
+ } catch (exception: Throwable) {
78
+ // The view wasn't constructed correctly, so errors are expected.
79
+ // We can ignore them.
80
+ if (view.isErrorView()) {
81
+ return@let
82
+ }
83
+
84
+ val codedException = exception.toCodedException()
85
+ logger.error("❌ Cannot set the '$name' prop on the '$view'", codedException)
86
+ definition.handleException(
87
+ view,
88
+ codedException
89
+ )
90
+ } finally {
91
+ handledProps.add(key)
92
+ }
70
93
  }
71
94
  }
72
95
  return handledProps
73
96
  }
74
97
 
75
- fun onDestroy(view: View) =
76
- definition.onViewDestroys?.invoke(view)
98
+ fun onDestroy(view: View) {
99
+ try {
100
+ definition.onViewDestroys?.invoke(view)
101
+ } catch (exception: Throwable) {
102
+ // The view wasn't constructed correctly, so errors are expected.
103
+ // We can ignore them.
104
+ if (view.isErrorView()) {
105
+ return
106
+ }
107
+
108
+ val codedException = exception.toCodedException()
109
+ logger.error("❌ '$view' wasn't able to destroy itself", codedException)
110
+ definition.handleException(
111
+ view,
112
+ codedException
113
+ )
114
+ }
115
+ }
77
116
 
78
117
  fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
79
118
  val builder = MapBuilder.builder<String, Any>()
@@ -290,11 +290,27 @@ open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
290
290
 
291
291
  // MARK: - Managing Interface Geometry
292
292
 
293
+ /**
294
+ * Sets allowed orientations for the application. It will use the values from `Info.plist`as the orientation mask unless a subscriber requested
295
+ * a different orientation.
296
+ */
293
297
  public func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
298
+ let deviceOrientationMask = allowedOrientations(for: UIDevice.current.userInterfaceIdiom)
299
+ let universalOrientationMask = allowedOrientations(for: .unspecified)
300
+ let infoPlistOrientations = deviceOrientationMask.isEmpty ? universalOrientationMask : deviceOrientationMask
301
+
302
+ let parsedSubscribers = subscribers.filter {
303
+ $0.responds(to: #selector(application(_:supportedInterfaceOrientationsFor:)))
304
+ }
305
+
294
306
  // We want to create an intersection of all orientations set by subscribers.
295
- return subscribers.reduce(.all) { result, subscriber in
296
- return subscriber.application?(application, supportedInterfaceOrientationsFor: window).intersection(result) ?? result
307
+ let subscribersMask: UIInterfaceOrientationMask = parsedSubscribers.reduce(.all) { result, subscriber in
308
+ guard let requestedOrientation = subscriber.application?(application, supportedInterfaceOrientationsFor: window) else {
309
+ return result
310
+ }
311
+ return requestedOrientation.intersection(result)
297
312
  }
313
+ return parsedSubscribers.isEmpty ? infoPlistOrientations : subscribersMask
298
314
  }
299
315
 
300
316
  // MARK: - Statics
@@ -330,3 +346,28 @@ open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
330
346
  }
331
347
  }
332
348
  }
349
+
350
+ private func allowedOrientations(for userInterfaceIdiom: UIUserInterfaceIdiom) -> UIInterfaceOrientationMask {
351
+ // For now only iPad-specific orientations are supported
352
+ let deviceString = userInterfaceIdiom == .pad ? "~pad" : ""
353
+ var mask: UIInterfaceOrientationMask = []
354
+ guard let orientations = Bundle.main.infoDictionary?["UISupportedInterfaceOrientations\(deviceString)"] as? [String] else {
355
+ return mask
356
+ }
357
+
358
+ for orientation in orientations {
359
+ switch orientation {
360
+ case "UIInterfaceOrientationPortrait":
361
+ mask.insert(.portrait)
362
+ case "UIInterfaceOrientationLandscapeLeft":
363
+ mask.insert(.landscapeLeft)
364
+ case "UIInterfaceOrientationLandscapeRight":
365
+ mask.insert(.landscapeRight)
366
+ case "UIInterfaceOrientationPortraitUpsideDown":
367
+ mask.insert(.portraitUpsideDown)
368
+ default:
369
+ break
370
+ }
371
+ }
372
+ return mask
373
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "1.5.7",
3
+ "version": "1.5.8",
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": "8fdc53c90c52242a80ea511ee3073d9ab950bc68"
45
+ "gitHead": "c0d646e9295094bca877513e500d3c9f2e990c42"
46
46
  }
@@ -1,11 +0,0 @@
1
- package expo.modules.kotlin.views
2
-
3
- import android.content.Context
4
- import android.view.ViewGroup
5
-
6
- /**
7
- * A NOOP view group, which is used when an error occurs.
8
- */
9
- class ErrorViewGroup(context: Context) : ViewGroup(context) {
10
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) = Unit
11
- }