expo-modules-core 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/CHANGELOG.md +18 -5
  2. package/android/CMakeLists.txt +154 -0
  3. package/android/build.gradle +293 -5
  4. package/android/src/main/cpp/Exceptions.cpp +22 -0
  5. package/android/src/main/cpp/Exceptions.h +38 -0
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  7. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  8. package/android/src/main/cpp/JNIFunctionBody.cpp +29 -0
  9. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +19 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  14. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  15. package/android/src/main/cpp/JavaScriptModuleObject.cpp +138 -0
  16. package/android/src/main/cpp/JavaScriptModuleObject.h +122 -0
  17. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  18. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  19. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  20. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  21. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  22. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  23. package/android/src/main/cpp/MethodMetadata.cpp +230 -0
  24. package/android/src/main/cpp/MethodMetadata.h +92 -0
  25. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  26. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  27. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  28. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +49 -1
  29. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  30. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  31. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +39 -3
  32. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  33. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  34. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  35. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +19 -14
  36. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +29 -7
  37. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -13
  38. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +18 -0
  39. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +56 -0
  41. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +28 -0
  42. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +18 -0
  43. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  44. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  45. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +44 -0
  46. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  47. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  48. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +15 -5
  49. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +65 -111
  50. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +35 -2
  51. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  52. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  53. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  54. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  55. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  56. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  57. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  58. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  59. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  60. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  61. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  62. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +5 -0
  63. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  64. package/build/NativeModulesProxy.native.js +9 -3
  65. package/build/NativeModulesProxy.native.js.map +1 -1
  66. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  67. package/ios/ExpoModulesCore.podspec +1 -1
  68. package/ios/JSI/EXJSIConversions.mm +6 -0
  69. package/ios/JSI/EXJSIInstaller.h +15 -21
  70. package/ios/JSI/EXJSIInstaller.mm +39 -3
  71. package/ios/JSI/EXJSIUtils.h +47 -3
  72. package/ios/JSI/EXJSIUtils.mm +88 -4
  73. package/ios/JSI/EXJavaScriptObject.h +11 -18
  74. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  75. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  76. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  77. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  78. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  79. package/ios/JSI/EXJavaScriptValue.h +3 -2
  80. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  81. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  82. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  83. package/ios/JSI/EXObjectDeallocator.h +27 -0
  84. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  85. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  86. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  87. package/ios/JSI/JavaScriptValue.swift +7 -0
  88. package/ios/JSI/TypedArray.cpp +67 -0
  89. package/ios/JSI/TypedArray.h +46 -0
  90. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  91. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  92. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +85 -77
  93. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  94. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  95. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  96. package/ios/Swift/AppContext.swift +208 -28
  97. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  98. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  99. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  100. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  101. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  102. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  103. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  104. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  105. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  106. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  107. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  108. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  109. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  110. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  111. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  112. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  113. package/ios/Swift/Exceptions/Exception.swift +8 -6
  114. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  115. package/ios/Swift/ExpoBridgeModule.m +5 -0
  116. package/ios/Swift/ExpoBridgeModule.swift +65 -0
  117. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  118. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  119. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  120. package/ios/Swift/JavaScriptUtils.swift +32 -57
  121. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  122. package/ios/Swift/Logging/LogType.swift +62 -0
  123. package/ios/Swift/Logging/Logger.swift +198 -0
  124. package/ios/Swift/ModuleHolder.swift +19 -54
  125. package/ios/Swift/ModuleRegistry.swift +7 -1
  126. package/ios/Swift/Modules/AnyModule.swift +3 -3
  127. package/ios/Swift/ModulesProvider.swift +2 -0
  128. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  129. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  130. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  131. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  132. package/ios/Swift/Promise.swift +12 -3
  133. package/ios/Swift/Records/Field.swift +2 -2
  134. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  135. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  136. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  137. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  138. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  139. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  140. package/ios/Swift/Utilities.swift +28 -0
  141. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  142. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  143. package/ios/Tests/ClassComponentSpec.swift +210 -0
  144. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  145. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  146. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  147. package/ios/Tests/FunctionSpec.swift +167 -118
  148. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  149. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  150. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  151. package/ios/Tests/TypedArraysSpec.swift +136 -0
  152. package/package.json +2 -2
  153. package/src/NativeModulesProxy.native.ts +13 -3
  154. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  155. package/tsconfig.json +1 -1
  156. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  157. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  158. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  159. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  160. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  161. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  162. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  163. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  164. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  165. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  166. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  167. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -28,6 +28,46 @@ static const NSString *methodInfoKeyKey = @"key";
