expo-modules-core 0.4.8 → 0.5.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 (129) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/android/build.gradle +30 -2
  3. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +27 -5
  4. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
  5. package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
  6. package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
  7. package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
  8. package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
  9. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.kt +18 -0
  10. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.kt +14 -0
  11. package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
  12. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
  13. package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
  14. package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
  15. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +23 -0
  16. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
  17. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
  18. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
  19. package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
  20. package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
  21. package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
  22. package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
  23. package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
  24. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
  25. package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
  26. package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
  27. package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
  28. package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
  29. package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
  30. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
  31. package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
  32. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
  33. package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
  34. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
  35. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
  36. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
  37. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
  38. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
  39. package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
  40. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
  41. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
  42. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
  43. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
  44. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
  45. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
  46. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
  47. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
  48. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
  49. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
  50. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
  51. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
  52. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
  53. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
  54. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
  55. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +36 -0
  56. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
  57. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
  58. package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
  59. package/build/NativeModulesProxy.native.d.ts +4 -0
  60. package/build/NativeModulesProxy.native.js +14 -1
  61. package/build/NativeModulesProxy.native.js.map +1 -1
  62. package/build/NativeModulesProxy.types.d.ts +3 -0
  63. package/build/NativeModulesProxy.types.js.map +1 -1
  64. package/ios/AppDelegates/EXAppDelegateWrapper.h +16 -0
  65. package/ios/AppDelegates/EXAppDelegateWrapper.m +42 -0
  66. package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
  67. package/ios/AppDelegates/EXAppDelegatesLoader.m +29 -0
  68. package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
  69. package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
  70. package/ios/AppDelegates/ExpoAppDelegate.swift +264 -0
  71. package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
  72. package/ios/ExpoModulesCore.podspec +7 -2
  73. package/ios/JSI/ExpoModulesProxySpec.h +24 -0
  74. package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
  75. package/ios/JSI/JSIConversions.h +42 -0
  76. package/ios/JSI/JSIConversions.mm +164 -0
  77. package/ios/JSI/JSIInstaller.h +19 -0
  78. package/ios/JSI/JSIInstaller.mm +22 -0
  79. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
  80. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
  81. package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +45 -12
  82. package/ios/Services/EXReactNativeEventEmitter.h +6 -0
  83. package/ios/Services/EXReactNativeEventEmitter.m +15 -0
  84. package/ios/Swift/AppContext.swift +14 -1
  85. package/ios/Swift/Arguments/AnyArgument.swift +14 -0
  86. package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
  87. package/ios/Swift/Arguments/ArgumentType.swift +24 -0
  88. package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
  89. package/ios/Swift/Arguments/Convertibles.swift +93 -0
  90. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
  91. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
  92. package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
  93. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
  94. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
  95. package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
  96. package/ios/Swift/Conversions.swift +199 -7
  97. package/ios/Swift/EventListener.swift +37 -5
  98. package/ios/Swift/Functions/AnyFunction.swift +42 -0
  99. package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
  100. package/ios/Swift/ModuleHolder.swift +75 -20
  101. package/ios/Swift/ModuleRegistry.swift +19 -8
  102. package/ios/Swift/Modules/AnyModule.swift +8 -8
  103. package/ios/Swift/Modules/Module.swift +7 -0
  104. package/ios/Swift/Modules/ModuleDefinition.swift +52 -8
  105. package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
  106. package/ios/Swift/Modules/ModuleDefinitionComponents.swift +140 -52
  107. package/ios/Swift/ModulesProvider.swift +9 -0
  108. package/ios/Swift/Promise.swift +1 -1
  109. package/ios/Swift/Records/Field.swift +1 -1
  110. package/ios/Swift/Records/Record.swift +8 -1
  111. package/ios/Swift/SwiftInteropBridge.swift +45 -16
  112. package/ios/Swift/Views/AnyViewProp.swift +2 -2
  113. package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
  114. package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
  115. package/ios/Swift.h +9 -0
  116. package/ios/Tests/ArgumentTypeSpec.swift +145 -0
  117. package/ios/Tests/ConvertiblesSpec.swift +231 -0
  118. package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
  119. package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
  120. package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
  121. package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
  122. package/ios/Tests/ModuleRegistrySpec.swift +4 -7
  123. package/package.json +3 -3
  124. package/src/NativeModulesProxy.native.ts +22 -2
  125. package/src/NativeModulesProxy.types.ts +8 -0
  126. package/ios/EXAppDelegateWrapper.h +0 -13
  127. package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
  128. package/ios/Swift/Methods/AnyMethod.swift +0 -31
  129. package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
