expo-modules-core 0.9.2 → 0.11.1

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 (193) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +3 -3
  3. package/android/CMakeLists.txt +163 -0
  4. package/android/build.gradle +343 -5
  5. package/android/src/main/cpp/CachedReferencesRegistry.cpp +65 -0
  6. package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
  7. package/android/src/main/cpp/Exceptions.cpp +22 -0
  8. package/android/src/main/cpp/Exceptions.h +38 -0
  9. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  10. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  11. package/android/src/main/cpp/JNIFunctionBody.cpp +44 -0
  12. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  13. package/android/src/main/cpp/JNIInjector.cpp +23 -0
  14. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  15. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  16. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  17. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  18. package/android/src/main/cpp/JavaScriptModuleObject.cpp +219 -0
  19. package/android/src/main/cpp/JavaScriptModuleObject.h +144 -0
  20. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  21. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  22. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  23. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  24. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  25. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  26. package/android/src/main/cpp/MethodMetadata.cpp +340 -0
  27. package/android/src/main/cpp/MethodMetadata.h +131 -0
  28. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  29. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  30. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  31. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +98 -3
  32. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  33. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  34. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +45 -3
  35. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
  36. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
  37. package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
  38. package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
  39. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
  40. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
  41. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
  42. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
  43. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
  44. package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
  45. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  46. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  47. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  48. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +53 -15
  49. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +35 -7
  50. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -121
  51. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +21 -0
  52. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +21 -0
  53. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +63 -0
  54. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +36 -0
  55. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +19 -0
  56. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  57. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  58. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +46 -0
  59. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  60. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  61. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +16 -6
  62. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -500
  63. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +30 -5
  64. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
  65. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
  66. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
  67. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
  68. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  69. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  70. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  71. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  72. package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
  73. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  74. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  75. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  76. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  77. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  78. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  79. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  80. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +12 -0
  81. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
  82. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
  83. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  84. package/build/NativeModulesProxy.native.js +9 -3
  85. package/build/NativeModulesProxy.native.js.map +1 -1
  86. package/build/PermissionsInterface.d.ts +29 -0
  87. package/build/PermissionsInterface.d.ts.map +1 -1
  88. package/build/PermissionsInterface.js +9 -0
  89. package/build/PermissionsInterface.js.map +1 -1
  90. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  91. package/ios/ExpoModulesCore.podspec +3 -2
  92. package/ios/JSI/EXJSIConversions.mm +6 -0
  93. package/ios/JSI/EXJSIInstaller.h +15 -21
  94. package/ios/JSI/EXJSIInstaller.mm +39 -3
  95. package/ios/JSI/EXJSIUtils.h +48 -3
  96. package/ios/JSI/EXJSIUtils.mm +88 -4
  97. package/ios/JSI/EXJavaScriptObject.h +11 -18
  98. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  99. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  100. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  101. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  102. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  103. package/ios/JSI/EXJavaScriptValue.h +3 -2
  104. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  105. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  106. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  107. package/ios/JSI/EXObjectDeallocator.h +27 -0
  108. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  109. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  110. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  111. package/ios/JSI/JavaScriptValue.swift +7 -0
  112. package/ios/JSI/TypedArray.cpp +67 -0
  113. package/ios/JSI/TypedArray.h +46 -0
  114. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  115. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  116. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +86 -77
  117. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  118. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  119. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  120. package/ios/Swift/AppContext.swift +206 -28
  121. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  122. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  123. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  124. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  125. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  126. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  127. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  128. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  129. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  130. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  131. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  132. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  133. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  134. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  135. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  136. package/ios/Swift/Exceptions/ChainableException.swift +3 -3
  137. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  138. package/ios/Swift/Exceptions/Exception.swift +8 -6
  139. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  140. package/ios/Swift/ExpoBridgeModule.m +5 -0
  141. package/ios/Swift/ExpoBridgeModule.swift +79 -0
  142. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  143. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  144. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  145. package/ios/Swift/JavaScriptUtils.swift +32 -57
  146. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  147. package/ios/Swift/Logging/LogType.swift +62 -0
  148. package/ios/Swift/Logging/Logger.swift +201 -0
  149. package/ios/Swift/ModuleHolder.swift +19 -54
  150. package/ios/Swift/ModuleRegistry.swift +7 -1
  151. package/ios/Swift/Modules/AnyModule.swift +3 -3
  152. package/ios/Swift/ModulesProvider.swift +2 -0
  153. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  154. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  155. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  156. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  157. package/ios/Swift/Promise.swift +17 -4
  158. package/ios/Swift/Records/Field.swift +2 -2
  159. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  160. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  161. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  162. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  163. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  164. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  165. package/ios/Swift/Utilities.swift +28 -0
  166. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  167. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  168. package/ios/Tests/ClassComponentSpec.swift +210 -0
  169. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  170. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  171. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  172. package/ios/Tests/FunctionSpec.swift +167 -118
  173. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  174. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  175. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  176. package/ios/Tests/TypedArraysSpec.swift +136 -0
  177. package/package.json +2 -2
  178. package/src/NativeModulesProxy.native.ts +13 -3
  179. package/src/PermissionsInterface.ts +29 -0
  180. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  181. package/tsconfig.json +1 -1
  182. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  183. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  184. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  185. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  186. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  187. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  188. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  189. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  190. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  191. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  192. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  193. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -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,46 @@ 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