28
28
  static const NSString *methodInfoNameKey = @"name";
29
29
  static const NSString *methodInfoArgumentsCountKey = @"argumentsCount";
30
30
 
31
+ @interface EXModulesProxyConfig ()
32
+
33
+ @property (readonly) NSMutableDictionary *exportedConstants;
34
+ @property (readonly) NSMutableDictionary *methodNames;
35
+ @property (readonly) NSMutableDictionary *viewManagerMetadata;
36
+
37
+ @end
38
+
39
+ @implementation EXModulesProxyConfig
40
+
41
+ - (instancetype)initWithConstants:(nonnull NSDictionary *)constants
42
+ methodNames:(nonnull NSDictionary *)methodNames
43
+ viewManagers:(nonnull NSDictionary *)viewManagerMetadata
44
+ {
45
+ if (self = [super init]) {
46
+ _exportedConstants = constants;
47
+ _methodNames = methodNames;
48
+ _viewManagerMetadata = viewManagerMetadata;
49
+ }
50
+ return self;
51
+ }
52
+
53
+ - (void)addEntriesFromConfig:(nonnull const EXModulesProxyConfig*)config
54
+ {
55
+ [_exportedConstants addEntriesFromDictionary:config.exportedConstants];
56
+ [_methodNames addEntriesFromDictionary:config.methodNames];
57
+ [_viewManagerMetadata addEntriesFromDictionary:config.viewManagerMetadata];
58
+ }
59
+
60
+ - (nonnull NSDictionary<NSString *, id> *)toDictionary
61
+ {
62
+ NSMutableDictionary <NSString *, id> *constantsAccumulator = [NSMutableDictionary dictionary];
63
+ constantsAccumulator[viewManagersMetadataKeyPath] = _viewManagerMetadata;
64
+ constantsAccumulator[exportedConstantsKeyPath] = _exportedConstants;
65
+ constantsAccumulator[exportedMethodsNamesKeyPath] = _methodNames;
66
+ return constantsAccumulator;
67
+ }
68
+
69
+ @end
70
+
31
71
  @interface RCTBridge (RegisterAdditionalModuleClasses)
32
72
 
33
73
  - (NSArray<RCTModuleData *> *)registerModulesForClasses:(NSArray<Class> *)moduleClasses;
@@ -51,9 +91,12 @@ static const NSString *methodInfoArgumentsCountKey = @"argumentsCount";
51
91
 
52
92
  @end
53
93
 
54
- @implementation EXNativeModulesProxy
94
+ @implementation EXNativeModulesProxy {
95
+ __weak EXAppContext * _Nullable _appContext;
96
+ }
55
97
 
56
98
  @synthesize bridge = _bridge;
99
+ @synthesize nativeModulesConfig = _nativeModulesConfig;
57
100
 
58
101
  RCT_EXPORT_MODULE(NativeUnimoduleProxy)
59
102
 
@@ -65,7 +108,6 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
65
108
  {
66
109
  if (self = [super init]) {
67
110
  _exModuleRegistry = moduleRegistry != nil ? moduleRegistry : [[EXModuleRegistryProvider new] moduleRegistry];
68
- _swiftInteropBridge = [[SwiftInteropBridge alloc] initWithModulesProvider:[EXNativeModulesProxy getExpoModulesProvider] legacyModuleRegistry:_exModuleRegistry];
69
111
  _exportedMethodsKeys = [NSMutableDictionary dictionary];
70
112
  _exportedMethodsReverseKeys = [NSMutableDictionary dictionary];
71
113
  _ownsModuleRegistry = moduleRegistry == nil;
@@ -73,6 +115,18 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
73
115
  return self;
74
116
  }
75
117
 
118
+ /**
119
+ The initializer for Expo Go to pass a custom `EXModuleRegistry`
120
+ other than the default one from `EXModuleRegistryProvider`.
121
+ The `EXModuleRegistry` is still owned by this class.
122
+ */
123
+ - (instancetype)initWithCustomModuleRegistry:(nonnull EXModuleRegistry *)moduleRegistry
124
+ {
125
+ self = [self initWithModuleRegistry:moduleRegistry];
126
+ self.ownsModuleRegistry = YES;
127
+ return self;
128
+ }
129
+
76
130
  /**
77
131
  Convenience initializer used by React Native in the new setup, where the modules are registered automatically.
78
132
  */
@@ -88,12 +142,12 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
88
142
  return YES;
89
143
  }
