@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
141 lines (123 loc) • 4.69 kB
text/typescript
import { ConfigPlugin, IOSConfig, WarningAggregator, withDangerousMod } from '@expo/config-plugins';
import { AppDelegateProjectFile } from '@expo/config-plugins/build/ios/Paths';
import { mergeContents } from '@expo/config-plugins/build/utils/generateCode';
import fs from 'fs';
export function modifyObjcAppDelegate(contents: string): string {
const methodInvocationBlock = `[FIRApp configure];`;
// https://regex101.com/r/mPgaq6/1
const methodInvocationLineMatcher =
/(?:self\.moduleName\s*=\s*@\"([^"]*)\";)|(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[(\[RCTBridge alloc\]|self\.reactDelegate))/g;
// https://regex101.com/r/nHrTa9/1/
// if the above regex fails, we can use this one as a fallback:
const fallbackInvocationLineMatcher =
/-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\s*\)\s*\w+\s+didFinishLaunchingWithOptions:/g;
// Add import
if (!contents.includes('#import <Firebase/Firebase.h>')) {
contents = contents.replace(
/#import "AppDelegate.h"/g,
`#import "AppDelegate.h"
#import <Firebase/Firebase.h>`,
);
}
// To avoid potential issues with existing changes from older plugin versions
if (contents.includes(methodInvocationBlock)) {
return contents;
}
if (
!methodInvocationLineMatcher.test(contents) &&
!fallbackInvocationLineMatcher.test(contents)
) {
WarningAggregator.addWarningIOS(
'@react-native-firebase/app',
'Unable to determine correct Firebase insertion point in AppDelegate.m. Skipping Firebase addition.',
);
return contents;
}
// Add invocation
try {
return mergeContents({
tag: '@react-native-firebase/app-didFinishLaunchingWithOptions',
src: contents,
newSrc: methodInvocationBlock,
anchor: methodInvocationLineMatcher,
offset: 0, // new line will be inserted right above matched anchor
comment: '//',
}).contents;
} catch (_: any) {
// tests if the opening `{` is in the new line
const multilineMatcher = new RegExp(fallbackInvocationLineMatcher.source + '.+\\n*{');
const isHeaderMultiline = multilineMatcher.test(contents);
// we fallback to another regex if the first one fails
return mergeContents({
tag: '@react-native-firebase/app-didFinishLaunchingWithOptions-fallback',
src: contents,
newSrc: methodInvocationBlock,
anchor: fallbackInvocationLineMatcher,
// new line will be inserted right below matched anchor
// or two lines, if the `{` is in the new line
offset: isHeaderMultiline ? 2 : 1,
comment: '//',
}).contents;
}
}
export function modifySwiftAppDelegate(contents: string): string {
const methodInvocationBlock = `FirebaseApp.configure()`;
const methodInvocationLineMatcher =
/(?:self\.moduleName\s*=\s*"([^"]*)")|(?:factory\.startReactNative\()/;
// Add import
if (!contents.includes('import FirebaseCore')) {
contents = contents.replace(
/import Expo/g,
`import Expo
import FirebaseCore`,
);
}
// To avoid potential issues with existing changes from older plugin versions
if (contents.includes(methodInvocationBlock)) {
return contents;
}
if (!methodInvocationLineMatcher.test(contents)) {
WarningAggregator.addWarningIOS(
'@react-native-firebase/app',
'Unable to determine correct Firebase insertion point in AppDelegate.swift. Skipping Firebase addition.',
);
return contents;
}
// Add invocation
return mergeContents({
tag: '@react-native-firebase/app-didFinishLaunchingWithOptions',
src: contents,
newSrc: methodInvocationBlock,
anchor: methodInvocationLineMatcher,
offset: 0, // new line will be inserted right above matched anchor
comment: '//',
}).contents;
}
export async function modifyAppDelegateAsync(appDelegateFileInfo: AppDelegateProjectFile) {
const { language, path, contents } = appDelegateFileInfo;
let newContents = contents;
switch (language) {
case 'objc':
case 'objcpp': {
newContents = modifyObjcAppDelegate(contents);
break;
}
case 'swift': {
newContents = modifySwiftAppDelegate(contents);
break;
}
default:
throw new Error(`Cannot add Firebase code to AppDelegate of language "${language}"`);
}
await fs.promises.writeFile(path, newContents);
}
export const withFirebaseAppDelegate: ConfigPlugin = config => {
return withDangerousMod(config, [
'ios',
async config => {
const fileInfo = IOSConfig.Paths.getAppDelegate(config.modRequest.projectRoot);
await modifyAppDelegateAsync(fileInfo);
return config;
},
]);
};