expo-modules-core 0.5.0 → 0.6.3

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 (41) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +16 -1
  4. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.java +10 -0
  5. package/android/src/main/java/expo/modules/core/interfaces/{ReactActivityHandler.kt → ReactActivityHandler.java} +9 -6
  6. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +37 -0
  7. package/android/src/main/java/expo/modules/core/interfaces/{ReactNativeHostHandler.kt → ReactNativeHostHandler.java} +22 -17
  8. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +1 -0
  9. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +6 -1
  10. package/build/NativeViewManagerAdapter.native.js +1 -1
  11. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  12. package/ios/AppDelegates/EXAppDelegateWrapper.h +3 -0
  13. package/ios/AppDelegates/EXAppDelegateWrapper.m +9 -6
  14. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -0
  15. package/ios/AppDelegates/ExpoAppDelegate.swift +31 -2
  16. package/ios/EXAppDefines.h +26 -0
  17. package/ios/EXAppDefines.m +61 -0
  18. package/ios/ExpoModulesCore.podspec +3 -3
  19. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +18 -5
  20. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
  21. package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
  22. package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
  23. package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
  24. package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
  25. package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
  26. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
  27. package/ios/ReactDelegates/ModulePriorities.swift +20 -0
  28. package/ios/Swift/Arguments/Convertibles.swift +15 -1
  29. package/ios/Swift/ModuleHolder.swift +11 -0
  30. package/ios/Swift/Modules/Module.swift +4 -0
  31. package/ios/Swift/Modules/ModuleDefinition.swift +3 -7
  32. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +9 -2
  33. package/ios/Swift/ModulesProvider.swift +10 -0
  34. package/ios/Swift/SwiftInteropBridge.swift +1 -1
  35. package/ios/Tests/ConstantsSpec.swift +36 -0
  36. package/ios/Tests/ConvertiblesSpec.swift +34 -0
  37. package/ios/Tests/EXAppDefinesTest.m +99 -0
  38. package/package.json +2 -2
  39. package/src/NativeViewManagerAdapter.native.tsx +1 -1
  40. package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.kt +0 -9
  41. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt +0 -11