90
144
 
91
- - (NSDictionary *)constantsToExport
145
+ - (nonnull EXModulesProxyConfig *)nativeModulesConfig
92
146
  {
93
- // Install ExpoModules host object in the runtime. It's probably not the right place,
94
- // but it's the earliest moment in bridge's lifecycle when we have access to the runtime.
95
- [self installExpoModulesHostObject];
96
-
147
+ if (_nativeModulesConfig) {
148
+ return _nativeModulesConfig;
149
+ }
150
+
97
151
  NSMutableDictionary <NSString *, id> *exportedModulesConstants = [NSMutableDictionary dictionary];
98
152
  // Grab all the constants exported by modules
99
153
  for (EXExportedModule *exportedModule in [_exModuleRegistry getAllExportedModules]) {
@@ -103,7 +157,6 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
103
157
  continue;
104
158
  }
105
159
  }
106
- [exportedModulesConstants addEntriesFromDictionary:[_swiftInteropBridge exportedModulesConstants]];
107
160
 
108
161
  // Also add `exportedMethodsNames`
109
162
  NSMutableDictionary<const NSString *, NSMutableArray<NSMutableDictionary<const NSString *, id> *> *> *exportedMethodsNamesAccumulator = [NSMutableDictionary dictionary];
@@ -120,10 +173,7 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
120
173
  }];
121
174
  [self assignExportedMethodsKeys:exportedMethodsNamesAccumulator[exportedModuleName] forModuleName:exportedModuleName];
122
175
  }
123
-
124
- // Add entries from Swift modules
125
- [exportedMethodsNamesAccumulator addEntriesFromDictionary:[_swiftInteropBridge exportedFunctionNames]];
126
-
176
+
127
177
  // Also, add `viewManagersMetadata` for sanity check and testing purposes -- with names we know what managers to mock on UIManager
128
178
  NSArray<EXViewManager *> *viewManagers = [_exModuleRegistry getAllViewManagers];
129
179
  NSMutableDictionary<NSString *, NSDictionary *> *viewManagersMetadata = [[NSMutableDictionary alloc] initWithCapacity:[viewManagers count]];
@@ -133,20 +183,28 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
133
183
  @"propsNames": [[viewManager getPropsNames] allKeys]
134
184
  };
135
185
  }
186
+
187
+ EXModulesProxyConfig *config = [[EXModulesProxyConfig alloc] initWithConstants:exportedModulesConstants
188
+ methodNames:exportedMethodsNamesAccumulator
189
+ viewManagers:viewManagersMetadata];
190
+ // decorate legacy config with sweet expo-modules config
191
+ [config addEntriesFromConfig:[_appContext expoModulesConfig]];
192
+
193
+ _nativeModulesConfig = config;
194
+ return config;
195
+ }
136
196
 
137
- // Add entries from Swift view managers
138
- [viewManagersMetadata addEntriesFromDictionary:[_swiftInteropBridge viewManagersMetadata]];
139
-
140
- NSMutableDictionary <NSString *, id> *constantsAccumulator = [NSMutableDictionary dictionary];
141
- constantsAccumulator[viewManagersMetadataKeyPath] = viewManagersMetadata;
142
- constantsAccumulator[exportedConstantsKeyPath] = exportedModulesConstants;
143
- constantsAccumulator[exportedMethodsNamesKeyPath] = exportedMethodsNamesAccumulator;
144
-
145
- return constantsAccumulator;
197
+ - (nonnull NSDictionary *)constantsToExport
198
+ {
199
+ return [self.nativeModulesConfig toDictionary];
146
200
  }
147
201
 
148
202
  - (void)setBridge:(RCTBridge *)bridge
