UNPKG

@react-native-firebase/app

Version:

A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Functions, Messaging (FCM), Remote Config, Sto

217 lines (208 loc) 8.06 kB
"use strict"; /* * Copyright (c) 2016-present Invertase Limited & Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this library except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { APP_NATIVE_MODULE } from "../constants.js"; import NativeFirebaseError from "../NativeFirebaseError.js"; import RNFBNativeEventEmitter from "../RNFBNativeEventEmitter.js"; import SharedEventEmitter from "../SharedEventEmitter.js"; import { getReactNativeModule } from '../nativeModule'; import { isAndroid, isIOS } from "../../common/index.js"; import { encodeNullValues } from "../nullSerialization.js"; const NATIVE_MODULE_REGISTRY = {}; const NATIVE_MODULE_EVENT_SUBSCRIPTIONS = {}; function nativeModuleKey(module) { return `${module._customUrlOrRegion || ''}:${module.app.name}:${module._config.namespace}`; } /** * Wraps a native module method to provide * auto prepended args and custom Error classes. * * @param namespace * @param method * @param argToPrepend * @returns {Function} */ function nativeModuleMethodWrapped(namespace, method, argToPrepend, isTurboModule) { return (...args) => { // For iOS TurboModules, encode null values in arguments to work around // the limitation where null values in object properties get stripped during serialization // See: https://github.com/facebook/react-native/issues/52802 const processedArgs = isIOS && isTurboModule ? args.map(arg => encodeNullValues(arg)) : args; const allArgs = [...argToPrepend, ...processedArgs]; const possiblePromise = method(...allArgs); if (possiblePromise && typeof possiblePromise === 'object' && 'then' in possiblePromise) { const jsStack = new Error().stack; return possiblePromise.catch(nativeError => Promise.reject(new NativeFirebaseError(nativeError, jsStack, namespace))); } return possiblePromise; }; } /** * Prepends all arguments in prependArgs to all native method calls * * @param namespace * @param NativeModule - Raw native module from React Native * @param argToPrepend */ function nativeModuleWrapped(namespace, NativeModule, argToPrepend, isTurboModule) { const native = {}; if (!NativeModule) { return NativeModule; } const nativeModuleObj = NativeModule; let properties = Object.keys(Object.getPrototypeOf(nativeModuleObj)); if (!properties.length) properties = Object.keys(nativeModuleObj); for (let i = 0, len = properties.length; i < len; i++) { const property = properties[i]; if (!property) continue; if (typeof nativeModuleObj[property] === 'function') { native[property] = nativeModuleMethodWrapped(namespace, nativeModuleObj[property], argToPrepend, isTurboModule); } else { native[property] = nativeModuleObj[property]; } } return native; } /** * Initialises and wraps all the native module methods. * * @param module * @returns {*} */ function initialiseNativeModule(module) { const config = module._config; const key = nativeModuleKey(module); const { namespace, nativeEvents, nativeModuleName, hasMultiAppSupport, hasCustomUrlOrRegionSupport, disablePrependCustomUrlOrRegion, turboModule } = config; const multiModuleRoot = {}; const isTurboModule = !!turboModule; const multiModule = Array.isArray(nativeModuleName); const nativeModuleNames = multiModule ? nativeModuleName : [nativeModuleName]; for (let i = 0; i < nativeModuleNames.length; i++) { const moduleName = nativeModuleNames[i]; if (!moduleName) continue; const nativeModule = getReactNativeModule(moduleName); // only error if there's a single native module // as multi modules can mean some are optional if (!multiModule && !nativeModule) { throw new Error(getMissingModuleHelpText(namespace)); } if (multiModule) { multiModuleRoot[moduleName] = !!nativeModule; } const argToPrepend = []; if (hasMultiAppSupport) { argToPrepend.push(module.app.name); } if (hasCustomUrlOrRegionSupport && !disablePrependCustomUrlOrRegion) { argToPrepend.push(module._customUrlOrRegion); } Object.assign(multiModuleRoot, nativeModuleWrapped(namespace, nativeModule, argToPrepend, isTurboModule)); } if (nativeEvents && Array.isArray(nativeEvents) && nativeEvents.length) { for (let i = 0, len = nativeEvents.length; i < len; i++) { const eventName = nativeEvents[i]; if (eventName) { subscribeToNativeModuleEvent(eventName); } } } Object.freeze(multiModuleRoot); NATIVE_MODULE_REGISTRY[key] = multiModuleRoot; return NATIVE_MODULE_REGISTRY[key]; } /** * Subscribe to a native event for js side distribution by appName * React Native events are hard set at compile - cant do dynamic event names * so we use a single event send it to js and js then internally can prefix it * and distribute dynamically. * * @param eventName * @private */ function subscribeToNativeModuleEvent(eventName) { if (!NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName]) { RNFBNativeEventEmitter.addListener(eventName, (...args) => { const event = args[0]; if (event.appName && event.databaseId) { // Firestore requires both appName and databaseId to prefix SharedEventEmitter.emit(`${event.appName}-${event.databaseId}-${eventName}`, event); } else if (event.appName) { // native event has an appName property - auto prefix and internally emit SharedEventEmitter.emit(`${event.appName}-${eventName}`, event); } else { // standard event - no need to prefix SharedEventEmitter.emit(eventName, event); } }); NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName] = true; } } /** * Help text for integrating the native counter parts for each firebase module. * * @param namespace * @returns {string} */ function getMissingModuleHelpText(namespace) { const snippet = `firebase.${namespace}()`; if (isIOS || isAndroid) { return `You attempted to use a Firebase module that's not installed natively on your project by calling ${snippet}.` + `\r\n\r\nEnsure you have installed the npm package '@react-native-firebase/${namespace}',` + ' have imported it in your project, and have rebuilt your native application.'; } return `You attempted to use a Firebase module that's not installed on your project by calling ${snippet}.` + `\r\n\r\nEnsure you have installed the npm package '@react-native-firebase/${namespace}' and` + ' have imported it in your project.'; } /** * Gets a wrapped native module instance for the provided firebase module. * Will attempt to create a new instance if non previously created. * * @param module * @returns {*} */ export function getNativeModule(module) { const key = nativeModuleKey(module); if (NATIVE_MODULE_REGISTRY[key]) { return NATIVE_MODULE_REGISTRY[key]; } return initialiseNativeModule(module); } /** * Custom wrapped app module as it does not have it's own FirebaseModule based class. * * @returns {*} */ export function getAppModule() { if (NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE]) { return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE]; } const namespace = 'app'; const nativeModule = getReactNativeModule(APP_NATIVE_MODULE); if (!nativeModule) { throw new Error(getMissingModuleHelpText(namespace)); } NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE] = nativeModuleWrapped(namespace, nativeModule, [], // TODO: change to true when we use TurboModules for app package false); return NATIVE_MODULE_REGISTRY[APP_NATIVE_MODULE]; } //# sourceMappingURL=nativeModule.js.map