@@ -0,0 +1,19 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #ifdef __cplusplus
4
+
5
+ #import <jsi/jsi.h>
6
+ #import <ReactCommon/RCTTurboModule.h>
7
+
8
+ #import <ExpoModulesCore/EXNativeModulesProxy.h>
9
+
10
+ using namespace facebook;
11
+ using namespace react;
12
+
13
+ namespace expo {
14
+
15
+ void installRuntimeObjects(jsi::Runtime &runtime, std::shared_ptr<CallInvoker> callInvoker, EXNativeModulesProxy *nativeModulesProxy);
16
+
17
+ } // namespace expo
18
+
19
+ #endif
@@ -0,0 +1,22 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoModulesCore/JSIInstaller.h>
4
+ #import <ExpoModulesCore/ExpoModulesProxySpec.h>
5
+
6
+ using namespace facebook;
7
+ using namespace react;
8
+
9
+ //using PromiseInvocationBlock = void (^)(RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper);
10
+
11
+ namespace expo {
12
+
13
+ void installRuntimeObjects(jsi::Runtime &runtime, std::shared_ptr<CallInvoker> callInvoker, EXNativeModulesProxy *nativeModulesProxy)
14
+ {
15
+ auto expoModulesProxyModule = std::make_shared<ExpoModulesProxySpec>(callInvoker, nativeModulesProxy);
16
+
17
+ runtime
18
+ .global()
19
+ .setProperty(runtime, "ExpoModulesProxy", jsi::Object::createFromHostObject(runtime, expoModulesProxyModule));
20
+ }
21
+
22
+ } // namespace expo
@@ -7,12 +7,7 @@
7
7
  #import <ExpoModulesCore/EXViewManagerAdapterClassesRegistry.h>
8
8
  #import <ExpoModulesCore/EXModuleRegistryHolderReactModule.h>
9
9
  #import <ExpoModulesCore/EXReactNativeEventEmitter.h>
10
- #if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
11
- // For cocoapods framework, the generated swift header will be inside ExpoModulesCore module
12
- #import <ExpoModulesCore/ExpoModulesCore-Swift.h>
13
- #else
14
- #import "ExpoModulesCore-Swift.h"
15
- #endif
10
+ #import <ExpoModulesCore/Swift.h>
16
11
 
17
12
  @interface EXModuleRegistryAdapter ()
18
13
 
@@ -12,6 +12,7 @@
12
12
  // Swift compatibility headers (e.g. `ExpoModulesCore-Swift.h`) are not available in headers,
13
13
  // so we use class forward declaration here. Swift header must be imported in the `.m` file.
14
14
  @class SwiftInteropBridge;
15
+ @protocol ModulesProviderObjCProtocol;
15
16
 
16
17
  NS_SWIFT_NAME(NativeModulesProxy)
17
18
  @interface EXNativeModulesProxy : NSObject <RCTBridgeModule>
@@ -21,4 +22,9 @@ NS_SWIFT_NAME(NativeModulesProxy)
21
22
  - (nonnull instancetype)init;
22
23
  - (nonnull instancetype)initWithModuleRegistry:(nullable EXModuleRegistry *)moduleRegistry;
23
24
 
25
+ - (void)callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNameOrKey arguments:(NSArray *)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
26
+ - (id)callMethodSync:(NSString *)moduleName methodName:(NSString *)methodName arguments:(NSArray *)arguments;
27
+
28
+ + (id<ModulesProviderObjCProtocol>)getExpoModulesProvider;
29
+
24
30
  @end
@@ -8,6 +8,8 @@
8
8
  #import <React/RCTModuleData.h>
9
9
  #import <React/RCTEventDispatcherProtocol.h>
10
10
 
11
+ #import <jsi/jsi.h>
12
+
11
13
  #import <ExpoModulesCore/EXNativeModulesProxy.h>
12
14
  #import <ExpoModulesCore/EXEventEmitter.h>