149
203
  {
204
+ _appContext = [(ExpoBridgeModule *)[bridge moduleForClass:ExpoBridgeModule.class] appContext];
205
+ [_appContext setLegacyModuleRegistry:_exModuleRegistry];
206
+ [_appContext setLegacyModulesProxy:self];
207
+
150
208
  if (!_bridge) {
151
209
  // The `setBridge` can be called during module setup or after. Registering more modules
152
210
  // during setup causes a crash due to mutating `_moduleDataByID` while it's being enumerated.
@@ -159,16 +217,17 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
159
217
  });
160
218
  }
161
219
  }
162
- [_swiftInteropBridge setReactBridge:bridge];
163
220
  _bridge = bridge;
164
221
  }
165
222
 
166
223
  RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNameOrKey arguments:(NSArray *)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
167
224
  {
168
- if ([_swiftInteropBridge hasModule:moduleName]) {
169
- [_swiftInteropBridge callFunction:methodNameOrKey onModule:moduleName withArgs:arguments resolve:resolve reject:reject];
225
+ // Backwards compatibility for the new architecture
226
+ if ([_appContext hasModule:moduleName]) {
227
+ [_appContext callFunction:methodNameOrKey onModule:moduleName withArgs:arguments resolve:resolve reject:reject];
170
228
  return;
171
229
  }
230
+
172
231
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
173
232
  if (module == nil) {
174
233
  NSString *reason = [NSString stringWithFormat:@"No exported module was found for name '%@'. Are you sure all the packages are linked correctly?", moduleName];
@@ -201,42 +260,6 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
201
260
  });
202
261
  }
203
262
 
204
- - (id)callMethodSync:(NSString *)moduleName methodName:(NSString *)methodName arguments:(NSArray *)arguments
205
- {
206
- if ([_swiftInteropBridge hasModule:moduleName]) {
207
- return [_swiftInteropBridge callFunctionSync:methodName onModule:moduleName withArgs:arguments];
208
- }
209
- return (id)kCFNull;
210
- }
211
-
212
- #pragma mark - Statics
213
-
214
- + (ModulesProvider *)getExpoModulesProvider
215
- {
216
- // Dynamically gets the modules provider class.
217
- // NOTE: This needs to be versioned in Expo Go.
218
- Class generatedExpoModulesProvider;
219
-
220
- // [0] When ExpoModulesCore is built as separated framework/module,
221
- // we should explicitly load main bundle's `ExpoModulesProvider` class.
222
- NSString *bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
223
- if (bundleName != nil) {
224
- generatedExpoModulesProvider = NSClassFromString([NSString stringWithFormat:@"%@.ExpoModulesProvider", bundleName]);
225
- if (generatedExpoModulesProvider != nil) {
226
- return [generatedExpoModulesProvider new];
227
- }
228
- }
229
-
230
- // [1] Fallback to load `ExpoModulesProvider` class from the current module.
231
- generatedExpoModulesProvider = NSClassFromString(@"ExpoModulesProvider");
232
- if (generatedExpoModulesProvider != nil) {
233
- return [generatedExpoModulesProvider new];
234
- }
235
-
236
- // [2] Fallback to load `ModulesProvider` if `ExpoModulesProvider` was not generated
237
- return [ModulesProvider new];
238
- }
239
-
240
263
  #pragma mark - Privates
241
264
 
242
265
  - (void)registerExpoModulesInBridge:(RCTBridge *)bridge
@@ -250,7 +273,7 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
250
273
  NSMutableSet *visitedSweetModules = [NSMutableSet new];
251
274
 
252
275
  // Add dynamic wrappers for view modules written in Sweet API.
253
- for (ViewModuleWrapper *swiftViewModule in [_swiftInteropBridge getViewManagers]) {
276
+ for (ViewModuleWrapper *swiftViewModule in [_appContext getViewManagers]) {
254
277
  Class wrappedViewModuleClass = [self registerComponentData:swiftViewModule inBridge:bridge];
255
278
  [additionalModuleClasses addObject:wrappedViewModuleClass];
256
279
  [visitedSweetModules addObject:swiftViewModule.name];
@@ -300,7 +323,7 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
300
323
 
301
324
  // Get the instance of `EXReactEventEmitter` bridge module and give it access to the interop bridge.
302
325
  EXReactNativeEventEmitter *eventEmitter = [bridge moduleForClass:[EXReactNativeEventEmitter class]];
303
- [eventEmitter setSwiftInteropBridge:_swiftInteropBridge];
326
+ [eventEmitter setAppContext:_appContext];
304
327
 
305
328
  // As the last step, when the registry is owned,
306
329
  // register the event emitter and initialize the registry.
@@ -405,19 +428,4 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
405
428
  }
406
429
  }
