expo-modules-core 0.5.0 → 0.6.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.
- package/CHANGELOG.md +14 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.java +10 -0
- package/android/src/main/java/expo/modules/core/interfaces/{ReactActivityHandler.kt → ReactActivityHandler.java} +9 -6
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java +14 -0
- package/android/src/main/java/expo/modules/core/interfaces/{ReactNativeHostHandler.kt → ReactNativeHostHandler.java} +22 -17
- package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +1 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +6 -1
- package/build/NativeViewManagerAdapter.native.js +1 -1
- package/build/NativeViewManagerAdapter.native.js.map +1 -1
- package/ios/AppDelegates/EXAppDelegateWrapper.h +3 -0
- package/ios/AppDelegates/EXAppDelegateWrapper.m +9 -6
- package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -0
- package/ios/AppDelegates/ExpoAppDelegate.swift +18 -0
- package/ios/EXAppDefines.h +26 -0
- package/ios/EXAppDefines.m +61 -0
- package/ios/ExpoModulesCore.podspec +3 -3
- package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +18 -5
- package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h +16 -0
- package/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.m +49 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper+Private.h +18 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper.h +25 -0
- package/ios/ReactDelegates/EXReactDelegateWrapper.m +40 -0
- package/ios/ReactDelegates/ExpoReactDelegate.swift +37 -0
- package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +52 -0
- package/ios/ReactDelegates/ModulePriorities.swift +20 -0
- package/ios/Swift/Arguments/Convertibles.swift +15 -1
- package/ios/Swift/ModuleHolder.swift +11 -0
- package/ios/Swift/Modules/Module.swift +4 -0
- package/ios/Swift/Modules/ModuleDefinition.swift +3 -7
- package/ios/Swift/Modules/ModuleDefinitionComponents.swift +9 -2
- package/ios/Swift/ModulesProvider.swift +10 -0
- package/ios/Swift/SwiftInteropBridge.swift +1 -1
- package/ios/Tests/ConstantsSpec.swift +36 -0
- package/ios/Tests/ConvertiblesSpec.swift +34 -0
- package/ios/Tests/EXAppDefinesTest.m +99 -0
- package/package.json +2 -2
- package/src/NativeViewManagerAdapter.native.tsx +1 -1
- package/android/src/main/java/expo/modules/core/interfaces/ApplicationLifecycleListener.kt +0 -9
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.6.0 — 2021-12-03
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- 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))
|
|
18
|
+
- 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))
|
|
19
|
+
|
|
20
|
+
### 💡 Others
|
|
21
|
+
|
|
22
|
+
- Add parameter to `ReactNativeHostHandler.onDidCreateReactInstanceManager` on Android. ([#15221](https://github.com/expo/expo/pull/15221) by [@esamelson](https://github.com/esamelson))
|
|
23
|
+
- 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))
|
|
24
|
+
|
|
13
25
|
## 0.5.0 — 2021-11-17
|
|
14
26
|
|
|
15
27
|
### 🎉 New features
|
|
@@ -24,6 +36,8 @@
|
|
|
24
36
|
- [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
37
|
- [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
38
|
- [Sweet API] Added experimental implementation of the new API in Kotlin. (by [@lukmccall](https://github.com/lukmccall))
|
|
39
|
+
- Introduce EXAppDefines to get app building configurations. ([#14428](https://github.com/expo/expo/pull/14428) by [@kudo](https://github.com/kudo))
|
|
40
|
+
- Introduce React Native bridge delegate handlers on iOS. ([#15138](https://github.com/expo/expo/pull/15138) by [@kudo](https://github.com/kudo))
|
|
27
41
|
|
|
28
42
|
### 🐛 Bug fixes
|
|
29
43
|
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '0.
|
|
6
|
+
version = '0.6.0'
|
|
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.
|
|
61
|
+
versionName "0.6.0"
|
|
62
62
|
}
|
|
63
63
|
lintOptions {
|
|
64
64
|
abortOnError false
|
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
+
@Nullable
|
|
18
|
+
default ReactRootView createReactRootView(Activity activity) {
|
|
19
|
+
return null;
|
|
17
20
|
}
|
|
18
21
|
}
|
package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.java
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.core.interfaces;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.os.Bundle;
|
|
5
|
+
|
|
6
|
+
public interface ReactActivityLifecycleListener {
|
|
7
|
+
default void onCreate(Activity activity, Bundle savedInstanceState) {}
|
|
8
|
+
|
|
9
|
+
default void onResume(Activity activity) {}
|
|
10
|
+
|
|
11
|
+
default void onPause(Activity activity) {}
|
|
12
|
+
|
|
13
|
+
default void onDestroy(Activity activity) {}
|
|
14
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
48
|
-
reactApplicationContext
|
|
49
|
-
jsContext
|
|
50
|
-
useDeveloperSupport
|
|
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
|
-
|
|
62
|
+
default void onWillCreateReactInstanceManager(boolean useDeveloperSupport) {}
|
|
58
63
|
|
|
59
64
|
/**
|
|
60
65
|
* Callback after {@link ReactInstanceManager} creation
|
|
61
66
|
*/
|
|
62
|
-
|
|
67
|
+
default void onDidCreateReactInstanceManager(ReactInstanceManager reactInstanceManager, boolean useDeveloperSupport) {}
|
|
63
68
|
|
|
64
69
|
//endregion
|
|
65
70
|
}
|
|
@@ -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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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,6 +15,9 @@ 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 {
|
|
@@ -261,4 +265,18 @@ open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
|
|
|
261
265
|
public static func getSubscriber(_ name: String) -> ExpoAppDelegateSubscriberProtocol? {
|
|
262
266
|
return subscribers.first { String(describing: $0) == name }
|
|
263
267
|
}
|
|
268
|
+
|
|
269
|
+
@objc
|
|
270
|
+
public static func registerReactDelegateHandlersFrom(modulesProvider: ModulesProviderObjCProtocol) {
|
|
271
|
+
guard let provider = modulesProvider as? ModulesProviderProtocol else {
|
|
272
|
+
fatalError("Expo modules provider must implement `ModulesProviderProtocol`.")
|
|
273
|
+
}
|
|
274
|
+
provider.getReactDelegateHandlers()
|
|
275
|
+
.sorted { (tuple1, tuple2) -> Bool in
|
|
276
|
+
return ModulePriorities.get(tuple1.packageName) > ModulePriorities.get(tuple2.packageName)
|
|
277
|
+
}
|
|
278
|
+
.forEach { handlerTuple in
|
|
279
|
+
reactDelegateHandlers.append(handlerTuple.handler.init())
|
|
280
|
+
}
|
|
281
|
+
}
|
|
264
282
|
}
|
|
@@ -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
|
|
211
|
-
|
|
212
|
-
|
|
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: [
|
|
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
|
|
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(_
|
|
25
|
-
return ConstantsDefinition(
|
|
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.
|
|
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.
|
|
3
|
+
"version": "0.6.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": "^2.0.0"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "2e5c6983b86d5ecfca028ba64002897d8adc2cc4"
|
|
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
|
|
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
|
-
}
|
package/android/src/main/java/expo/modules/core/interfaces/ReactActivityLifecycleListener.kt
DELETED
|
@@ -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
|
-
}
|