13
15
  #import <ExpoModulesCore/EXViewManager.h>
@@ -15,12 +17,8 @@
15
17
  #import <ExpoModulesCore/EXViewManagerAdapterClassesRegistry.h>
16
18
  #import <ExpoModulesCore/EXModuleRegistryProvider.h>
17
19
  #import <ExpoModulesCore/EXReactNativeEventEmitter.h>
18
- #if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
19
- // For cocoapods framework, the generated swift header will be inside ExpoModulesCore module
20
- #import <ExpoModulesCore/ExpoModulesCore-Swift.h>
21
- #else
22
- #import "ExpoModulesCore-Swift.h"
23
- #endif
20
+ #import <ExpoModulesCore/JSIInstaller.h>
21
+ #import <ExpoModulesCore/Swift.h>
24
22
 
25
23
  static const NSString *exportedMethodsNamesKeyPath = @"exportedMethods";
26
24
  static const NSString *viewManagersNamesKeyPath = @"viewManagersNames";
@@ -37,6 +35,12 @@ static const NSString *methodInfoArgumentsCountKey = @"argumentsCount";
37
35
 
38
36
  @end
39
37
 
38
+ @interface RCTBridge (JSIRuntime)
39
+
40
+ - (void *)runtime;
41
+
42
+ @end
43
+
40
44
  @interface RCTComponentData (EXNativeModulesProxy)
41
45
 
42
46
  - (instancetype)initWithManagerClass:(Class)managerClass bridge:(RCTBridge *)bridge eventDispatcher:(id<RCTEventDispatcherProtocol>) eventDispatcher; // available in RN 0.65+
@@ -68,7 +72,7 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
68
72
  {
69
73
  if (self = [super init]) {
70
74
  _exModuleRegistry = moduleRegistry != nil ? moduleRegistry : [[EXModuleRegistryProvider new] moduleRegistry];
71
- _swiftInteropBridge = [[SwiftInteropBridge alloc] initWithModulesProvider:[self getExpoModulesProvider] legacyModuleRegistry:_exModuleRegistry];
75
+ _swiftInteropBridge = [[SwiftInteropBridge alloc] initWithModulesProvider:[EXNativeModulesProxy getExpoModulesProvider] legacyModuleRegistry:_exModuleRegistry];
72
76
  _exportedMethodsKeys = [NSMutableDictionary dictionary];
73
77
  _exportedMethodsReverseKeys = [NSMutableDictionary dictionary];
74
78
  _ownsModuleRegistry = moduleRegistry == nil;
@@ -93,6 +97,9 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
93
97
 
94
98
  - (NSDictionary *)constantsToExport
95
99
  {
100
+ // Install the TurboModule implementation of the proxy.
101
+ [self installExpoTurboModules];
102
+
96
103
  NSMutableDictionary <NSString *, id> *exportedModulesConstants = [NSMutableDictionary dictionary];
97
104
  // Grab all the constants exported by modules
98
105
  for (EXExportedModule *exportedModule in [_exModuleRegistry getAllExportedModules]) {
@@ -121,7 +128,7 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
121
128
  }
122
129
 
123
130
  // Add entries from Swift modules
124
- [exportedMethodsNamesAccumulator addEntriesFromDictionary:[_swiftInteropBridge exportedMethodNames]];
131
+ [exportedMethodsNamesAccumulator addEntriesFromDictionary:[_swiftInteropBridge exportedFunctionNames]];
125
132
 
126
133
  // Also, add `viewManagersNames` for sanity check and testing purposes -- with names we know what managers to mock on UIManager
127
134
  NSArray<EXViewManager *> *viewManagers = [_exModuleRegistry getAllViewManagers];
@@ -151,7 +158,7 @@ RCT_EXPORT_MODULE(NativeUnimoduleProxy)
151
158
  RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNameOrKey arguments:(NSArray *)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
152
159
  {
153
160
  if ([_swiftInteropBridge hasModule:moduleName]) {
154
- [_swiftInteropBridge callMethod:methodNameOrKey onModule:moduleName withArgs:arguments resolve:resolve reject:reject];
161
+ [_swiftInteropBridge callFunction:methodNameOrKey onModule:moduleName withArgs:arguments resolve:resolve reject:reject];
155
162
  return;
156
163
  }
157
164
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
@@ -186,10 +193,20 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
186
193
  });
187
194
  }
188
195
 
189
- #pragma mark - Privates
196
+ - (id)callMethodSync:(NSString *)moduleName methodName:(NSString *)methodName arguments:(NSArray *)arguments
197
+ {
198
+ if ([_swiftInteropBridge hasModule:moduleName]) {
199
+ return [_swiftInteropBridge callFunctionSync:methodName onModule:moduleName withArgs:arguments];
200
+ }
201
+ return (id)kCFNull;
202
+ }
190
203
 
191
- - (id<ModulesProviderObjCProtocol>)getExpoModulesProvider
204
+ #pragma mark - Statics
205
+
206
+ + (id<ModulesProviderObjCProtocol>)getExpoModulesProvider
192
207
  {
208
+ // Dynamically gets the modules provider class.
209
+ // NOTE: This needs to be versioned in Expo Go.
193
210
  Class generatedExpoModulesProvider = NSClassFromString(@"ExpoModulesProvider");
194
211
  // Checks if `ExpoModulesProvider` was generated
195
212
  if (generatedExpoModulesProvider) {
@@ -199,6 +216,8 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
199
216
  }
200
217
  }
201
218
 
219
+ #pragma mark - Privates
220
+
202
221
  - (void)registerExpoModulesInBridge:(RCTBridge *)bridge
203
222
  {
204
223
  // Registering expo modules in bridge is needed only when the proxy module owns the registry
@@ -255,8 +274,10 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
255
274
  // components in UIManager — we need to register them on our own.
256
275
  [self registerComponentDataForModuleClasses:additionalModuleClasses inBridge:bridge];
257
276
 
258
- // Get the newly created instance of `EXReactEventEmitter` bridge module and register it in expo modules registry.
277
+ // Get the newly created instance of `EXReactEventEmitter` bridge module,
278
+ // pass event names supported by Swift modules and register it in legacy modules registry.
259
279
  EXReactNativeEventEmitter *eventEmitter = [bridge moduleForClass:[EXReactNativeEventEmitter class]];
280
+ [eventEmitter setSwiftInteropBridge:_swiftInteropBridge];
260
281
  [_exModuleRegistry registerInternalModule:eventEmitter];
261
282
 
262
283
  // Let the modules consume the registry :)
@@ -345,4 +366,16 @@ RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNa
345
366
  }
346
367
  }