407
430
 
408
- /**
409
- Installs ExpoModules host object in the runtime that the current bridge operates on.
410
- */
411
- - (void)installExpoModulesHostObject
412
- {
413
- facebook::jsi::Runtime *jsiRuntime = [_bridge respondsToSelector:@selector(runtime)] ? reinterpret_cast<facebook::jsi::Runtime *>(_bridge.runtime) : nullptr;
414
-
415
- if (jsiRuntime) {
416
- EXJavaScriptRuntime *runtime = [[EXJavaScriptRuntime alloc] initWithRuntime:jsiRuntime callInvoker:_bridge.jsCallInvoker];
417
-
418
- [EXJavaScriptRuntimeManager installExpoModulesToRuntime:runtime withSwiftInterop:_swiftInteropBridge];
419
- [_swiftInteropBridge setRuntime:runtime];
420
- }
421
- }
422
-
423
431
  @end
@@ -0,0 +1,17 @@
1
+ import Foundation
2
+
3
+ public class NativeModulesProxyModule: Module {
4
+ public static let moduleName = "NativeModulesProxy"
5
+
6
+ public func definition() -> ModuleDefinition {
7
+ Name(Self.moduleName)
8
+
9
+ Constants { () -> [String: Any?] in
10
+ guard let config = self.appContext?.legacyModulesProxy?.nativeModulesConfig else {
11
+ // TODO: Throw, but what?
12
+ return [:]
13
+ }
14
+ return config.toDictionary()
15
+ }
16
+ }
17
+ }
@@ -9,10 +9,10 @@
9
9
 
10
10
  // Swift compatibility headers (e.g. `ExpoModulesCore-Swift.h`) are not available in headers,
11
11
  // so we use class forward declaration here. Swift header must be imported in the `.m` file.
12
- @class SwiftInteropBridge;
12
+ @class EXAppContext;
13
13
 
14
14
  @interface EXReactNativeEventEmitter : RCTEventEmitter <EXInternalModule, EXBridgeModule, EXModuleRegistryConsumer, EXEventEmitterService>
15
15
 
16
- @property (nonatomic, strong) SwiftInteropBridge *swiftInteropBridge;
16
+ @property (nonatomic, strong) EXAppContext *appContext;
17
17
 
18
18
  @end
