1import { NativeModules } from 'react-native'; 2 3import { ProxyNativeModule } from './NativeModulesProxy.types'; 4 5const NativeProxy = NativeModules.NativeUnimoduleProxy; 6const modulesConstantsKey = 'modulesConstants'; 7const exportedMethodsKey = 'exportedMethods'; 8 9const NativeModulesProxy: { [moduleName: string]: ProxyNativeModule } = {}; 10 11if (NativeProxy) { 12 Object.keys(NativeProxy[exportedMethodsKey]).forEach((moduleName) => { 13 NativeModulesProxy[moduleName] = NativeProxy[modulesConstantsKey][moduleName] || {}; 14 NativeProxy[exportedMethodsKey][moduleName].forEach((methodInfo) => { 15 NativeModulesProxy[moduleName][methodInfo.name] = (...args: unknown[]): Promise<any> => { 16 const { key, argumentsCount } = methodInfo; 17 if (argumentsCount !== args.length) { 18 return Promise.reject( 19 new Error( 20 `Native method ${moduleName}.${methodInfo.name} expects ${argumentsCount} ${ 21 argumentsCount === 1 ? 'argument' : 'arguments' 22 } but received ${args.length}` 23 ) 24 ); 25 } 26 return NativeProxy.callMethod(moduleName, key, args); 27 }; 28 }); 29 30 // These are called by EventEmitter (which is a wrapper for NativeEventEmitter) 31 // only on iOS and they use iOS-specific native module, EXReactNativeEventEmitter. 32 // 33 // On Android only {start,stop}Observing are called on the native module 34 // and these should be exported as Expo methods. 35 // 36 // Before the RN 65, addListener/removeListeners weren't called on Android. However, it no longer stays true. 37 // See https://github.com/facebook/react-native/commit/f5502fbda9fe271ff6e1d0da773a3a8ee206a453. 38 // That's why, we check if the `EXReactNativeEventEmitter` exists and only if yes, we use it in the listener implementation. 39 // Otherwise, those methods are NOOP. 40 if (NativeModules.EXReactNativeEventEmitter) { 41 NativeModulesProxy[moduleName].addListener = (...args) => 42 NativeModules.EXReactNativeEventEmitter.addProxiedListener(moduleName, ...args); 43 NativeModulesProxy[moduleName].removeListeners = (...args) => 44 NativeModules.EXReactNativeEventEmitter.removeProxiedListeners(moduleName, ...args); 45 } else { 46 // Fixes on Android: 47 // WARN `new NativeEventEmitter()` was called with a non-null argument without the required `addListener` method. 48 // WARN `new NativeEventEmitter()` was called with a non-null argument without the required `removeListeners` method. 49 NativeModulesProxy[moduleName].addListener = () => {}; 50 NativeModulesProxy[moduleName].removeListeners = () => {}; 51 } 52 }); 53} else { 54 console.warn( 55 `The "EXNativeModulesProxy" native module is not exported through NativeModules; verify that expo-modules-core's native code is linked properly` 56 ); 57} 58 59export default NativeModulesProxy; 60