347
368
 
369
+ /**
370
+ Installs expo modules in JSI runtime.
371
+ */
372
+ - (void)installExpoTurboModules
373
+ {
374
+ facebook::jsi::Runtime *runtime = [_bridge respondsToSelector:@selector(runtime)] ? reinterpret_cast<facebook::jsi::Runtime *>(_bridge.runtime) : NULL;
375
+
376
+ if (runtime) {
377
+ expo::installRuntimeObjects(*runtime, _bridge.jsCallInvoker, self);
378
+ }
379
+ }
380
+
348
381
  @end
@@ -7,6 +7,12 @@
7
7
  #import <ExpoModulesCore/EXModuleRegistryConsumer.h>
8
8
  #import <ExpoModulesCore/EXBridgeModule.h>
9
9
 
10
+ // Swift compatibility headers (e.g. `ExpoModulesCore-Swift.h`) are not available in headers,
11
+ // so we use class forward declaration here. Swift header must be imported in the `.m` file.
12
+ @class SwiftInteropBridge;
13
+
10
14
  @interface EXReactNativeEventEmitter : RCTEventEmitter <EXInternalModule, EXBridgeModule, EXModuleRegistryConsumer, EXEventEmitterService>
11
15
 
16
+ @property (nonatomic, strong) SwiftInteropBridge *swiftInteropBridge;
17
+
12
18
  @end
@@ -4,6 +4,7 @@
4
4
  #import <ExpoModulesCore/EXEventEmitter.h>
5
5
  #import <ExpoModulesCore/EXExportedModule.h>
6
6
  #import <ExpoModulesCore/EXModuleRegistry.h>
7
+ #import <ExpoModulesCore/Swift.h>
7
8
 
8
9
  @interface EXReactNativeEventEmitter ()
9
10
 
@@ -42,6 +43,10 @@
42
43
  - (NSArray<NSString *> *)supportedEvents