@@ -44,8 +44,9 @@
44
44
  {
45
45
  NSMutableSet<NSString *> *eventsAccumulator = [NSMutableSet set];
46
46
 
47
- if (_swiftInteropBridge) {
48
- [eventsAccumulator addObjectsFromArray:[_swiftInteropBridge getSupportedEvents]];
47
+ // Backwards compatibility for the new architecture
48
+ if (_appContext) {
49
+ [eventsAccumulator addObjectsFromArray:[_appContext getSupportedEvents]];
49
50
  }
50
51
  for (EXExportedModule *exportedModule in [_exModuleRegistry getAllExportedModules]) {
51
52
  if ([exportedModule conformsToProtocol:@protocol(EXEventEmitter)]) {
@@ -60,10 +61,12 @@ RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString
60
61
  {
61
62
  [self addListener:eventName];
62
63
 
63
- if ([_swiftInteropBridge hasModule:moduleName]) {
64
- [_swiftInteropBridge modifyEventListenersCount:moduleName count:1];
64
+ // Backwards compatibility for the new architecture
65
+ if ([_appContext hasModule:moduleName]) {
66
+ [_appContext modifyEventListenersCount:moduleName count:1];
65
67
  return;
66
68
  }
69
+
67
70
  // Validate module
68
71
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
69
72
 
@@ -101,10 +104,12 @@ RCT_EXPORT_METHOD(removeProxiedListeners:(NSString *)moduleName count:(double)co
101
104
  {
102
105
  [self removeListeners:count];
103
106
 
104
- if ([_swiftInteropBridge hasModule:moduleName]) {
105
- [_swiftInteropBridge modifyEventListenersCount:moduleName count:-count];
107
+ // Backwards compatibility for the new architecture
108
+ if ([_appContext hasModule:moduleName]) {
109
+ [_appContext modifyEventListenersCount:moduleName count:-count];
106
110
  return;
107
111
  }
112
+
108
113
  // Validate module
109
114
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
110
115
 
@@ -1,8 +1,10 @@
1
1
  import UIKit
2
+
2
3
  /**
3
4
  The app context is an interface to a single Expo app.
4
5
  */
5
- public final class AppContext {
6
+ @objc(EXAppContext)
7
+ public final class AppContext: NSObject {
6
8
  internal static func create() -> AppContext {
7
9
  let appContext = AppContext()
8
10
 
@@ -13,28 +15,48 @@ public final class AppContext {
13
15
  /**
14
16
  The module registry for the app context.
15
17
  */
16
- public private(set) lazy var moduleRegistry: ModuleRegistry = ModuleRegistry(appContext: self)
18
+ public private(set) lazy var moduleRegistry: ModuleRegistry = {
19
+ isModuleRegistryInitialized = true
20
+ return ModuleRegistry(appContext: self)
21
+ }()
22
+
23
+ /**
24
+ Whether the module registry for this app context has already been initialized.
25
+ */
26
+ private var isModuleRegistryInitialized: Bool = false
17
27
 
18
28
  /**
19
29
  The legacy module registry with modules written in the old-fashioned way.
20
30
  */
21
- public private(set) weak var legacyModuleRegistry: EXModuleRegistry?
31
+ @objc
32
+ public weak var legacyModuleRegistry: EXModuleRegistry?
33
+
34
+ @objc
35
+ public weak var legacyModulesProxy: LegacyNativeModulesProxy?
22
36
 
23
37
  /**
24
- React bridge of the context's app.
38
+ React bridge of the context's app. Can be `nil` when the bridge
39
+ hasn't been propagated to the bridge modules yet (see ``ExpoBridgeModule``),
40
+ or when the app context is "bridgeless" (for example in native unit tests).
25
41
  */
42
+ @objc
26
43
  public internal(set) weak var reactBridge: RCTBridge?
27
44
 
28
45
  /**
29
46
  JSI runtime of the running app.
30
47
  */
31
- public internal(set) var runtime: JavaScriptRuntime? {
48
+ @objc
49
+ public var runtime: JavaScriptRuntime? {
32
50
  didSet {
33
- // When the runtime is unpinned from the context (e.g. deallocated),
34
- // we should make sure to release all JS objects from the memory.
35
- // Otherwise the JSCRuntime asserts may fail on deallocation.
36
51
  if runtime == nil {
52
+ // When the runtime is unpinned from the context (e.g. deallocated),
53
+ // we should make sure to release all JS objects from the memory.
54
+ // Otherwise the JSCRuntime asserts may fail on deallocation.
37
55
  releaseRuntimeObjects()
56
+ } else if runtime != oldValue {
57
+ // Try to install ExpoModules host object automatically when the runtime changes.
58
+ // TODO: Should we uninstall in the old runtime? (@tsapeta)
59
+ try? installExpoModulesHostObject()
38
60
  }
39
61
  }
40
62
  }
@@ -42,17 +64,20 @@ public final class AppContext {
42
64
  /**
43
65
  Designated initializer without modules provider.
44
66
  */
45
- public init() {
67
+ public override init() {
68
+ super.init()
46
69
  listenToClientAppNotifications()
47
70
  }
48
71
 
49
- /**
50
- Initializes the app context and registers provided modules in the module registry.
51
- */
52
- public convenience init(withModulesProvider provider: ModulesProviderProtocol, legacyModuleRegistry: EXModuleRegistry?) {
53
- self.init()
54
- self.legacyModuleRegistry = legacyModuleRegistry
72
+ @discardableResult
73
+ public func useModulesProvider(_ providerName: String) -> Self {
74
+ return useModulesProvider(Self.modulesProvider(withName: providerName))
75
+ }
76
+
77
+ @discardableResult
78
+ public func useModulesProvider(_ provider: ModulesProvider) -> Self {
55
79
  moduleRegistry.register(fromProvider: provider)
80
+ return self
56
81
  }
57
82
 
58
83
  /**
@@ -134,14 +159,139 @@ public final class AppContext {
134
159
  }
135
160
  }
136
161
 
162
+ // MARK: - Interop with NativeModulesProxy
163
+
164
+ /**
165
+ Returns view modules wrapped by the base `ViewModuleWrapper` class.
166
+ */
167
+ @objc
168
+ public func getViewManagers() -> [ViewModuleWrapper] {
169
+ return moduleRegistry.compactMap { holder in
170
+ if holder.definition.viewManager != nil {
171
+ return ViewModuleWrapper(holder)
172
+ } else {
173
+ return nil
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ Returns a bool whether the module with given name is registered in this context.
180
+ */
181
+ @objc
182
+ public func hasModule(_ moduleName: String) -> Bool {
183
+ return moduleRegistry.has(moduleWithName: moduleName)
184
+ }
185
+
186
+ /**
187
+ Returns an array of names of the modules registered in the module registry.
188
+ */
189
+ @objc
190
+ public func getModuleNames() -> [String] {
191
+ return moduleRegistry.getModuleNames()
192
+ }
193
+
194
+ /**
195
+ Returns a JavaScript object that represents a module with given name.
196
+ When remote debugging is enabled, this will always return `nil`.
197
+ */
198
+ @objc
199
+ public func getNativeModuleObject(_ moduleName: String) -> JavaScriptObject? {
200
+ return moduleRegistry.get(moduleHolderForName: moduleName)?.javaScriptObject
201
+ }
202
+
203
+ /**
204
+ Returns an array of event names supported by all Swift modules.
205
+ */
206
+ @objc
207
+ public func getSupportedEvents() -> [String] {
208
+ return moduleRegistry.reduce(into: [String]()) { events, holder in
209
+ events.append(contentsOf: holder.definition.eventNames)
210
+ }
211
+ }
212
+
213
+ /**
214
+ Modifies listeners count for module with given name. Depending on the listeners count,
215
+ `onStartObserving` and `onStopObserving` are called.
216
+ */
217
+ @objc
218
+ public func modifyEventListenersCount(_ moduleName: String, count: Int) {
219
+ moduleRegistry
220
+ .get(moduleHolderForName: moduleName)?
221
+ .modifyListenersCount(count)
222
+ }
223
+
224
+ /**
225
+ Asynchronously calls module's function with given arguments.
226
+ */
227
+ @objc
228
+ public func callFunction(
229
+ _ functionName: String,
230
+ onModule moduleName: String,
231
+ withArgs args: [Any],
232
+ resolve: @escaping EXPromiseResolveBlock,
233
+ reject: @escaping EXPromiseRejectBlock
234
+ ) {
235
+ moduleRegistry
236
+ .get(moduleHolderForName: moduleName)?
237
+ .call(function: functionName, args: args) { result in
238
+ switch result {
239
+ case .failure(let error):
240
+ reject(error.code, error.description, error)
241
+ case .success(let value):
242
+ resolve(value)
243
+ }
244
+ }
245
+ }
246
+
247
+ @objc
248
+ public final lazy var expoModulesConfig = ModulesProxyConfig(constants: self.exportedModulesConstants(),
249
+ methodNames: self.exportedFunctionNames(),
250
+ viewManagers: self.viewManagersMetadata())
251
+
252
+ private func exportedFunctionNames() -> [String: [[String: Any]]] {
253
+ var constants = [String: [[String: Any]]]()
254
+
255
+ for holder in moduleRegistry {
256
+ constants[holder.name] = holder.definition.functions.map({ functionName, function in
257
+ return [
258
+ "name": functionName,
259
+ "argumentsCount": function.argumentsCount,
260
+ "key": functionName
261
+ ]
262
+ })
263
+ }
264
+ return constants
265
+ }
266
+
267
+ private func exportedModulesConstants() -> [String: Any] {
268
+ return moduleRegistry
269
+ // prevent infinite recursion - exclude NativeProxyModule constants
270
+ .filter { $0.name != NativeModulesProxyModule.moduleName }
271
+ .reduce(into: [String: Any]()) { acc, holder in
272
+ acc[holder.name] = holder.getConstants()
273
+ }
274
+ }
275
+
276
+ private func viewManagersMetadata() -> [String: Any] {
277
+ return moduleRegistry.reduce(into: [String: Any]()) { acc, holder in
278
+ if let viewManager = holder.definition.viewManager {
279
+ acc[holder.name] = [
280
+ "propsNames": viewManager.props.map { $0.name }
281
+ ]
282
+ }
283
+ }
284
+ }
285
+
137
286
  // MARK: - Runtime
138
287
 
139
- internal func installExpoModulesHostObject(_ interopBridge: SwiftInteropBridge) throws {
140
- guard let runtime = runtime else {
141
- throw UndefinedRuntimeException()
288
+ internal func installExpoModulesHostObject() throws {
289
+ guard runtime != nil else {
290
+ throw RuntimeLostException()
142
291
  }
143
- EXJavaScriptRuntimeManager.installExpoModules(to: runtime, withSwiftInterop: interopBridge)
292
+ EXJavaScriptRuntimeManager.installExpoModulesHostObject(self)
144
293
  }
294
+
145
295
  /**
146
296
  Unsets runtime objects that we hold for each module.
147
297
  */
@@ -158,20 +308,50 @@ public final class AppContext {
158
308
  */
159
309
  deinit {
160
310
  NotificationCenter.default.removeObserver(self)
161
- moduleRegistry.post(event: .appContextDestroys)
311
+
312
+ // Post an event to the registry only if it was already created.
313
+ // If we let it to lazy-load here, that would crash since the module registry
314
+ // has a weak reference to the app context which is being deallocated.
315
+ if isModuleRegistryInitialized {
316
+ moduleRegistry.post(event: .appContextDestroys)
317
+ }
162
318
  }
163
319
 
164
- // MARK: - Exceptions
320
+ // MARK: - Statics
321
+
322
+ /**
323
+ Returns an instance of the generated Expo modules provider.
324
+ The provider is usually generated in application's `ExpoModulesProviders` files group.
325
+ */
326
+ @objc
327
+ public static func modulesProvider(withName providerName: String = "ExpoModulesProvider") -> ModulesProvider {
328
+ // [0] When ExpoModulesCore is built as separated framework/module,
329
+ // we should explicitly load main bundle's `ExpoModulesProvider` class.
330
+ if let bundleName = Bundle.main.infoDictionary?["CFBundleName"],
331
+ let providerClass = NSClassFromString("\(bundleName).\(providerName)") as? ModulesProvider.Type {
332
+ return providerClass.init()
333
+ }
165
334
 
166
- class DeallocatedAppContextException: Exception {
167
- override var reason: String {
168
- "The AppContext has been deallocated"
335
+ // [1] Fallback to `ExpoModulesProvider` class from the current module.
336
+ if let providerClass = NSClassFromString(providerName) as? ModulesProvider.Type {
337
+ return providerClass.init()
169
338
  }
339
+
340
+ // [2] Fallback to an empty `ModulesProvider` if `ExpoModulesProvider` was not generated
341
+ return ModulesProvider()
342
+ }
343
+ }
344
+
345
+ // MARK: - Public exceptions
346
+
347
+ public final class AppContextLostException: Exception {
348
+ override public var reason: String {
349
+ "The app context has been lost"
170
350
  }
351
+ }
171
352
 
172
- class UndefinedRuntimeException: Exception {
173
- override var reason: String {
174
- "The AppContext has undefined runtime"
175
- }
353
+ public final class RuntimeLostException: Exception {
354
+ override public var reason: String {
355
+ "The JavaScript runtime has been lost"
176
356
  }
177
357
  }
@@ -5,10 +5,28 @@
5
5
  */
6
6
  public protocol AnyArgument {}
7
7
 
8
+ // Extend the optional type - this is required to support optional arguments.
9
+ extension Optional: AnyArgument where Wrapped: AnyArgument {}
10
+
8
11
  // Extend the primitive types — these may come from React Native bridge.
9
12
  extension Bool: AnyArgument {}
13
+
10
14
  extension Int: AnyArgument {}
15
+ extension Int8: AnyArgument {}
16
+ extension Int16: AnyArgument {}
17
+ extension Int32: AnyArgument {}
18
+ extension Int64: AnyArgument {}
19
+
20
+ extension UInt: AnyArgument {}
21
+ extension UInt8: AnyArgument {}
22
+ extension UInt16: AnyArgument {}
23
+ extension UInt32: AnyArgument {}
24
+ extension UInt64: AnyArgument {}
25
+
26
+ extension Float32: AnyArgument {}
11
27
  extension Double: AnyArgument {}
28
+
12
29
  extension String: AnyArgument {}
13
30
  extension Array: AnyArgument {}
14
31
  extension Dictionary: AnyArgument {}
32
+