+ internal weak var legacyModuleRegistry: EXModuleRegistry?
32
+
33
+ internal weak var legacyModulesProxy: LegacyNativeModulesProxy?
22
34
 
23
35
  /**
24
- React bridge of the context's app.
36
+ React bridge of the context's app. Can be `nil` when the bridge
37
+ hasn't been propagated to the bridge modules yet (see ``ExpoBridgeModule``),
38
+ or when the app context is "bridgeless" (for example in native unit tests).
25
39
  */
40
+ @objc
26
41
  public internal(set) weak var reactBridge: RCTBridge?
27
42
 
28
43
  /**
29
44
  JSI runtime of the running app.
30
45
  */
31
- public internal(set) var runtime: JavaScriptRuntime? {
46
+ @objc
47
+ public var runtime: JavaScriptRuntime? {
32
48
  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
49
  if runtime == nil {
50
+ // When the runtime is unpinned from the context (e.g. deallocated),
51
+ // we should make sure to release all JS objects from the memory.
52
+ // Otherwise the JSCRuntime asserts may fail on deallocation.
37
53
  releaseRuntimeObjects()
54
+ } else if runtime != oldValue {
55
+ // Try to install ExpoModules host object automatically when the runtime changes.
56
+ // TODO: Should we uninstall in the old runtime? (@tsapeta)
57
+ try? installExpoModulesHostObject()
38
58
  }
39
59
  }
40
60
  }