package/CHANGELOG.md CHANGED
@@ -10,6 +10,34 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.6.3 — 2021-12-16
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed the deep link wasn't passed to the application if the application wasn't running when the deep link was sent. ([#15593](https://github.com/expo/expo/pull/15593) by [@lukmccall](https://github.com/lukmccall))
18
+
19
+ ## 0.6.2 — 2021-12-15
20
+
21
+ ### 🎉 New features
22
+
23
+ - Add `onNewIntent` and `onBackPressed` support to `ReactActivityLifecycleListener`. ([#15550](https://github.com/expo/expo/pull/15550) by [@Kudo](https://github.com/Kudo))
24
+
25
+ ## 0.6.1 — 2021-12-08
26
+
27
+ _This version does not introduce any user-facing changes._
28
+
29
+ ## 0.6.0 — 2021-12-03
30
+
31
+ ### 🎉 New features
32
+
33
+ - Made `Foundation.URL` a convertible type to consistently normalize file paths to file URLs. ([#15278](https://github.com/expo/expo/pull/15278) by [@tsapeta](https://github.com/tsapeta))
34
+ - Improve external Android handlers or listeners backward compatibility by Java 8 interface default method. ([#15421](https://github.com/expo/expo/pull/15421) by [@kudo](https://github.com/kudo))
35
+
36
+ ### 💡 Others
37
+
38
+ - Add parameter to `ReactNativeHostHandler.onDidCreateReactInstanceManager` on Android. ([#15221](https://github.com/expo/expo/pull/15221) by [@esamelson](https://github.com/esamelson))
39
+ - Make the no-argument module initializer unavailable — `onCreate` definition component should be used instead. ([#15262](https://github.com/expo/expo/pull/15262) by [@tsapeta](https://github.com/tsapeta))
40
+
13
41
  ## 0.5.0 — 2021-11-17
14
42
 
15
43
  ### 🎉 New features
@@ -24,6 +52,8 @@
24
52
  - [Sweet API] Added support for enums in method arguments on iOS. ([#15129](https://github.com/expo/expo/pull/15129) by [@tsapeta](https://github.com/tsapeta))
25
53
  - [Sweet API] Automatic conversion is now available for view props setters. ([#15132](https://github.com/expo/expo/pull/15132) by [@tsapeta](https://github.com/tsapeta))
26
54
  - [Sweet API] Added experimental implementation of the new API in Kotlin. (by [@lukmccall](https://github.com/lukmccall))
55
+ - Introduce EXAppDefines to get app building configurations. ([#14428](https://github.com/expo/expo/pull/14428) by [@kudo](https://github.com/kudo))
56
+ - Introduce React Native bridge delegate handlers on iOS. ([#15138](https://github.com/expo/expo/pull/15138) by [@kudo](https://github.com/kudo))
27
57
 
28
58
  ### 🐛 Bug fixes
29
59
 
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '0.5.0'
6
+ version = '0.6.3'
7
7
 
8
8
  buildscript {
9
9
  // Simple helper that allows the root project to override versions declared by this library.
@@ -58,7 +58,7 @@ android {
58
58
  targetSdkVersion safeExtGet("targetSdkVersion", 30)
59
59
  consumerProguardFiles 'proguard-rules.pro'
60
60
  versionCode 1
61
- versionName "0.5.0"
61
+ versionName "0.6.3"
62
62
  }
63
63
  lintOptions {
64
64
  abortOnError false
@@ -15,6 +15,7 @@ import expo.modules.core.ModuleRegistry;
15
15
  import expo.modules.core.interfaces.InternalModule;
16
16
  import expo.modules.core.interfaces.Package;
17
17
  import expo.modules.kotlin.KotlinInteropModuleRegistry;
18
+ import expo.modules.kotlin.ModulesProvider;
18
19
  import expo.modules.kotlin.views.ViewWrapperDelegateHolder;
19
20
 
20
21
  /**
@@ -24,6 +25,7 @@ import expo.modules.kotlin.views.ViewWrapperDelegateHolder;
24
25
  */
25
26
  public class ModuleRegistryAdapter implements ReactPackage {
26
27
  protected ReactModuleRegistryProvider mModuleRegistryProvider;
28
+ protected ModulesProvider mModulesProvider;
27
29
  protected ReactAdapterPackage mReactAdapterPackage = new ReactAdapterPackage();
28
30
  private NativeModulesProxy mModulesProxy;
29
31
  // We need to save all view holders to update them when the new kotlin module registry will be created.
@@ -37,6 +39,11 @@ public class ModuleRegistryAdapter implements ReactPackage {
37
39
  mModuleRegistryProvider = moduleRegistryProvider;
38
40
  }
39
41
 
42
+ public ModuleRegistryAdapter(ReactModuleRegistryProvider moduleRegistryProvider, ModulesProvider modulesProvider) {
43
+ mModuleRegistryProvider = moduleRegistryProvider;
44
+ mModulesProvider = modulesProvider;
45
+ }
46
+
40
47
  @Override
41
48
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
42
49
  ModuleRegistry moduleRegistry = mModuleRegistryProvider.get(reactContext);
@@ -57,7 +64,7 @@ public class ModuleRegistryAdapter implements ReactPackage {
57
64
  protected List<NativeModule> getNativeModulesFromModuleRegistry(ReactApplicationContext reactContext, ModuleRegistry moduleRegistry) {
58
65
  List<NativeModule> nativeModulesList = new ArrayList<>(2);
59
66
 
60
- mModulesProxy = new NativeModulesProxy(reactContext, moduleRegistry);
67
+ mModulesProxy = createNativeModulesProxy(reactContext, moduleRegistry);
61
68
  nativeModulesList.add(mModulesProxy);
62
69
 
63
70
  // Add listener that will notify expo.modules.core.ModuleRegistry when all modules are ready
@@ -97,4 +104,12 @@ public class ModuleRegistryAdapter implements ReactPackage {
97
104
 
98
105
  return viewManagerList;
99
106
  }
107
+
108
+ private NativeModulesProxy createNativeModulesProxy(ReactApplicationContext reactContext, ModuleRegistry moduleRegistry) {
109
+ if (mModulesProvider != null) {
110
+ return new NativeModulesProxy(reactContext, moduleRegistry, mModulesProvider);
111
+ } else {
112
+ return new NativeModulesProxy(reactContext, moduleRegistry);
113
+ }
114
+ }
100
115
  }
@@ -0,0 +1,10 @@
1
+ package expo.modules.core.interfaces;
2
+
3
+ import android.app.Application;
4
+ import android.content.res.Configuration;
5
+
6
+ public interface ApplicationLifecycleListener {
7
+ default void onCreate(Application application) {}
8
+
9
+ default void onConfigurationChanged(Configuration newConfig) {}
10
+ }
@@ -1,18 +1,21 @@
1
- package expo.modules.core.interfaces
1
+ package expo.modules.core.interfaces;
2
2
 
3
- import android.app.Activity
4
- import com.facebook.react.ReactRootView
3
+ import android.app.Activity;
4
+ import com.facebook.react.ReactRootView;
5
+
6
+ import androidx.annotation.Nullable;
5
7
 
6
8
  /**
7
9
  * A handler API for modules to override default ReactActivity behaviors.
8
10
  * Used by {@link ReactActivityDelegateWrapper}
9
11
  */
10
- interface ReactActivityHandler {
12
+ public interface ReactActivityHandler {
11
13
  /**
12
14
  * Given modules a chance to override the default {@link ReactRootView}
13
15
  * @return the override ReactRootView instance or null if not to override
14
16
  */
15
- fun createReactRootView(activity: Activity): ReactRootView? {
16
- return null
17
+ @Nullable
18
+ default ReactRootView createReactRootView(Activity activity) {
19
+ return null;
17
20
  }
18
21
  }
@@ -0,0 +1,37 @@
1
+ package expo.modules.core.interfaces;
2
+
3
+ import android.app.Activity;
4
+ import android.content.Intent;
5
+ import android.os.Bundle;
6
+
7
+ public interface ReactActivityLifecycleListener {
8
+ default void onCreate(Activity activity, Bundle savedInstanceState) {}
9
+
10
+ default void onResume(Activity activity) {}
11
+
12
+ default void onPause(Activity activity) {}
13
+
14
+ default void onDestroy(Activity activity) {}
15
+
16
+ /**
17
+ * Called when {@link com.facebook.react.ReactActivity} received `onNewIntent`
18
+ * Every listener will receive this callback.
19
+ * `ReactActivityDelegateWrapper.onNewIntent` will get `true` if there's some module returns `true`
20
+ *
21
+ * @return true if this module wants to return `true` from `ReactActivityDelegateWrapper.onNewIntent`
22
+ */
23
+ default boolean onNewIntent(Intent intent) {
24
+ return false;
25
+ }
26
+
27
+ /**
28
+ * Called when {@link com.facebook.react.ReactActivity} received `onBackPressed`
29
+ * Every listener will receive this callback.
30
+ * `ReactActivityDelegateWrapper.onBackPressed` will get `true` if there's some module returns `true`
31
+ *
32
+ * @return true if this module wants to return `true` from `ReactActivityDelegateWrapper.onBackPressed`
33
+ */
34
+ default boolean onBackPressed() {
35
+ return false;
36
+ }
37
+ }
@@ -1,18 +1,21 @@
1
- package expo.modules.core.interfaces
1
+ package expo.modules.core.interfaces;
2
2
 
3
- import com.facebook.react.ReactInstanceManager
4
- import com.facebook.react.bridge.JavaScriptContextHolder
5
- import com.facebook.react.bridge.ReactApplicationContext
3
+ import com.facebook.react.ReactInstanceManager;
4
+ import com.facebook.react.bridge.JavaScriptContextHolder;
5
+ import com.facebook.react.bridge.ReactApplicationContext;
6
6
 
7
- interface ReactNativeHostHandler {
7
+ import androidx.annotation.Nullable;
8
+
9
+ public interface ReactNativeHostHandler {
8
10
  /**
9
11
  * Given chance for modules to customize {@link ReactInstanceManager}
10
12
  *
11
13
  * @param useDeveloperSupport true if {@link ReactNativeHost} enabled developer support
12
14
  * @return instance of {@link ReactInstanceManager}, or null if not to override
13
15
  */
14
- fun createReactInstanceManager(useDeveloperSupport: Boolean): ReactInstanceManager? {
15
- return null
16
+ @Nullable
17
+ default ReactInstanceManager createReactInstanceManager(boolean useDeveloperSupport) {
18
+ return null;
16
19
  }
17
20
 
18
21
  /**
@@ -22,8 +25,9 @@ interface ReactNativeHostHandler {
22
25
  * @param useDeveloperSupport true if {@link ReactNativeHost} enabled developer support
23
26
  * @return custom path to bundle file, or null if not to override
24
27
  */
25
- fun getJSBundleFile(useDeveloperSupport: Boolean): String? {
26
- return null
28
+ @Nullable
29
+ default String getJSBundleFile(boolean useDeveloperSupport) {
30
+ return null;
27
31
  }
28
32
 
29
33
  /**
@@ -33,8 +37,9 @@ interface ReactNativeHostHandler {
33
37
  * @param useDeveloperSupport true if {@link ReactNativeHost} enabled developer support
34
38
  * @return custom bundle asset name, or null if not to override
35
39
  */
36
- fun getBundleAssetName(useDeveloperSupport: Boolean): String? {
37
- return null
40
+ @Nullable
41
+ default String getBundleAssetName(boolean useDeveloperSupport) {
42
+ return null;
38
43
  }
39
44
 
40
45
  //region event listeners
@@ -44,22 +49,22 @@ interface ReactNativeHostHandler {
44
49
  *
45
50
  * @param useDeveloperSupport true if {@link ReactNativeHost} enabled developer support
46
51
  */
47
- fun onRegisterJSIModules(
48
- reactApplicationContext: ReactApplicationContext,
49
- jsContext: JavaScriptContextHolder,
50
- useDeveloperSupport: Boolean
52
+ default void onRegisterJSIModules(
53
+ ReactApplicationContext reactApplicationContext,
54
+ JavaScriptContextHolder jsContext,
55
+ boolean useDeveloperSupport
51
56
  ) {
52
57
  }
53
58
 
54
59
  /**
55
60
  * Callback before {@link ReactInstanceManager} creation
56
61
  */
57
- fun onWillCreateReactInstanceManager(useDeveloperSupport: Boolean) {}
62
+ default void onWillCreateReactInstanceManager(boolean useDeveloperSupport) {}
58
63
 
59
64
  /**
60
65
  * Callback after {@link ReactInstanceManager} creation
61
66
  */
62
- fun onDidCreateReactInstanceManager(useDeveloperSupport: Boolean) {}
67
+ default void onDidCreateReactInstanceManager(ReactInstanceManager reactInstanceManager, boolean useDeveloperSupport) {}
63
68
 
64
69
  //endregion
65
70
  }
@@ -10,6 +10,7 @@ class KPromiseWrapper(
10
10
  override fun resolve(value: Any?) {
11
11
  bridgePromise.resolve(
12
12
  when (value) {
13
+ is Unit -> null
13
14
  is Bundle -> Arguments.fromBundle(value as Bundle?)
14
15
  is List<*> -> Arguments.fromList(value as List<*>?)
15
16
  else -> value
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin.views
2
2
 
3
3
  import android.content.Context
4
+ import android.util.Log
4
5
  import android.view.View
5
6
  import android.view.ViewGroup
6
7
  import com.facebook.react.bridge.ReadableMap
@@ -29,7 +30,11 @@ class ViewManagerDefinition(
29
30
  val key = iterator.nextKey()
30
31
  val propDelegate = props[key] ?: continue
31
32
  propsToSet.getDynamic(key).recycle {
32
- propDelegate.set(this, onView)
33
+ try {
34
+ propDelegate.set(this, onView)
35
+ } catch (exception: Throwable) {
36
+ Log.e("ExpoModulesCore", "Cannot set the $key prop on the ${viewType.simpleName}.", exception)
37
+ }
33
38
  }
34
39
  }
35
40
  }
@@ -20,7 +20,7 @@ export function requireNativeViewManager(viewName) {
20
20
  const { NativeUnimoduleProxy } = NativeModules;
21
21
  if (!NativeUnimoduleProxy.viewManagersNames.includes(viewName)) {
22
22
  const exportedViewManagerNames = NativeUnimoduleProxy.viewManagersNames.join(', ');
23
- console.warn(`The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by @unimodules/react-native-adapter. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`);
23
+ console.warn(`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}].`);
24
24
  }
25
25
  }
26
26
  // Set up the React Native native component, which is an adapter to the universal module's view
@@ -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,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE/F,gFAAgF;AAChF,0FAA0F;AAC1F,4CAA4C;AAC5C,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,mGAAmG;AACnG,+DAA+D;AAE/D,kGAAkG;AAClG,+FAA+F;AAC/F,qCAAqC;AACrC,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAMrD;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAU,QAAgB;IAChE,IAAI,OAAO,EAAE;QACX,MAAM,EAAE,oBAAoB,EAAE,GAAG,aAAa,CAAC;QAC/C,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAC9D,MAAM,wBAAwB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CACV,6CAA6C,QAAQ,6JAA6J,wBAAwB,IAAI,CAC/O,CAAC;SACH;KACF;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,0BAA0B,GAAG,CAAC,SAAS,CAAC,oBAAoB;QAChE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,mBAAmB,CAAC;QACrD,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,IAAI;QACrC,WAAW,EAAE,EAAE;QACf,gBAAgB,EAAE,EAAE;KACrB,CAAC;IACF,MAAM,6BAA6B,GAAG;QACpC,UAAU;QACV,GAAG,iBAAiB;QACpB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,WAAW,CAAC;QACtD,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,gBAAgB,CAAC;KAC5D,CAAC;IAEF,sFAAsF;IACtF,SAAS,sBAAsB,CAAC,KAAK,EAAE,GAAG;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;QAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;QAChE,OAAO,oBAAC,oBAAoB,OAAK,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,GAAI,CAAC;IAC9F,CAAC;IACD,sBAAsB,CAAC,WAAW,GAAG,WAAW,QAAQ,GAAG,CAAC;IAC5D,OAAO,KAAK,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;AAClD,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, UIManager, ViewPropTypes, 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\n// NOTE: React Native is moving away from runtime PropTypes and may remove ViewPropTypes, in which\n// case we will need another way to separate standard React Native view props from other props,\n// which we proxy through the adapter\nconst ViewPropTypesKeys = Object.keys(ViewPropTypes);\n\ntype NativeExpoComponentProps = {\n proxiedProperties: object;\n};\n\n/**\n * A drop-in replacement for `requireNativeComponent`.\n */\nexport function requireNativeViewManager<P = any>(viewName: string): React.ComponentType<P> {\n if (__DEV__) {\n const { NativeUnimoduleProxy } = NativeModules;\n if (!NativeUnimoduleProxy.viewManagersNames.includes(viewName)) {\n const exportedViewManagerNames = NativeUnimoduleProxy.viewManagersNames.join(', ');\n console.warn(\n `The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by @unimodules/react-native-adapter. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`\n );\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 reactNativeUIConfiguration = (UIManager.getViewManagerConfig\n ? UIManager.getViewManagerConfig(reactNativeViewName)\n : UIManager[reactNativeViewName]) || {\n NativeProps: {},\n directEventTypes: {},\n };\n const reactNativeComponentPropNames = [\n 'children',\n ...ViewPropTypesKeys,\n ...Object.keys(reactNativeUIConfiguration.NativeProps),\n ...Object.keys(reactNativeUIConfiguration.directEventTypes),\n ];\n\n // Define a component for universal-module authors to access their native view manager\n function NativeComponentAdapter(props, ref) {\n const nativeProps = pick(props, reactNativeComponentPropNames);\n const proxiedProps = omit(props, reactNativeComponentPropNames);\n return <ReactNativeComponent {...nativeProps} proxiedProperties={proxiedProps} ref={ref} />;\n }\n NativeComponentAdapter.displayName = `Adapter<${viewName}>`;\n return React.forwardRef(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,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE/F,gFAAgF;AAChF,0FAA0F;AAC1F,4CAA4C;AAC5C,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,mGAAmG;AACnG,+DAA+D;AAE/D,kGAAkG;AAClG,+FAA+F;AAC/F,qCAAqC;AACrC,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAMrD;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAU,QAAgB;IAChE,IAAI,OAAO,EAAE;QACX,MAAM,EAAE,oBAAoB,EAAE,GAAG,aAAa,CAAC;QAC/C,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAC9D,MAAM,wBAAwB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CACV,6CAA6C,QAAQ,8IAA8I,wBAAwB,IAAI,CAChO,CAAC;SACH;KACF;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,0BAA0B,GAAG,CAAC,SAAS,CAAC,oBAAoB;QAChE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,mBAAmB,CAAC;QACrD,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,IAAI;QACrC,WAAW,EAAE,EAAE;QACf,gBAAgB,EAAE,EAAE;KACrB,CAAC;IACF,MAAM,6BAA6B,GAAG;QACpC,UAAU;QACV,GAAG,iBAAiB;QACpB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,WAAW,CAAC;QACtD,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,gBAAgB,CAAC;KAC5D,CAAC;IAEF,sFAAsF;IACtF,SAAS,sBAAsB,CAAC,KAAK,EAAE,GAAG;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;QAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;QAChE,OAAO,oBAAC,oBAAoB,OAAK,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,GAAI,CAAC;IAC9F,CAAC;IACD,sBAAsB,CAAC,WAAW,GAAG,WAAW,QAAQ,GAAG,CAAC;IAC5D,OAAO,KAAK,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;AAClD,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, UIManager, ViewPropTypes, 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\n// NOTE: React Native is moving away from runtime PropTypes and may remove ViewPropTypes, in which\n// case we will need another way to separate standard React Native view props from other props,\n// which we proxy through the adapter\nconst ViewPropTypesKeys = Object.keys(ViewPropTypes);\n\ntype NativeExpoComponentProps = {\n proxiedProperties: object;\n};\n\n/**\n * A drop-in replacement for `requireNativeComponent`.\n */\nexport function requireNativeViewManager<P = any>(viewName: string): React.ComponentType<P> {\n if (__DEV__) {\n const { NativeUnimoduleProxy } = NativeModules;\n if (!NativeUnimoduleProxy.viewManagersNames.includes(viewName)) {\n const exportedViewManagerNames = NativeUnimoduleProxy.viewManagersNames.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\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 reactNativeUIConfiguration = (UIManager.getViewManagerConfig\n ? UIManager.getViewManagerConfig(reactNativeViewName)\n : UIManager[reactNativeViewName]) || {\n NativeProps: {},\n directEventTypes: {},\n };\n const reactNativeComponentPropNames = [\n 'children',\n ...ViewPropTypesKeys,\n ...Object.keys(reactNativeUIConfiguration.NativeProps),\n ...Object.keys(reactNativeUIConfiguration.directEventTypes),\n ];\n\n // Define a component for universal-module authors to access their native view manager\n function NativeComponentAdapter(props, ref) {\n const nativeProps = pick(props, reactNativeComponentPropNames);\n const proxiedProps = omit(props, reactNativeComponentPropNames);\n return <ReactNativeComponent {...nativeProps} proxiedProperties={proxiedProps} ref={ref} />;\n }\n NativeComponentAdapter.displayName = `Adapter<${viewName}>`;\n return React.forwardRef(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,6 +1,7 @@
1
1
  // Copyright 2018-present 650 Industries. All rights reserved.
2
2
 
3
3
  #import <UIKit/UIKit.h>
4
+ #import <ExpoModulesCore/EXReactDelegateWrapper.h>
4
5
 
5
6
  NS_ASSUME_NONNULL_BEGIN
6
7
 
@@ -11,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN
11
12
  */
12
13
  @interface EXAppDelegateWrapper : UIResponder <UIApplicationDelegate>
13
14
 
15
+ @property (nonatomic, strong, readonly) EXReactDelegateWrapper *reactDelegate;
16
+
14
17
  @end
15
18
 
16
19
  NS_ASSUME_NONNULL_END
@@ -1,13 +1,15 @@
1
1
  // Copyright 2018-present 650 Industries. All rights reserved.
2
2
 
3
3
  #import <ExpoModulesCore/EXAppDelegateWrapper.h>
4
+ #import <ExpoModulesCore/EXReactDelegateWrapper+Private.h>
5
+ #import <ExpoModulesCore/Swift.h>
4
6
 
5
- #if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
6
- // When `use_frameworks!` is used, the generated Swift header is inside ExpoModulesCore module.
7
- #import <ExpoModulesCore/ExpoModulesCore-Swift.h>
8
- #else
9
- #import "ExpoModulesCore-Swift.h"
10
- #endif
7
+
8
+ @interface EXAppDelegateWrapper()
9
+
10
+ @property (nonatomic, strong) EXReactDelegateWrapper *reactDelegate;
11
+
12
+ @end
11
13
 
12
14
  @implementation EXAppDelegateWrapper {
13
15
  EXExpoAppDelegate *_expoAppDelegate;
@@ -20,6 +22,7 @@
20
22
  {
21
23
  if (self = [super init]) {
22
24
  _expoAppDelegate = [[EXExpoAppDelegate alloc] init];
25
+ _reactDelegate = [[EXReactDelegateWrapper alloc] initWithExpoReactDelegate:_expoAppDelegate.reactDelegate];
23
26
  }
24
27
  return self;
25
28
  }
@@ -24,6 +24,7 @@
24
24
  id<ModulesProviderObjCProtocol> modulesProvider = [EXNativeModulesProxy getExpoModulesProvider];
25
25
  [EXExpoAppDelegate registerSubscriber:[[EXLegacyAppDelegateWrapper alloc] init]];
26
26
  [EXExpoAppDelegate registerSubscribersFromModulesProvider:modulesProvider];
27
+ [EXExpoAppDelegate registerReactDelegateHandlersFromModulesProvider:modulesProvider];
27
28
  }
28
29
 
29
30
  @end
@@ -3,6 +3,7 @@ import Dispatch
3
3
  import Foundation
4
4
 
5
5
  var subscribers = [ExpoAppDelegateSubscriberProtocol]()
6
+ var reactDelegateHandlers = [ExpoReactDelegateHandler]()
6
7
 
7
8
  /**
8
9
  Allows classes extending `ExpoAppDelegateSubscriber` to hook into project's app delegate
@@ -14,11 +15,25 @@ var subscribers = [ExpoAppDelegateSubscriberProtocol]()
14
15
  open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
15
16
  open var window: UIWindow?
16
17
 
18
+ @objc
19
+ public let reactDelegate = ExpoReactDelegate(handlers: reactDelegateHandlers)
20
+
17
21
  // MARK: - Initializing the App
18
22
 
19
23
  open func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
20
- return subscribers.reduce(false) { result, subscriber in
21
- return subscriber.application?(application, willFinishLaunchingWithOptions: launchOptions) ?? false || result
24
+ let parsedSubscribers = subscribers.filter {
25
+ $0.responds(to: #selector(application(_:willFinishLaunchingWithOptions:)))
26
+ }
27
+
28
+ // If we can't find a subscriber that implements `willFinishLaunchingWithOptions`, we will delegate the decision if we can handel the passed URL to
29
+ // the `didFinishLaunchingWithOptions` method by returning `true` here.
30
+ // You can read more about how iOS handles deep links here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application#discussion
31
+ if (parsedSubscribers.isEmpty) {
32
+ return true;
33
+ }
34
+
35
+ return parsedSubscribers.reduce(false) { result, subscriber in
36
+ return subscriber.application!(application, willFinishLaunchingWithOptions: launchOptions) || result
22
37
  }
23
38
  }
24
39
 
@@ -261,4 +276,18 @@ open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
261
276
  public static func getSubscriber(_ name: String) -> ExpoAppDelegateSubscriberProtocol? {
262
277
  return subscribers.first { String(describing: $0) == name }
263
278
  }
279
+
280
+ @objc
281
+ public static func registerReactDelegateHandlersFrom(modulesProvider: ModulesProviderObjCProtocol) {
282
+ guard let provider = modulesProvider as? ModulesProviderProtocol else {
283
+ fatalError("Expo modules provider must implement `ModulesProviderProtocol`.")
284
+ }
285
+ provider.getReactDelegateHandlers()
286
+ .sorted { (tuple1, tuple2) -> Bool in
287
+ return ModulePriorities.get(tuple1.packageName) > ModulePriorities.get(tuple2.packageName)
288
+ }
289
+ .forEach { handlerTuple in
290
+ reactDelegateHandlers.append(handlerTuple.handler.init())
291
+ }
292
+ }
264
293
  }
@@ -0,0 +1,26 @@
1
+ // Copyright 2016-present 650 Industries. All rights reserved.
2
+
3
+ #import <Foundation/Foundation.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ For expo modules to get app build time preprocessor values.
9
+ We ship some modules in [prebuilt binaries](https://expo.fyi/prebuilt-modules),
10
+ classic defines like `DEBUG` or `RCT_DEV` may not work as expected
11
+ because the prebuilt modules are always built with Release configuration.
12
+ This class acts as a supporter to get app build time preprocessor values.
13
+ */
14
+ @interface EXAppDefines : NSObject
15
+
16
+ @property (class, nonatomic, assign, readonly) BOOL APP_DEBUG NS_SWIFT_NAME(APP_DEBUG);
17
+ @property (class, nonatomic, assign, readonly) BOOL APP_RCT_DEBUG NS_SWIFT_NAME(APP_RCT_DEBUG);
18
+ @property (class, nonatomic, assign, readonly) BOOL APP_RCT_DEV NS_SWIFT_NAME(APP_RCT_DEV);
19
+
20
+ + (NSDictionary *)getAllDefines;
21
+
22
+ + (void)load:(NSDictionary *)defines;
23
+
24
+ @end
25
+
26
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,61 @@
1
+ // Copyright 2016-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/EXAppDefines.h>
4
+ #import <React/RCTDefines.h>
5
+
6
+ @implementation EXAppDefines
7
+
8
+ static NSDictionary *_storage;
9
+ static BOOL _loaded = NO;
10
+
11
+ + (BOOL)APP_DEBUG
12
+ {
13
+ [self throwIfNotLoaded];
14
+ return [_storage[@"APP_DEBUG"] boolValue];
15
+ }
16
+
17
+ + (BOOL)APP_RCT_DEBUG
18
+ {
19
+ [self throwIfNotLoaded];
20
+ return [_storage[@"APP_RCT_DEBUG"] boolValue];
21
+ }
22
+
23
+ + (BOOL)APP_RCT_DEV
24
+ {
25
+ [self throwIfNotLoaded];
26
+ return [_storage[@"APP_RCT_DEV"] boolValue];
27
+ }
28
+
29
+ + (NSDictionary *)getAllDefines
30
+ {
31
+ return _storage;
32
+ }
33
+
34
+ + (void)load:(NSDictionary *)defines
35
+ {
36
+ NSAssert([NSThread isMainThread], @"This function must be called on main thread");
37
+ NSAssert(!_loaded, @"EXAppDefines is already loaded");
38
+ if (!_loaded) {
39
+ _storage = defines;
40
+ _loaded = YES;
41
+ }
42
+ }
43
+
44
+ // Private function for EXAppDefinesTest to unload the current state.
45
+ + (void)_unload
46
+ {
47
+ _storage = nil;
48
+ _loaded = NO;
49
+ }
50
+
51
+ + (void)throwIfNotLoaded
52
+ {
53
+ if (!_loaded) {
54
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
55
+ reason:@"EXAppDefines is not loaded."
56
+ userInfo:nil];
57
+ }
58
+ }
59
+
60
+
61
+ @end
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
21
21
  'USE_HEADERMAP' => 'YES',
22
22
  'DEFINES_MODULE' => 'YES',
23
23
  'CLANG_CXX_LANGUAGE_STANDARD' => 'c++14',
24
- 'SWIFT_COMPILATION_MODE' => 'wholemodule'
24
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule',
25
25
  }
26
26
 
27
27
  s.dependency 'React-Core'
@@ -35,12 +35,12 @@ Pod::Spec.new do |s|
35
35
  end
36
36
 
37
37
  s.exclude_files = 'Tests/'
38
- s.private_header_files = '**/Swift.h'
38
+ s.private_header_files = ['**/*+Private.h', '**/Swift.h']
39
39
 
40
40
  s.test_spec 'Tests' do |test_spec|
41
41
  test_spec.dependency 'Quick'
42
42
  test_spec.dependency 'Nimble'
43
43
 
44
- test_spec.source_files = 'Tests/**/*.swift'
44
+ test_spec.source_files = 'Tests/**/*.{m,swift}'
45
45
  end
46
46
  end
@@ -207,13 +207,26 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
207
207
  {
208
208
  // Dynamically gets the modules provider class.
209
209
  // NOTE: This needs to be versioned in Expo Go.
210
- Class generatedExpoModulesProvider = NSClassFromString(@"ExpoModulesProvider");
211
- // Checks if `ExpoModulesProvider` was generated
212
- if (generatedExpoModulesProvider) {
210
+ Class generatedExpoModulesProvider;
211
+
212
+ // [0] When ExpoModulesCore is built as separated framework/module,
213
+ // we should explicitly load main bundle's `ExpoModulesProvider` class.
214
+ NSString *bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
215
+ if (bundleName != nil) {
216
+ generatedExpoModulesProvider = NSClassFromString([NSString stringWithFormat:@"%@.ExpoModulesProvider", bundleName]);
217
+ if (generatedExpoModulesProvider != nil) {
218
+ return [generatedExpoModulesProvider new];
219
+ }
220
+ }
221
+
222
+ // [1] Fallback to load `ExpoModulesProvider` class from the current module.
223
+ generatedExpoModulesProvider = NSClassFromString(@"ExpoModulesProvider");
224
+ if (generatedExpoModulesProvider != nil) {
213
225
  return [generatedExpoModulesProvider new];
214
- } else {
215
- return [ModulesProvider new];
216
226
  }
227
+
228
+ // [2] Fallback to load `ModulesProvider` if `ExpoModulesProvider` was not generated
229
+ return [ModulesProvider new];
217
230
  }
218
231
 
219
232
  #pragma mark - Privates
@@ -0,0 +1,16 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <React/RCTBridgeDelegate.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface EXRCTBridgeDelegateInterceptor : NSObject<RCTBridgeDelegate>
8
+
9
+ @property (nonatomic, weak) id<RCTBridgeDelegate> bridgeDelegate;
10
+ @property (nonatomic, weak) id<RCTBridgeDelegate> interceptor;
11
+
12
+ - (instancetype)initWithBridgeDelegate:(id<RCTBridgeDelegate>)bridgeDelegate interceptor:(id<RCTBridgeDelegate>)interceptor;
13
+
14
+ @end
15
+
16
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,49 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/EXRCTBridgeDelegateInterceptor.h>
4
+
5
+ @implementation EXRCTBridgeDelegateInterceptor
6
+
7
+ - (instancetype)initWithBridgeDelegate:(id<RCTBridgeDelegate>)bridgeDelegate interceptor:(id<RCTBridgeDelegate>)interceptor
8
+ {
9
+ if (self = [super init]) {
10
+ self.bridgeDelegate = bridgeDelegate;
11
+ self.interceptor = interceptor;
12
+ }
13
+ return self;
14
+ }
15
+
16
+ - (BOOL)conformsToProtocol:(Protocol *)protocol
17
+ {
18
+ return [self.bridgeDelegate conformsToProtocol:protocol];
19
+ }
20
+
21
+ - (id)forwardingTargetForSelector:(SEL)selector
22
+ {
23
+ if ([self isInterceptedSelector:selector]) {
24
+ return self;
25
+ }
26
+ return self.bridgeDelegate;
27
+ }
28
+
29
+ - (BOOL)respondsToSelector:(SEL)selector
30
+ {
31
+ if ([self isInterceptedSelector:selector]) {
32
+ return YES;
33
+ }
34
+ return [self.bridgeDelegate respondsToSelector:selector];
35
+ }
36
+
37
+ - (BOOL)isInterceptedSelector:(SEL)selector
38
+ {
39
+ if ([self.interceptor respondsToSelector:selector]) {
40
+ return YES;
41
+ }
42
+ return NO;
43
+ }
44
+
45
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
46
+ return [self.interceptor sourceURLForBridge:bridge];
47
+ }
48
+
49
+ @end
@@ -0,0 +1,18 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/Swift.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ Internal `EXReactDelegateWrapper` interface for the designated initializer with `ExpoReactDelegate`.
9
+ Since `ExpoReactDelegate` implements in swift and requires the generated `ExpoModulesCore-Swift.h` header,
10
+ this header file should ONLY be imported from *.m or *.mm files.
11
+ */
12
+ @interface EXReactDelegateWrapper(Private)
13
+
14
+ - (instancetype)initWithExpoReactDelegate:(ExpoReactDelegate *)expoReactDelegate;
15
+
16
+ @end
17
+
18
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,25 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <UIKit/UIKit.h>
4
+ #import <React/RCTBridge.h>
5
+ #import <React/RCTRootView.h>
6
+
7
+ NS_ASSUME_NONNULL_BEGIN
8
+
9
+ /**
10
+ A wrapper of `ExpoReactDelegate` for Objective-C bindings.
11
+ */
12
+ @interface EXReactDelegateWrapper : NSObject
13
+
14
+ - (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate
15
+ launchOptions:(nullable NSDictionary *)launchOptions;
16
+
17
+ - (RCTRootView *)createRootViewWithBridge:(RCTBridge *)bridge
18
+ moduleName:(NSString *)moduleName
19
+ initialProperties:(nullable NSDictionary *)initialProperties;
20
+
21
+ - (UIViewController *)createRootViewController;
22
+
23
+ @end
24
+
25
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,40 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/EXReactDelegateWrapper.h>
4
+ #import <ExpoModulesCore/EXReactDelegateWrapper+Private.h>
5
+
6
+ @interface EXReactDelegateWrapper()
7
+
8
+ @property (nonatomic, weak) ExpoReactDelegate *expoReactDelegate;
9
+
10
+ @end
11
+
12
+ @implementation EXReactDelegateWrapper
13
+
14
+ - (instancetype)initWithExpoReactDelegate:(ExpoReactDelegate *)expoReactDelegate
15
+ {
16
+ if (self = [super init]) {
17
+ _expoReactDelegate = expoReactDelegate;
18
+ }
19
+ return self;
20
+ }
21
+
22
+ - (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate
23
+ launchOptions:(nullable NSDictionary *)launchOptions
24
+ {
25
+ return [_expoReactDelegate createBridgeWithDelegate:delegate launchOptions:launchOptions];
26
+ }
27
+
28
+ - (RCTRootView *)createRootViewWithBridge:(RCTBridge *)bridge
29
+ moduleName:(NSString *)moduleName
30
+ initialProperties:(nullable NSDictionary *)initialProperties
31
+ {
32
+ return [_expoReactDelegate createRootViewWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
33
+ }
34
+
35
+ - (UIViewController *)createRootViewController
36
+ {
37
+ return [_expoReactDelegate createRootViewController];
38
+ }
39
+
40
+ @end
@@ -0,0 +1,37 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ An extensible react instance creation delegate. This class will loop through each `ExpoReactDelegateHandler` to determine the winner to create the instance.
5
+ */
6
+ @objc
7
+ public class ExpoReactDelegate: NSObject {
8
+ private let handlers: [ExpoReactDelegateHandler]
9
+
10
+ public init(handlers: [ExpoReactDelegateHandler]) {
11
+ self.handlers = handlers
12
+ }
13
+
14
+ @objc
15
+ public func createBridge(delegate: RCTBridgeDelegate, launchOptions: [AnyHashable : Any]?) -> RCTBridge {
16
+ self.handlers.forEach { $0.bridgeWillCreate() }
17
+ let result = self.handlers.lazy
18
+ .compactMap { $0.createBridge(reactDelegate: self, bridgeDelegate: delegate, launchOptions: launchOptions) }
19
+ .first(where: { _ in true }) ?? RCTBridge(delegate: delegate, launchOptions: launchOptions)!
20
+ self.handlers.forEach { $0.bridgeDidCreate(bridge: result) }
21
+ return result
22
+ }
23
+
24
+ @objc
25
+ public func createRootView(bridge: RCTBridge, moduleName: String, initialProperties: [AnyHashable : Any]?) -> RCTRootView {
26
+ return self.handlers.lazy
27
+ .compactMap { $0.createRootView(reactDelegate: self, bridge: bridge, moduleName: moduleName, initialProperties: initialProperties) }
28
+ .first(where: { _ in true }) ?? RCTRootView(bridge: bridge, moduleName: moduleName, initialProperties: initialProperties)
29
+ }
30
+
31
+ @objc
32
+ public func createRootViewController() -> UIViewController {
33
+ return self.handlers.lazy
34
+ .compactMap { $0.createRootViewController(reactDelegate: self) }
35
+ .first(where: { _ in true }) ?? UIViewController()
36
+ }
37
+ }
@@ -0,0 +1,52 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+
5
+ /**
6
+ The handler for `ExpoReactDelegate`. A module can implement a handler to process react instance creation.
7
+ */
8
+ @objc
9
+ open class ExpoReactDelegateHandler: NSObject {
10
+ public override required init() {}
11
+
12
+ /**
13
+ If this module wants to handle `RCTBridge` creation, it can return the instance.
14
+ Otherwise return nil.
15
+ */
16
+ @objc
17
+ open func createBridge(reactDelegate: ExpoReactDelegate, bridgeDelegate: RCTBridgeDelegate, launchOptions: [AnyHashable : Any]?) -> RCTBridge? {
18
+ return nil
19
+ }
20
+
21
+ /**
22
+ If this module wants to handle `RCTRootView` creation, it can return the instance.
23
+ Otherwise return nil.
24
+ */
25
+ @objc
26
+ open func createRootView(reactDelegate: ExpoReactDelegate, bridge: RCTBridge, moduleName: String, initialProperties: [AnyHashable : Any]?) -> RCTRootView? {
27
+ return nil
28
+ }
29
+
30
+ /**
31
+ If this module wants to handle `UIViewController` creation for `RCTRootView`, it can return the instance.
32
+ Otherwise return nil.
33
+ */
34
+ @objc
35
+ open func createRootViewController(reactDelegate: ExpoReactDelegate) -> UIViewController? {
36
+ return nil
37
+ }
38
+
39
+ // MARK - event callbacks
40
+
41
+ /**
42
+ Callback before bridge creation
43
+ */
44
+ @objc
45
+ open func bridgeWillCreate() {}
46
+
47
+ /**
48
+ Callback after bridge creation
49
+ */
50
+ @objc
51
+ open func bridgeDidCreate(bridge: RCTBridge) {}
52
+ }
@@ -0,0 +1,20 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ This class determines the order of `ExpoReactDelegateHandler`.
5
+
6
+ The priority is only for internal use and we maintain a pre-defined `SUPPORTED_MODULE` map.
7
+ */
8
+ internal struct ModulePriorities {
9
+ static let SUPPORTED_MODULE = [
10
+ // {key}: {value}
11
+ // key: node package name
12
+ // value: priority value, the higher value takes precedence
13
+ "expo-screen-orientation": 10,
14
+ "expo-updates": 5,
15
+ ]
16
+
17
+ static func get(_ packageName: String) -> Int {
18
+ return SUPPORTED_MODULE[packageName] ?? 0
19
+ }
20
+ }
@@ -9,6 +9,20 @@ import CoreGraphics
9
9
  /// As an example, when the `CGPoint` type is used as an argument type, its instance can be
10
10
  /// created from an array of two doubles or an object with `x` and `y` fields.
11
11
 
12
+ // MARK: - Foundation
13
+
14
+ extension URL: ConvertibleArgument {
15
+ public static func convert(from value: Any?) throws -> Self {
16
+ if let uri = value as? String, let url = URL(string: uri) {
17
+ // `URL(string:)` is an optional init but it doesn't imply it's a valid URL,
18
+ // so here we don't check for the correctness of the URL.
19
+ // If it has no scheme, we assume it was the file path.
20
+ return url.scheme != nil ? url : URL(fileURLWithPath: uri)
21
+ }
22
+ throw Conversions.ConvertingError<URL>(value: value)
23
+ }
24
+ }
25
+
12
26
  // MARK: - UIKit
13
27
 
14
28
  extension UIColor: ConvertibleArgument {
@@ -18,7 +32,7 @@ extension UIColor: ConvertibleArgument {
18
32
  }
19
33
  if let components = value as? [Double] {
20
34
  let alpha = components.count > 3 ? components[3] : 1.0
21
- return Self.init(red: components[0], green: components[1], blue: components[2], alpha: alpha)
35
+ return Self.init(red: CGFloat(components[0]), green: CGFloat(components[1]), blue: CGFloat(components[2]), alpha: CGFloat(alpha))
22
36
  }
23
37
  if let value = value as? Int {
24
38
  return try Conversions.toColor(argb: UInt64(value)) as! Self
@@ -38,6 +38,17 @@ public final class ModuleHolder {
38
38
  post(event: .moduleCreate)
39
39
  }
40
40
 
41
+ // MARK: Constants
42
+
43
+ /**
44
+ Merges all `constants` definitions into one dictionary.
45
+ */
46
+ func getConstants() -> [String: Any?] {
47
+ return definition.constants.reduce(into: [String: Any?]()) { dict, definition in
48
+ dict.merge(definition.body()) { $1 }
49
+ }
50
+ }
51
+
41
52
  // MARK: Calling functions
42
53
 
43
54
  func call(function functionName: String, args: [Any], promise: Promise) {
@@ -1,3 +1,4 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
1
2
 
2
3
  /**
3
4
  `BaseModule` is just a stub class that fulfils `AnyModule` protocol requirement of public default initializer,
@@ -7,6 +8,9 @@
7
8
  open class BaseModule {
8
9
  public private(set) weak var appContext: AppContext?
9
10
 
11
+ @available(*, unavailable, message: "Module's initializer cannot be overriden, use \"onCreate\" definition component instead.")
12
+ public init() {}
13
+
10
14
  required public init(appContext: AppContext) {
11
15
  self.appContext = appContext
12
16
  }
@@ -21,7 +21,7 @@ public final class ModuleDefinition: AnyDefinition {
21
21
  var name: String
22
22
 
23
23
  let functions: [String : AnyFunction]
24
- let constants: [String : Any?]
24
+ let constants: [ConstantsDefinition]
25
25
  let eventListeners: [EventListener]
26
26
  let viewManager: ViewManagerDefinition?
27
27
 
@@ -45,11 +45,7 @@ public final class ModuleDefinition: AnyDefinition {
45
45
  dict[function.name] = function
46
46
  }
47
47
 
48
- self.constants = definitions
49
- .compactMap { $0 as? ConstantsDefinition }
50
- .reduce(into: [String : Any?]()) { dict, definition in
51
- dict.merge(definition.constants) { $1 }
52
- }
48
+ self.constants = definitions.compactMap { $0 as? ConstantsDefinition }
53
49
 
54
50
  self.eventListeners = definitions.compactMap { $0 as? EventListener }
55
51
 
@@ -90,7 +86,7 @@ internal struct ModuleNameDefinition: AnyDefinition {
90
86
  A definition for module's constants. Returned by `constants(() -> SomeType)` in module's definition.
91
87
  */
92
88
  internal struct ConstantsDefinition: AnyDefinition {
93
- let constants: [String : Any?]
89
+ let body: () -> [String: Any?]
94
90
  }
95
91
 
96
92
  /**
@@ -21,8 +21,15 @@ extension AnyModule {
21
21
  /**
22
22
  Definition function setting the module's constants to export.
23
23
  */
24
- public func constants(_ closure: () -> [String : Any?]) -> AnyDefinition {
25
- return ConstantsDefinition(constants: closure())
24
+ public func constants(_ body: @escaping () -> [String: Any?]) -> AnyDefinition {
25
+ return ConstantsDefinition(body: body)
26
+ }
27
+
28
+ /**
29
+ Definition function setting the module's constants to export.
30
+ */
31
+ public func constants(_ body: @autoclosure @escaping () -> [String: Any?]) -> AnyDefinition {
32
+ return ConstantsDefinition(body: body)
26
33
  }
27
34
 
28
35
  // MARK: - Functions
@@ -18,6 +18,12 @@ public protocol ModulesProviderProtocol: ModulesProviderObjCProtocol {
18
18
  Returns an array of classes that hooks into `ExpoAppDelegate` to receive app delegate events.
19
19
  */
20
20
  func getAppDelegateSubscribers() -> [ExpoAppDelegateSubscriber.Type]
21
+
22
+ typealias ExpoReactDelegateHandlerTupleType = (packageName: String, handler: ExpoReactDelegateHandler.Type)
23
+ /**
24
+ Returns an array of `ExpoReactDelegateHandlerTupleType` for `ReactDelegate` to hook React instance creation.
25
+ */
26
+ func getReactDelegateHandlers() -> [ExpoReactDelegateHandlerTupleType]
21
27
  }
22
28
 
23
29
  /**
@@ -33,4 +39,8 @@ open class ModulesProvider: NSObject, ModulesProviderProtocol, ModulesProviderOb
33
39
  open func getAppDelegateSubscribers() -> [ExpoAppDelegateSubscriber.Type] {
34
40
  return []
35
41
  }
42
+
43
+ open func getReactDelegateHandlers() -> [ExpoReactDelegateHandlerTupleType] {
44
+ return []
45
+ }
36
46
  }
@@ -67,7 +67,7 @@ public final class SwiftInteropBridge: NSObject {
67
67
  @objc
68
68
  public func exportedModulesConstants() -> [String: Any] {
69
69
  return registry.reduce(into: [String: Any]()) { acc, holder in
70
- acc[holder.name] = holder.definition.constants
70
+ acc[holder.name] = holder.getConstants()
71
71
  }
72
72
  }
73
73
 
@@ -0,0 +1,36 @@
1
+ import Quick
2
+ import Nimble
3
+
4
+ @testable import ExpoModulesCore
5
+
6
+ class ConstantsSpec: QuickSpec {
7
+ override func spec() {
8
+ let appContext = AppContext()
9
+
10
+ it("takes closure resolving to dictionary") {
11
+ let holder = mockModuleHolder(appContext) {
12
+ $0.constants {
13
+ return ["test": 123]
14
+ }
15
+ }
16
+ expect(holder.getConstants()["test"] as? Int) == 123
17
+ }
18
+
19
+ it("takes the dictionary") {
20
+ let holder = mockModuleHolder(appContext) {
21
+ $0.constants(["test": 123])
22
+ }
23
+ expect(holder.getConstants()["test"] as? Int) == 123
24
+ }
25
+
26
+ it("merges multiple constants definitions") {
27
+ let holder = mockModuleHolder(appContext) {
28
+ $0.constants(["test": 456, "test2": 789])
29
+ $0.constants(["test": 123])
30
+ }
31
+ let consts = holder.getConstants()
32
+ expect(consts["test"] as? Int) == 123
33
+ expect(consts["test2"] as? Int) == 789
34
+ }
35
+ }
36
+ }
@@ -8,6 +8,40 @@ import Nimble
8
8
 
9
9
  class ConvertiblesSpec: QuickSpec {
10
10
  override func spec() {
11
+ describe("URL") {
12
+ it("converts from remote url") {
13
+ let remoteUrlString = "https://expo.dev"
14
+ let url = try URL.convert(from: remoteUrlString)
15
+
16
+ expect(url.path) == ""
17
+ expect(url.absoluteString) == remoteUrlString
18
+ }
19
+
20
+ it("converts from file url") {
21
+ let fileUrlString = "file:///expo/tmp"
22
+ let url = try URL.convert(from: fileUrlString)
23
+
24
+ expect(url.path) == "/expo/tmp"
25
+ expect(url.absoluteString) == fileUrlString
26
+ expect(url.isFileURL) == true
27
+ }
28
+
29
+ it("converts from file path") {
30
+ let filePath = "/expo/image.png"
31
+ let url = try URL.convert(from: filePath)
32
+
33
+ expect(url.path) == filePath
34
+ expect(url.absoluteString) == "file://\(filePath)"
35
+ expect(url.isFileURL) == true
36
+ }
37
+
38
+ it("throws when no string") {
39
+ expect { try URL.convert(from: 29.5) }.to(
40
+ throwError(errorType: Conversions.ConvertingError<URL>.self)
41
+ )
42
+ }
43
+ }
44
+
11
45
  describe("CGPoint") {
12
46
  let x = -8.3
13
47
  let y = 4.6
@@ -0,0 +1,99 @@
1
+ // Copyright 2016-present 650 Industries. All rights reserved.
2
+
3
+ #import <XCTest/XCTest.h>
4
+ #import <ExpoModulesCore/EXAppDefines.h>
5
+
6
+ @interface EXAppDefines (EXAppDefinesWithUnloader)
7
+
8
+ + (void)_unload;
9
+
10
+ @end
11
+
12
+ @interface EXAppDefinesTest : XCTestCase
13
+
14
+ @end
15
+
16
+ @implementation EXAppDefinesTest
17
+
18
+ - (void)setUp
19
+ {
20
+ // EXAppDefines expects to load just once.
21
+ // To make it testable with difference test cases,
22
+ // we call the internal private `_unload` method to reset state.
23
+ [EXAppDefines performSelector:@selector(_unload)];
24
+ }
25
+
26
+ - (void)test_load
27
+ {
28
+ NSDictionary *defines = @{
29
+ @"APP_DEBUG": @(YES),
30
+ @"APP_RCT_DEBUG": @(YES),
31
+ @"APP_RCT_DEV": @(YES),
32
+ };
33
+ XCTAssertNoThrow([EXAppDefines load:defines]);
34
+ }
35
+
36
+ - (void)test_load_throwIfLoadedTwice
37
+ {
38
+ NSDictionary *defines = @{
39
+ @"APP_DEBUG": @(YES),
40
+ @"APP_RCT_DEBUG": @(YES),
41
+ @"APP_RCT_DEV": @(YES),
42
+ };
43
+ XCTAssertNoThrow([EXAppDefines load:defines]);
44
+ XCTAssertThrows([EXAppDefines load:defines]);
45
+ }
46
+
47
+ - (void)test_loadAndGetAppDebug_shouldMatchDebugDefines
48
+ {
49
+ NSDictionary *defines = @{
50
+ @"APP_DEBUG": @(YES),
51
+ @"APP_RCT_DEBUG": @(YES),
52
+ @"APP_RCT_DEV": @(YES),
53
+ };
54
+ [EXAppDefines load:defines];
55
+ XCTAssertEqual(EXAppDefines.APP_DEBUG, YES);
56
+ }
57
+
58
+ - (void)test_loadAndGetAppDebug_shouldMatchReleaseDefines
59
+ {
60
+ NSDictionary *defines = @{
61
+ @"APP_DEBUG": @(NO),
62
+ @"APP_RCT_DEBUG": @(NO),
63
+ @"APP_RCT_DEV": @(NO),
64
+ };
65
+ [EXAppDefines load:defines];
66
+ XCTAssertEqual(EXAppDefines.APP_DEBUG, NO);
67
+ }
68
+
69
+ - (void)test_getters_returnsDefaultValues
70
+ {
71
+ XCTAssertNoThrow([EXAppDefines load:@{}]);
72
+ XCTAssertEqual(EXAppDefines.APP_DEBUG, NO);
73
+ XCTAssertEqual(EXAppDefines.APP_RCT_DEBUG, NO);
74
+ XCTAssertEqual(EXAppDefines.APP_RCT_DEV, NO);
75
+ XCTAssertEqual(EXAppDefines.getAllDefines.count, 0u);
76
+ }
77
+
78
+ - (void)test_getAppDebug_throwIfNotLoaded
79
+ {
80
+ XCTAssertThrows(EXAppDefines.APP_DEBUG);
81
+ }
82
+
83
+ - (void)test_passExtraDefines_shouldGetMatchedDefines
84
+ {
85
+ NSDictionary *defines = @{
86
+ @"APP_DEBUG": @(YES),
87
+ @"APP_RCT_DEBUG": @(YES),
88
+ @"APP_RCT_DEV": @(YES),
89
+ @"foo": @1,
90
+ @"bar": @2,
91
+ };
92
+
93
+ [EXAppDefines load:defines];
94
+ NSDictionary *result = EXAppDefines.getAllDefines;
95
+ XCTAssertEqual([result[@"foo"] intValue], 1);
96
+ XCTAssertEqual([result[@"bar"] intValue], 2);
97
+ }
98
+
99
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "0.5.0",
3
+ "version": "0.6.3",
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": "^2.0.0"
44
44
  },
45
- "gitHead": "9faa58818454ba59dbff95077b9025a1c1cbd9fd"
45
+ "gitHead": "150733e32b766c53c573ed505d2fe42bd8ceaa75"
46
46
  }
@@ -28,7 +28,7 @@ export function requireNativeViewManager<P = any>(viewName: string): React.Compo
28
28
  if (!NativeUnimoduleProxy.viewManagersNames.includes(viewName)) {
29
29
  const exportedViewManagerNames = NativeUnimoduleProxy.viewManagersNames.join(', ');
30
30
  console.warn(
31
- `The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by @unimodules/react-native-adapter. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`
31
+ `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}].`
32
32
  );
33
33
  }
34
34
  }
@@ -1,9 +0,0 @@
1
- package expo.modules.core.interfaces
2
-
3
- import android.app.Application
4
- import android.content.res.Configuration
5
-
6
- interface ApplicationLifecycleListener {
7
- fun onCreate(application: Application) {}
8
- fun onConfigurationChanged(newConfig: Configuration) {}
9
- }
@@ -1,11 +0,0 @@
1
- package expo.modules.core.interfaces
2
-
3
- import android.app.Activity
4
- import android.os.Bundle
5
-
6
- interface ReactActivityLifecycleListener {
7
- fun onCreate(activity: Activity, savedInstanceState: Bundle?) {}
8
- fun onResume(activity: Activity) {}
9
- fun onPause(activity: Activity) {}
10
- fun onDestroy(activity: Activity) {}
11
- }