43
44
  {
44
45
  NSMutableSet<NSString *> *eventsAccumulator = [NSMutableSet set];
46
+
47
+ if (_swiftInteropBridge) {
48
+ [eventsAccumulator addObjectsFromArray:[_swiftInteropBridge getSupportedEvents]];
49
+ }
45
50
  for (EXExportedModule *exportedModule in [_exModuleRegistry getAllExportedModules]) {
46
51
  if ([exportedModule conformsToProtocol:@protocol(EXEventEmitter)]) {
47
52
  id<EXEventEmitter> eventEmitter = (id<EXEventEmitter>)exportedModule;
@@ -54,6 +59,11 @@
54
59
  RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString *)eventName)
55
60
  {
56
61
  [self addListener:eventName];
62
+
63
+ if ([_swiftInteropBridge hasModule:moduleName]) {
64
+ [_swiftInteropBridge modifyEventListenersCount:moduleName count:1];
65
+ return;
66
+ }
57
67
  // Validate module
58
68
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
59
69
 
@@ -90,6 +100,11 @@ RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString
90
100
  RCT_EXPORT_METHOD(removeProxiedListeners:(NSString *)moduleName count:(double)count)
91
101
  {
92
102
  [self removeListeners:count];
103
+
104
+ if ([_swiftInteropBridge hasModule:moduleName]) {
105
+ [_swiftInteropBridge modifyEventListenersCount:moduleName count:-count];
106
+ return;
107
+ }
93
108
  // Validate module
94
109
  EXExportedModule *module = [_exModuleRegistry getExportedModuleForName:moduleName];
95
110
 
@@ -2,7 +2,7 @@ import UIKit
2
2
  /**
3
3
  The app context is an interface to a single Expo app.
4
4
  */
5
- public class AppContext {
5
+ public final class AppContext {
6
6
  /**
7
7
  The module registry for the app context.
8
8
  */
@@ -71,6 +71,13 @@ public class AppContext {
71
71
  return legacyModule(implementing: EXUtilitiesInterface.self)
72
72
  }
73
73
 
74
+ /**
75
+ Provides access to the event emitter from legacy module registry.
76
+ */
77
+ public var eventEmitter: EXEventEmitterService? {
78
+ return legacyModule(implementing: EXEventEmitterService.self)
79
+ }
80
+
74
81
  /**
75
82
  Starts listening to `UIApplication` notifications.
76
83
  */
@@ -108,4 +115,10 @@ public class AppContext {
108
115
  NotificationCenter.default.removeObserver(self)
109
116
  moduleRegistry.post(event: .appContextDestroys)
110
117
  }
118
+
119
+ // MARK: Errors
120
+
121
+ struct DeallocatedAppContextError: CodedError {
122
+ var description: String = "The app context has been deallocated."
123
+ }
111
124
  }
@@ -0,0 +1,14 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A protocol for classes/structs accepted as an argument of functions.
5
+ */
6
+ public protocol AnyArgument {}
7
+
8
+ // Extend the primitive types — these may come from React Native bridge.
9
+ extension Bool: AnyArgument {}
10
+ extension Int: AnyArgument {}
11
+ extension Double: AnyArgument {}
12
+ extension String: AnyArgument {}
13
+ extension Array: AnyArgument {}
14
+ extension Dictionary: AnyArgument {}
@@ -0,0 +1,13 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A protocol whose intention is to wrap function's argument type
5
+ to keep its real signature and not type-erase it by the compiler.
6
+ */
7
+ internal protocol AnyArgumentType: CustomStringConvertible {
8
+ /**
9
+ Casts given any value to the wrapped type and returns as `Any`.
10
+ NOTE: It may not be just simple type-casting (e.g. when the wrapped type conforms to `ConvertibleArgument`).
11
+ */
12
+ func cast<ArgType>(_ value: ArgType) throws -> Any
13
+ }
@@ -0,0 +1,24 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Factory creating an instance of the argument type wrapper conforming to `AnyArgumentType`.
5
+ Depending on the given type, it may return one of `ArrayArgumentType`, `OptionalArgumentType`, `ConvertibleArgumentType`, etc.
6
+ */
7
+ internal func ArgumentType<T>(_ type: T.Type) -> AnyArgumentType {
8
+ if let ArrayType = T.self as? AnyArrayArgument.Type {
9
+ return ArrayArgumentType(elementType: ArrayType.getElementArgumentType())
10
+ }
11
+ if let OptionalType = T.self as? AnyOptionalArgument.Type {
12
+ return OptionalArgumentType(wrappedType: OptionalType.getWrappedArgumentType())
13
+ }
14
+ if let ConvertibleType = T.self as? ConvertibleArgument.Type {
15
+ return ConvertibleArgumentType(innerType: ConvertibleType)
16
+ }
17
+ if let EnumType = T.self as? EnumArgument.Type {
18
+ return EnumArgumentType(innerType: EnumType)
19
+ }
20
+ if T.self is Promise.Type {
21
+ return PromiseArgumentType()
22
+ }
23
+ return RawArgumentType(innerType: T.self)
24
+ }
@@ -0,0 +1,15 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A protocol that allows custom classes or structs to be used as function arguments.
5
+ It requires static `convert(from:)` function that knows how to convert incoming
6
+ value of `Any` type to the type implemented by this protocol. It should throw an error
7
+ when the value is not recognized, is invalid or doesn't meet type requirements.
8
+ */
9
+ public protocol ConvertibleArgument: AnyArgument {
10
+ /**
11
+ Converts any value to the instance of its class (or struct).
12
+ Throws an error when given value cannot be converted.
13
+ */
14
+ static func convert(from value: Any?) throws -> Self
15
+ }
@@ -0,0 +1,93 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ import UIKit
4
+ import CoreGraphics
5
+
6
+ /// Here we extend some common iOS types to implement `ConvertibleArgument` protocol and
7
+ /// describe how they can be converted from primitive types received from JavaScript runtime.
8
+ /// This allows these types to be used as argument types of functions callable from JavaScript.
9
+ /// As an example, when the `CGPoint` type is used as an argument type, its instance can be
10
+ /// created from an array of two doubles or an object with `x` and `y` fields.
11
+
12
+ // MARK: - UIKit
13
+
14
+ extension UIColor: ConvertibleArgument {
15
+ public static func convert(from value: Any?) throws -> Self {
16
+ if let value = value as? String {
17
+ return try Conversions.toColor(hexString: value) as! Self
18
+ }
19
+ if let components = value as? [Double] {
20
+ let alpha = components.count > 3 ? components[3] : 1.0
21
+ return Self.init(red: components[0], green: components[1], blue: components[2], alpha: alpha)
22
+ }
23
+ if let value = value as? Int {
24
+ return try Conversions.toColor(argb: UInt64(value)) as! Self
25
+ }
26
+ throw Conversions.ConvertingError<UIColor>(value: value)
27
+ }
28
+ }
29
+
30
+ // MARK: - CoreGraphics
31
+
32
+ extension CGPoint: ConvertibleArgument {
33
+ public static func convert(from value: Any?) throws -> CGPoint {
34
+ if let value = value as? [Double], value.count == 2 {
35
+ return CGPoint(x: value[0], y: value[1])
36
+ }
37
+ if let value = value as? [String: Any] {
38
+ let args = try Conversions.pickValues(from: value, byKeys: ["x", "y"], as: Double.self)
39
+ return CGPoint(x: args[0], y: args[1])
40
+ }
41
+ throw Conversions.ConvertingError<CGPoint>(value: value)
42
+ }
43
+ }
44
+
45
+ extension CGSize: ConvertibleArgument {
46
+ public static func convert(from value: Any?) throws -> CGSize {
47
+ if let value = value as? [Double], value.count == 2 {
48
+ return CGSize(width: value[0], height: value[1])
49
+ }
50
+ if let value = value as? [String: Any] {
51
+ let args = try Conversions.pickValues(from: value, byKeys: ["width", "height"], as: Double.self)
52
+ return CGSize(width: args[0], height: args[1])
53
+ }
54
+ throw Conversions.ConvertingError<CGSize>(value: value)
55
+ }
56
+ }
57
+
58
+ extension CGVector: ConvertibleArgument {
59
+ public static func convert(from value: Any?) throws -> CGVector {
60
+ if let value = value as? [Double], value.count == 2 {
61
+ return CGVector(dx: value[0], dy: value[1])
62
+ }
63
+ if let value = value as? [String: Any] {
64
+ let args = try Conversions.pickValues(from: value, byKeys: ["dx", "dy"], as: Double.self)
65
+ return CGVector(dx: args[0], dy: args[1])
66
+ }
67
+ throw Conversions.ConvertingError<CGVector>(value: value)
68
+ }
69
+ }
70
+
71
+ extension CGRect: ConvertibleArgument {
72
+ public static func convert(from value: Any?) throws -> CGRect {
73
+ if let value = value as? [Double], value.count == 4 {
74
+ return CGRect(x: value[0], y: value[1], width: value[2], height: value[3])
75
+ }
76
+ if let value = value as? [String: Any] {
77
+ let args = try Conversions.pickValues(from: value, byKeys: ["x", "y", "width", "height"], as: Double.self)
78
+ return CGRect(x: args[0], y: args[1], width: args[2], height: args[3])
79
+ }
80
+ throw Conversions.ConvertingError<CGRect>(value: value)
81
+ }
82
+ }
83
+
84
+ extension CGColor: ConvertibleArgument {
85
+ public static func convert(from value: Any?) throws -> Self {
86
+ do {
87
+ return try UIColor.convert(from: value).cgColor as! Self
88
+ } catch _ as Conversions.ConvertingError<UIColor> {
89
+ // Rethrow `ConvertingError` with proper type
90
+ throw Conversions.ConvertingError<CGColor>(value: value)
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,42 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ An argument type representing array types. Requires the argument type for
5
+ array's element type as it delegates casting to that type for each element in the array.
6
+ */
7
+ internal struct ArrayArgumentType: AnyArgumentType {
8
+ let elementType: AnyArgumentType
9
+
10
+ func cast<ArgType>(_ value: ArgType) throws -> Any {
11
+ if let value = value as? [Any] {
12
+ return try value.map { try elementType.cast($0) }
13
+ }
14
+ // We should probably throw an error if we get here. On the other side, the array type
15
+ // requirement can be more loosen so we can try to arrayize values that are not arrays.
16
+ return [try elementType.cast(value)]
17
+ }
18
+
19
+ var description: String {
20
+ "[\(elementType.description)]"
21
+ }
22
+ }
23
+
24
+ /**
25
+ A type-erased protocol used to recognize arrays with elements of argument-compatible type.
26
+ `Array` is a generic type, so it's impossible to check the inheritance directly.
27
+ */
28
+ internal protocol AnyArrayArgument: AnyArgument {
29
+ /**
30
+ Exposes the `Element` generic type wrapped by the argument type to preserve its metadata.
31
+ */
32
+ static func getElementArgumentType() -> AnyArgumentType
33
+ }
34
+
35
+ /**
36
+ Extends the `Array` type to expose its generic `Element` as an argument type.
37
+ */
38
+ extension Array: AnyArrayArgument where Element: AnyArgument {
39
+ static func getElementArgumentType() -> AnyArgumentType {
40
+ return ArgumentType(Element.self)
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ An argument type that wraps any type conforming to `ConvertibleArgument` protocol.
5
+ */
6
+ internal struct ConvertibleArgumentType: AnyArgumentType {
7
+ let innerType: ConvertibleArgument.Type
8
+
9
+ func cast<ArgType>(_ value: ArgType) throws -> Any {
10
+ return try innerType.convert(from: value)
11
+ }
12
+
13
+ var description: String {
14
+ String(describing: innerType.self)
15
+ }
16
+ }
@@ -0,0 +1,105 @@
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
+ }
17
+
18
+ /**
19
+ A protocol that allows converting raw values to enum cases.
20
+ */
21
+ public protocol EnumArgument: AnyArgument {
22
+ /**
23
+ Tries to create an enum case using given raw value.
24
+ May throw errors, e.g. when the raw value doesn't match any case.
25
+ */
26
+ static func create<ArgType>(fromRawValue rawValue: ArgType) throws -> Self
27
+
28
+ /**
29
+ Returns an array of all raw values available in the enum.
30
+ */
31
+ static var allRawValues: [Any] { get }
32
+
33
+ /**
34
+ Type-erased enum's raw value.
35
+ */
36
+ var anyRawValue: Any { get }
37
+ }
38
+
39
+ /**
40
+ Extension for `EnumArgument` that also conforms to `RawRepresentable`.
41
+ This constraint allows us to reference the associated `RawValue` type.
42
+ */
43
+ public extension EnumArgument where Self: RawRepresentable, Self: Hashable {
44
+ static func create<ArgType>(fromRawValue rawValue: ArgType) throws -> Self {
45
+ guard let rawValue = rawValue as? RawValue else {
46
+ throw EnumCastingError(type: RawValue.self, value: rawValue)
47
+ }
48
+ guard let enumCase = Self.init(rawValue: rawValue) else {
49
+ throw EnumNoSuchValueError(type: Self.self, value: rawValue)
50
+ }
51
+ return enumCase
52
+ }
53
+
54
+ var anyRawValue: Any {
55
+ rawValue
56
+ }
57
+
58
+ static var allRawValues: [Any] {
59
+ // Be careful — it operates on unsafe pointers!
60
+ let sequence = AnySequence { () -> AnyIterator<RawValue> in
61
+ var raw = 0
62
+ return AnyIterator {
63
+ let current: Self? = withUnsafePointer(to: &raw) { ptr in
64
+ ptr.withMemoryRebound(to: Self.self, capacity: 1) { $0.pointee }
65
+ }
66
+ guard let value = current?.rawValue else {
67
+ return nil
68
+ }
69
+ raw += 1
70
+ return value
71
+ }
72
+ }
73
+ return Array(sequence)
74
+ }
75
+ }
76
+
77
+ /**
78
+ An error that is thrown when the value cannot be casted to associated `RawValue`.
79
+ */
80
+ internal struct EnumCastingError: CodedError {
81
+ let type: Any.Type
82
+ let value: Any
83
+
84
+ var description: String {
85
+ "Cannot cast value `\(value)` to expected type `\(type)`"
86
+ }
87
+ }
88
+
89
+ /**
90
+ An error that is thrown when the value doesn't match any available case.
91
+ */
92
+ internal struct EnumNoSuchValueError: CodedError {
93
+ let type: EnumArgument.Type
94
+ let value: Any
95
+
96
+ var allRawValuesFormatted: String {
97
+ return type.allRawValues
98
+ .map { "`\($0)`" }
99
+ .joined(separator: ", ")
100
+ }
101
+
102
+ var description: String {
103
+ "Cannot create `\(type)` enum from value `\(value)`. It must be one of: \(allRawValuesFormatted)"
104
+ }
105
+ }
@@ -0,0 +1,49 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ An argument type that represents an optional type, which allows `nil` to be passed when casting.
5
+ Requires the argument type for optional's unwrapped type as it delegates casting to that type for non-nil values.
6
+ */
7
+ internal struct OptionalArgumentType: AnyArgumentType {
8
+ let wrappedType: AnyArgumentType
9
+
10
+ func cast<ArgType>(_ value: ArgType) throws -> Any {
11
+ if Optional.isNil(value) {
12
+ return Optional<Any>.none as Any
13
+ }
14
+ return try wrappedType.cast(value)
15
+ }
16
+
17
+ var description: String {
18
+ "\(wrappedType)?"
19
+ }
20
+ }
21
+
22
+ /**
23
+ A type-erased protocol used to recognize if the generic type is an optional type.
24
+ `Optional` is a generic enum, so it's impossible to check the inheritance directly.
25
+ */
26
+ internal protocol AnyOptionalArgument: AnyArgument {
27
+ /**
28
+ Exposes the `Wrapped` generic type wrapped by the argument type to preserve its metadata.`
29
+ */
30
+ static func getWrappedArgumentType() -> AnyArgumentType
31
+ }
32
+
33
+ /**
34
+ Make generic `Optional` implement non-generic `AnyOptional` and add handy check against type-erased `nil`.
35
+ */
36
+ extension Optional: AnyOptionalArgument {
37
+ static func getWrappedArgumentType() -> AnyArgumentType {
38
+ return ArgumentType(Wrapped.self)
39
+ }
40
+
41
+ static func isNil(_ object: Wrapped) -> Bool {
42
+ switch object as Any {
43
+ case Optional<Any>.none:
44
+ return true
45
+ default:
46
+ return false
47
+ }
48
+ }
49
+ }