@@ -42,17 +62,20 @@ public final class AppContext {
42
62
  /**
43
63
  Designated initializer without modules provider.
44
64
  */
45
- public init() {
65
+ public override init() {
66
+ super.init()
46
67
  listenToClientAppNotifications()
47
68
  }
48
69
 
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
70
+ @discardableResult
71
+ public func useModulesProvider(_ providerName: String) -> Self {
72
+ return useModulesProvider(Self.modulesProvider(withName: providerName))
73
+ }
74
+
75
+ @discardableResult
76
+ public func useModulesProvider(_ provider: ModulesProvider) -> Self {
55
77
  moduleRegistry.register(fromProvider: provider)
78
+ return self
56
79
  }
57
80
 
58
81
  /**
@@ -134,14 +157,139 @@ public final class AppContext {
134
157
  }
135
158
  }
136
159
 
160
+ // MARK: - Interop with NativeModulesProxy
161
+
162
+ /**
163
+ Returns view modules wrapped by the base `ViewModuleWrapper` class.
164
+ */
165
+ @objc
166
+ public func getViewManagers() -> [ViewModuleWrapper] {
167
+ return moduleRegistry.compactMap { holder in
168
+ if holder.definition.viewManager != nil {
169
+ return ViewModuleWrapper(holder)
170
+ } else {
171
+ return nil
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ Returns a bool whether the module with given name is registered in this context.
178
+ */
179
+ @objc
180
+ public func hasModule(_ moduleName: String) -> Bool {
181
+ return moduleRegistry.has(moduleWithName: moduleName)
182
+ }
183
+
184
+ /**
185
+ Returns an array of names of the modules registered in the module registry.
186
+ */
187
+ @objc
188
+ public func getModuleNames() -> [String] {
189
+ return moduleRegistry.getModuleNames()
190
+ }
191
+
192
+ /**
193
+ Returns a JavaScript object that represents a module with given name.
194
+ When remote debugging is enabled, this will always return `nil`.
195
+ */
196
+ @objc
197
+ public func getNativeModuleObject(_ moduleName: String) -> JavaScriptObject? {
198
+ return moduleRegistry.get(moduleHolderForName: moduleName)?.javaScriptObject
199
+ }
200
+
201
+ /**
202
+ Returns an array of event names supported by all Swift modules.
203
+ */
204
+ @objc
205
+ public func getSupportedEvents() -> [String] {
206
+ return moduleRegistry.reduce(into: [String]()) { events, holder in
207
+ events.append(contentsOf: holder.definition.eventNames)
208
+ }
209
+ }
210
+
211
+ /**
212
+ Modifies listeners count for module with given name. Depending on the listeners count,
213
+ `onStartObserving` and `onStopObserving` are called.
214
+ */
215
+ @objc
216
+ public func modifyEventListenersCount(_ moduleName: String, count: Int) {
217
+ moduleRegistry
218
+ .get(moduleHolderForName: moduleName)?
219
+ .modifyListenersCount(count)
220
+ }
221
+
222
+ /**
223
+ Asynchronously calls module's function with given arguments.
224
+ */
225
+ @objc
226
+ public func callFunction(
227
+ _ functionName: String,
228
+ onModule moduleName: String,
229
+ withArgs args: [Any],
230
+ resolve: @escaping EXPromiseResolveBlock,
231
+ reject: @escaping EXPromiseRejectBlock
232
+ ) {
233
+ moduleRegistry
234
+ .get(moduleHolderForName: moduleName)?
235
+ .call(function: functionName, args: args) { result in
236
+ switch result {
237
+ case .failure(let error):
238
+ reject(error.code, error.description, error)
239
+ case .success(let value):
240
+ resolve(value)
241
+ }
242
+ }
243
+ }
244
+
245
+ @objc
246
+ public final lazy var expoModulesConfig = ModulesProxyConfig(constants: self.exportedModulesConstants(),
247
+ methodNames: self.exportedFunctionNames(),
248
+ viewManagers: self.viewManagersMetadata())
249
+
250
+ private func exportedFunctionNames() -> [String: [[String: Any]]] {
251
+ var constants = [String: [[String: Any]]]()
252
+
253
+ for holder in moduleRegistry {
254
+ constants[holder.name] = holder.definition.functions.map({ functionName, function in
255
+ return [
256
+ "name": functionName,
257
+ "argumentsCount": function.argumentsCount,
258
+ "key": functionName
259
+ ]
260
+ })
261
+ }
262
+ return constants
263
+ }
264
+
265
+ private func exportedModulesConstants() -> [String: Any] {
266
+ return moduleRegistry
267
+ // prevent infinite recursion - exclude NativeProxyModule constants
268
+ .filter { $0.name != NativeModulesProxyModule.moduleName }
269
+ .reduce(into: [String: Any]()) { acc, holder in
270
+ acc[holder.name] = holder.getConstants()
271
+ }
272
+ }
273
+
274
+ private func viewManagersMetadata() -> [String: Any] {
275
+ return moduleRegistry.reduce(into: [String: Any]()) { acc, holder in
276
+ if let viewManager = holder.definition.viewManager {
277
+ acc[holder.name] = [
278
+ "propsNames": viewManager.props.map { $0.name }
279
+ ]
280
+ }
281
+ }
282
+ }
283
+
137
284
  // MARK: - Runtime
138
285
 
139
- internal func installExpoModulesHostObject(_ interopBridge: SwiftInteropBridge) throws {
140
- guard let runtime = runtime else {
141
- throw UndefinedRuntimeException()
286
+ internal func installExpoModulesHostObject() throws {
287
+ guard runtime != nil else {
288
+ throw RuntimeLostException()
142
289
  }
143
- EXJavaScriptRuntimeManager.installExpoModules(to: runtime, withSwiftInterop: interopBridge)
290
+ EXJavaScriptRuntimeManager.installExpoModulesHostObject(self)
144
291
  }
292
+
145
293
  /**
146
294
  Unsets runtime objects that we hold for each module.
147
295
  */
@@ -158,20 +306,50 @@ public final class AppContext {
158
306
  */
159
307
  deinit {
160
308
  NotificationCenter.default.removeObserver(self)
161
- moduleRegistry.post(event: .appContextDestroys)
309
+
310
+ // Post an event to the registry only if it was already created.
311
+ // If we let it to lazy-load here, that would crash since the module registry
312
+ // has a weak reference to the app context which is being deallocated.
313
+ if isModuleRegistryInitialized {
314
+ moduleRegistry.post(event: .appContextDestroys)
315
+ }
162
316
  }
163
317
 
164
- // MARK: - Exceptions
318
+ // MARK: - Statics
165
319
 
166
- class DeallocatedAppContextException: Exception {
167
- override var reason: String {
168
- "The AppContext has been deallocated"
320
+ /**
321
+ Returns an instance of the generated Expo modules provider.
322
+ The provider is usually generated in application's `ExpoModulesProviders` files group.
323
+ */
324
+ @objc
325
+ public static func modulesProvider(withName providerName: String = "ExpoModulesProvider") -> ModulesProvider {
326
+ // [0] When ExpoModulesCore is built as separated framework/module,
327
+ // we should explicitly load main bundle's `ExpoModulesProvider` class.
328
+ if let bundleName = Bundle.main.infoDictionary?["CFBundleName"],
329
+ let providerClass = NSClassFromString("\(bundleName).\(providerName)") as? ModulesProvider.Type {
330
+ return providerClass.init()
169
331
  }
170
- }
171
332
 
172
- class UndefinedRuntimeException: Exception {
173
- override var reason: String {
174
- "The AppContext has undefined runtime"
333
+ // [1] Fallback to `ExpoModulesProvider` class from the current module.
334
+ if let providerClass = NSClassFromString(providerName) as? ModulesProvider.Type {
335
+ return providerClass.init()
175
336
  }
337
+
338
+ // [2] Fallback to an empty `ModulesProvider` if `ExpoModulesProvider` was not generated
339
+ return ModulesProvider()
340
+ }
341
+ }
342
+
343
+ // MARK: - Public exceptions
344
+
345
+ public final class AppContextLostException: Exception {
346
+ override public var reason: String {
347
+ "The app context has been lost"
348
+ }
349
+ }
350
+
351
+ public final class RuntimeLostException: Exception {
352
+ override public var reason: String {
353
+ "The JavaScript runtime has been lost"
176
354
  }
177
355
  }
@@ -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
+
@@ -1,19 +1,4 @@
1
- // Copyright 2021-present 650 Industries. All rights reserved.
2
-
3
- /**
4
- An argument type representing an enum that conforms to `EnumArgument`.
5
- */
6
- internal struct EnumArgumentType: AnyArgumentType {
7
- let innerType: EnumArgument.Type
8
-
9
- func cast<ArgType>(_ value: ArgType) throws -> Any {
10
- return try innerType.create(fromRawValue: value)
11
- }
12
-
13
- var description: String {
14
- "Enum<\(innerType)>"
15
- }
16
- }
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
17
2
 
18
3
  /**
19
4
  A protocol that allows converting raw values to enum cases.
@@ -23,7 +8,7 @@ public protocol EnumArgument: AnyArgument {
23
8
  Tries to create an enum case using given raw value.
24
9
  May throw errors, e.g. when the raw value doesn't match any case.
25
10
  */
26
- static func create<ArgType>(fromRawValue rawValue: ArgType) throws -> Self
11
+ static func create<RawValueType>(fromRawValue rawValue: RawValueType) throws -> Self
27
12
 
28
13
  /**
29
14
  Returns an array of all raw values available in the enum.
@@ -0,0 +1,95 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Represents a JavaScript class.
5
+ */
6
+ public final class ClassComponent: ObjectDefinition {
7
+ /**
8
+ Name of the class.
9
+ */
10
+ let name: String
11
+
12
+ /**
13
+ A synchronous function that gets called when the object of this class is initializing.
14
+ */
15
+ let constructor: AnySyncFunctionComponent?
16
+
17
+ /**
18
+ A dynamic type for the associated object class.
19
+ */
20
+ let associatedType: AnyDynamicType?
21
+
22
+ init<AssociatedObject: ClassAssociatedObject>(
23
+ name: String,
24
+ associatedType: AssociatedObject.Type,
25
+ elements: [AnyClassComponentElement] = []
26
+ ) {
27
+ self.name = name
28
+ self.constructor = elements.first(where: isConstructor) as? AnySyncFunctionComponent
29
+ self.associatedType = ~AssociatedObject.self
30
+
31
+ // Constructors can't be passed down to the object component
32
+ // as we shouldn't override the default `<Class>.prototype.constructor`.
33
+ let elementsWithoutConstructors = elements.filter({ !isConstructor($0) })
34
+
35
+ super.init(definitions: elementsWithoutConstructors)
36
+ }
37
+
38
+ // MARK: - JavaScriptObjectBuilder
39
+
40
+ public override func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
41
+ let klass = runtime.createClass(name) { [weak self, weak runtime] this, arguments in
42
+ guard let self = self, let runtime = runtime else {
43
+ // TODO: Throw an exception? (@tsapeta)
44
+ return
45
+ }
46
+ // The properties can't go into the prototype as they would be shared across all instances.
47
+ // Instead, we decorate the instance object on initialization.
48
+ self.decorateWithProperties(runtime: runtime, object: this)
49
+
50
+ // Call the native constructor when defined.
51
+ let result = try? self.constructor?.call(by: this, withArguments: arguments)
52
+
53
+ // Register the shared object if returned by the constructor.
54
+ if let result = result as? SharedObject {
55
+ SharedObjectRegistry.add(native: result, javaScript: this)
56
+ }
57
+ }
58
+ decorate(object: klass, inRuntime: runtime)
59
+ return klass
60
+ }
61
+
62
+ public override func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
63
+ // Here we actually don't decorate the input object (constructor) but its prototype.
64
+ // Properties are intentionally skipped here — they have to decorate an instance instead of the prototype.
65
+ let prototype = object.getProperty("prototype").getObject()
66
+ decorateWithConstants(runtime: runtime, object: prototype)
67
+ decorateWithFunctions(runtime: runtime, object: prototype)
68
+ decorateWithClasses(runtime: runtime, object: prototype)
69
+ }
70
+ }
71
+
72
+ // MARK: - ClassAssociatedObject
73
+
74
+ /**
75
+ A protocol for types that can be used an associated type of the `ClassComponent`.
76
+ */
77
+ internal protocol ClassAssociatedObject {}
78
+
79
+ // Basically we only need these two
80
+ extension JavaScriptObject: ClassAssociatedObject {}
81
+ extension SharedObject: ClassAssociatedObject {}
82
+
83
+ // MARK: - Privates
84
+
85
+ /**
86
+ Checks whether the definition item is a constructor — a synchronous function whose name is "constructor".
87
+
88
+ We do it that way for the following two reasons:
89
+ - It's easier to reuse existing `SyncFunctionComponent`.
90
+ - Redefining prototype's `constructor` is a bad idea so a function with this name
91
+ needs to be filtered out when decorating the prototype.
92
+ */
93
+ fileprivate func isConstructor(_ item: AnyDefinition) -> Bool {
94
+ return (item as? AnySyncFunctionComponent)?.name == "constructor"
95
+ }
@@ -0,0 +1,33 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A type-erased protocol that must be implemented by the components passed as ``ClassComponent`` elements.
5
+ */
6
+ public protocol AnyClassComponentElement: AnyDefinition {}
7
+
8
+ /**
9
+ Class component element with an associated owner type. The `OwnerType` should refer to
10
+ the type that the parent `Class` component is associated with (e.g. the shared object type).
11
+ */
12
+ public protocol ClassComponentElement: AnyClassComponentElement {
13
+ associatedtype OwnerType
14
+ }
15
+
16
+ // MARK: - Conformance
17
+ // Allow some other components to be used as the class component elements.
18
+
19
+ extension SyncFunctionComponent: ClassComponentElement {
20
+ public typealias OwnerType = FirstArgType
21
+ }
22
+
23
+ extension AsyncFunctionComponent: ClassComponentElement {
24
+ public typealias OwnerType = FirstArgType
25
+ }
26
+
27
+ extension PropertyComponent: ClassComponentElement {
28
+ public typealias OwnerType = Void
29
+ }
30
+
31
+ extension ConstantsDefinition: ClassComponentElement {
32
+ public typealias OwnerType = Void
33
+ }
@@ -0,0 +1,34 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A result builder that captures the ``ClassComponent`` elements such as functions, constants and properties.
5
+ */
6
+ @resultBuilder
7
+ public struct ClassComponentElementsBuilder<OwnerType> {
8
+ public static func buildBlock(_ elements: AnyClassComponentElement...) -> [AnyClassComponentElement] {
9
+ return elements
10
+ }
11
+
12
+ /**
13
+ Default implementation without any constraints that just returns type-erased element.
14
+ */
15
+ static func buildExpression<ElementType: ClassComponentElement>(
16
+ _ element: ElementType
17
+ ) -> AnyClassComponentElement {
18
+ return element
19
+ }
20
+
21
+ /**
22
+ In case the element's owner type matches builder's generic type,
23
+ we need to instruct the function to pass `this` to the closure
24
+ as the first argument and deduct it from `argumentsCount`.
25
+ */
26
+ static func buildExpression<ElementType: ClassComponentElement>(
27
+ _ element: ElementType
28
+ ) -> AnyClassComponentElement where ElementType.OwnerType == OwnerType {
29
+ if var function = element as? AnyFunction {
30
+ function.takesOwner = true
31
+ }
32
+ return element
33
+ }
34
+ }
@@ -0,0 +1,96 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Class constructor without arguments.
5
+ */
6
+ public func Constructor<R>(
7
+ _ body: @escaping () throws -> R
8
+ ) -> SyncFunctionComponent<(), Void, R> {
9
+ return Function("constructor", body)
10
+ }
11
+
12
+ /**
13
+ Class constructor with one argument.
14
+ */
15
+ public func Constructor<R, A0: AnyArgument>(
16
+ _ body: @escaping (A0) throws -> R
17
+ ) -> SyncFunctionComponent<(A0), A0, R> {
18
+ return Function("constructor", body)
19
+ }
20
+
21
+ /**
22
+ Class constructor with two arguments.
23
+ */
24
+ public func Constructor<R, A0: AnyArgument, A1: AnyArgument>(
25
+ _ body: @escaping (A0, A1) throws -> R
26
+ ) -> SyncFunctionComponent<(A0, A1), A0, R> {
27
+ return Function("constructor", body)
28
+ }
29
+
30
+ /**
31
+ Class constructor with three arguments.
32
+ */
33
+ public func Constructor<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>(
34
+ _ body: @escaping (A0, A1, A2) throws -> R
35
+ ) -> SyncFunctionComponent<(A0, A1, A2), A0, R> {
36
+ return Function("constructor", body)
37
+ }
38
+
39
+ /**
40
+ Class constructor with four arguments.
41
+ */
42
+ public func Constructor<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>(
43
+ _ body: @escaping (A0, A1, A2, A3) throws -> R
44
+ ) -> SyncFunctionComponent<(A0, A1, A2, A3), A0, R> {
45
+ return Function("constructor", body)
46
+ }
47
+
48
+ /**
49
+ Class constructor with five arguments.
50
+ */
51
+ public func Constructor<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>(
52
+ _ body: @escaping (A0, A1, A2, A3, A4) throws -> R
53
+ ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4), A0, R> {
54
+ return Function("constructor", body)
55
+ }
56
+
57
+ /**
58
+ Class constructor with six arguments.
59
+ */
60
+ public func Constructor<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>(
61
+ _ body: @escaping (A0, A1, A2, A3, A4, A5) throws -> R
62
+ ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5), A0, R> {
63
+ return Function("constructor", body)
64
+ }
65
+
66
+ /**
67
+ Creates the component describing a JavaScript class.
68
+ */
69
+ public func Class(
70
+ _ name: String,
71
+ @ClassComponentElementsBuilder<JavaScriptObject> _ elements: () -> [AnyClassComponentElement]
72
+ ) -> ClassComponent {
73
+ return ClassComponent(name: name, associatedType: JavaScriptObject.self, elements: elements())
74
+ }
75
+
76
+ /**
77
+ Creates the component describing a JavaScript class with an associated native shared object class.
78
+ */
79
+ public func Class<SharedObjectType: SharedObject>(
80
+ _ name: String = String(describing: SharedObjectType.self),
81
+ _ sharedObjectType: SharedObjectType.Type,
82
+ @ClassComponentElementsBuilder<SharedObjectType> _ elements: () -> [AnyClassComponentElement]
83
+ ) -> ClassComponent {
84
+ return ClassComponent(name: name, associatedType: SharedObjectType.self, elements: elements())
85
+ }
86
+
87
+ /**
88
+ Creates the component describing a JavaScript class with an associated native shared object class
89
+ and with the name that is inferred from the shared object type.
90
+ */
91
+ public func Class<SharedObjectType: SharedObject>(
92
+ _ sharedObjectType: SharedObjectType.Type,
93
+ @ClassComponentElementsBuilder<SharedObjectType> _ elements: () -> [AnyClassComponentElement]
94
+ ) -> ClassComponent {
95
+ return ClassComponent(name: String(describing: SharedObjectType.self), associatedType: SharedObjectType.self, elements: elements())
96
+ }
@@ -0,0 +1,44 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A protocol whose intention is to wrap any type
5
+ to keep its real signature and not type-erase it by the compiler.
6
+ */
7
+ public protocol AnyDynamicType: CustomStringConvertible {
8
+ /**
9
+ Checks whether the inner type is the same as the given type.
10
+ */
11
+ func wraps<InnerType>(_ type: InnerType.Type) -> Bool
12
+
13
+ /**
14
+ Checks whether the dynamic type is equal to another,
15
+ that is when the type of the dynamic types are equal and their inner types are equal.
16
+ */
17
+ func equals(_ type: AnyDynamicType) -> Bool
18
+
19
+ /**
20
+ Casts given any value to the wrapped type and returns as `Any`.
21
+ NOTE: It may not be just simple type-casting (e.g. when the wrapped type conforms to `ConvertibleArgument`).
22
+ */
23
+ func cast<ValueType>(_ value: ValueType) throws -> Any
24
+ }
25
+
26
+ // MARK: - Operators
27
+
28
+ infix operator ~>
29
+ public func ~> <T>(lhs: AnyDynamicType, rhs: T.Type) -> Bool {
30
+ return lhs.wraps(rhs)
31
+ }
32
+
33
+ infix operator !~>
34
+ public func !~> <T>(lhs: AnyDynamicType, rhs: T.Type) -> Bool {
35
+ return !lhs.wraps(rhs)
36
+ }
37
+
38
+ public func == (lhs: AnyDynamicType, rhs: AnyDynamicType) -> Bool {
39
+ return lhs.equals(rhs)
40
+ }
41
+
42
+ public func != (lhs: AnyDynamicType, rhs: AnyDynamicType) -> Bool {
43
+ return !lhs.equals(rhs)
44
+ }