Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[πŸ›] Config Plugin breaks on Expo SDK 44 #5936

Closed
2 of 7 tasks
nandorojo opened this issue Dec 15, 2021 · 21 comments Β· Fixed by #5940
Closed
2 of 7 tasks

[πŸ›] Config Plugin breaks on Expo SDK 44 #5936

nandorojo opened this issue Dec 15, 2021 · 21 comments Β· Fixed by #5940
Labels
help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report

Comments

@nandorojo
Copy link
Contributor

nandorojo commented Dec 15, 2021

Issue

If you upgrade to Expo SDK 44, and run expo prebuild -p ios --clean, you get this error:

Click to view error
(Use `node --trace-deprecation ...` to show where the warning was created)
βœ– Config sync failed
[ios.dangerous]: withIosDangerousBaseMod: Failed to match "/(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g" in contents:
#import "AppDelegate.h"
@import Firebase;

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>

#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
  InitializeFlipper(application);
#endif
  
  RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
  rootView.backgroundColor = [UIColor whiteColor];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [self.reactDelegate createRootViewController];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];

  [super application:application didFinishLaunchingWithOptions:launchOptions];

  return YES;
 }

- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
  // If you'd like to export some custom RCTBridgeModules, add them here!
  return @[];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
 #ifdef DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
 #else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
 #endif
}

// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  return [RCTLinkingManager application:application openURL:url options:options];
}

// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
  return [RCTLinkingManager application:application
                   continueUserActivity:userActivity
                     restorationHandler:restorationHandler];
}

@end

Error: [ios.dangerous]: withIosDangerousBaseMod: Failed to match "/(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g" in contents:
#import "AppDelegate.h"
@import Firebase;

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>

#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
  InitializeFlipper(application);
#endif
  
  RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
  rootView.backgroundColor = [UIColor whiteColor];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [self.reactDelegate createRootViewController];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];

  [super application:application didFinishLaunchingWithOptions:launchOptions];

  return YES;
 }

- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
  // If you'd like to export some custom RCTBridgeModules, add them here!
  return @[];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
 #ifdef DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
 #else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
 #endif
}

// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  return [RCTLinkingManager application:application openURL:url options:options];
}

// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
  return [RCTLinkingManager application:application
                   continueUserActivity:userActivity
                     restorationHandler:restorationHandler];
}

@end

    at addLines (/Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/node_modules/@expo/config-plugins/src/utils/generateCode.ts:82:19)
    at mergeContents (/Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/node_modules/@expo/config-plugins/src/utils/generateCode.ts:55:17)
    at modifyObjcAppDelegate (/Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js:24:45)
    at /Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js:41:28
    at action (/Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:227:23)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@react-native-firebase/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/.nvm/versions/node/v16.13.0/lib/node_modules/expo-cli/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/Developer/madison-hacks/app/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/.nvm/versions/node/v16.13.0/lib/node_modules/expo-cli/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at interceptingMod (/Users/user/.nvm/versions/node/v16.13.0/lib/node_modules/expo-cli/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)
    at action (/Users/user/.nvm/versions/node/v16.13.0/lib/node_modules/expo-cli/node_modules/@expo/config-plugins/src/plugins/createBaseMod.ts:82:21)
    at interceptingMod (/Users/user/.nvm/versions/node/v16.13.0/lib/node_modules/expo-cli/node_modules/@expo/config-plugins/src/plugins/withMod.ts:108:21)

I'm omitting most other fields in this issue, since they don't apply to Expo projects. But let me know if you need any more info.

It was working before upgrading to SDK 44. It seems like the regex doesn't match anymore. I'm not sure, since I don't exactly know what to look for in native files.

Project Files

Javascript

Click To Expand

package.json:

    "@react-native-firebase/app": "^13.1.0",
    "@react-native-firebase/auth": "^13.1.0",

firebase.json for react-native-firebase v6:

# N/A

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
# N/A

AppDelegate.m:

// N/A


Environment

Click To Expand

react-native info output:

 OUTPUT GOES HERE
  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • react-native-firebase version you're using that has this issue:
    • 13.1.0
  • Firebase module(s) you're using that has the issue:
    • basic installation
  • Are you using TypeScript?
    • 4.4.4


@nandorojo nandorojo added help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report labels Dec 15, 2021
@nandorojo
Copy link
Contributor Author

@mikehardy
Copy link
Collaborator

Oh darn - this happened with Expo 34 as well. It appears there is no stable interface here yet?
#5788 and then PR #5796
Historically ("once") @barthap has lent a hand here but it would be fantastic if there was more than one motivated / expo-savvy contributor around here. I don't use Expo myself so I can't be counted on for it unfortunately but I am willing to collaborate and get anything merged. Do you think you could give a fix a try?

Additionally I'm not sure if Expo allows patch-package to do it's thing but if you do propose anything, I made a CI action that generates a patch-package set for the monorepo packages, which helps get you over the hurdle of quick feedback, although I suppose tweaking a regex in node_modules isn't that hard of a case

@nandorojo
Copy link
Contributor Author

A patch would be fine here for me. Unfortunately I have no idea where to start β€” I don’t know what an app delegate looks like or anything like that. I’m spoiled by expo and only know JS haha.

@barthap
Copy link
Contributor

barthap commented Dec 15, 2021

They hoped not to change AppDelegate template since SDK 43 and use the plain React Native template, but I found out they actually did it again πŸ™„ I'm sorry. Afaik it's related to some "app delegate extensions/wrappers / react delegates" (the name changed a few times), but I'm not into that deep into the topic yet. Here's some info I found

I'll open a PR later, but now a quick patch for @react-native-firebase/app:

diff --git a/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js b/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js
index be0f265..1cabc5b 100644
--- a/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js
+++ b/node_modules/@react-native-firebase/app/plugin/build/ios/appDelegate.js
@@ -9,7 +9,7 @@ const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
 const fs_1 = __importDefault(require("fs"));
 const methodInvocationBlock = `[FIRApp configure];`;
 // https://regex101.com/r/Imm3E8/1
-const methodInvocationLineMatcher = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
+const methodInvocationLineMatcher = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[(\[RCTBridge alloc\]|self\.reactDelegate))/g;
 function modifyObjcAppDelegate(contents) {
     // Add import
     if (!contents.includes('@import Firebase;')) {

I put a patch-package.zip for the diff above, but feel free to test patch generated from my fix PR: https://github.com/invertase/react-native-firebase/actions/runs/1582563137 - I'll have a feedback if the fix is working properly

The Regex101 proof: https://regex101.com/r/mPgaq6/1

@nandorojo
Copy link
Contributor Author

Cool thanks for the quick PR, I’ll try this!

@mikehardy
Copy link
Collaborator

I'm going to issue it as a release in just a moment, so it'll be out on npmjs.com - I have a different bugfix I just merged that I want to get out there

@nandorojo
Copy link
Contributor Author

Hey guys, following up here. I am having a slight issue with React Native Firebase with Expo SDK 44.

When I call auth().signInWithPhoneNumber(...), everything works perfectly.

However, when I build the app on Expo's hosted EAS service, calling this function results in the app crashing.

To summarize: if I do expo run:ios, which builds the app locally, then auth().signInWithPhoneNumber() works. On the other hand, if I build the app via eas build, then calling auth().signInWithPhoneNumber() causes the app to crash.

@barthap does anything come to mind for why this might be? Do you expect it to be on the Expo side, or perhaps on the plugin side? I tried running eas build --local, and indeed had the issue on my locally-built EAS binary.

Thank you so much!

@barthap
Copy link
Contributor

barthap commented Mar 29, 2022

I doubt that it's related to the config plugin in any way. It looks more like an EAS issue. Do you have any logs from that crash?

@mikehardy
Copy link
Collaborator

I was thinking the same thing. Crash === crash trace needed for diagnosis

@nandorojo
Copy link
Contributor Author

I doubt that it's related to the config plugin in any way. It looks more like an EAS issue. Do you have any logs from that crash?

Yeah my instinct says the same. Sorry for the silly question, but what's the easiest way to get shareable crash logs? Is it logcat? (I'm not used to native debugging; I usually use the Console app.)

Thank you both!

@mikehardy
Copy link
Collaborator

Plug device in to computer, Console.app should show the device and from there you will have the ability to see device logs I believe

Also of note but unrelated, react-native 0.68 is releasing shortly and the react-native template has moved to .mm (Objective-C++) so there will be ongoing change here. Looks like expo support will be a treadmill of a feature

@nandorojo
Copy link
Contributor Author

I found the cause of the crash, which led me to #2346:

Screen Shot 2022-03-30 at 10 40 10 AM

I think I incorrectly followed the Firebase captcha instructions. I'm not sure why this only crashed on the EAS build though. Captcha worked fine on my simulator from local builds.

The Firebase captcha instructions require editing fields inside of XCode, using a field from the GoogleService-Info.plist. I wonder if this should be done via config plugin for the auth package, perhaps under a captcha key?

image

Thank you so much for the lead on checking out the crash logs, and for responding quick in general. The moral support helps.

@barthap
Copy link
Contributor

barthap commented Mar 30, 2022

Related: #5787

@mikehardy
Copy link
Collaborator

@nandorojo if this (the captcha info.plist expo command to define the url scheme) could use a pointer in the docs a PR would be awesome πŸ™ πŸ˜„

@nandorojo
Copy link
Contributor Author

I am trying a solution via Expo config plugin. If it works, I think we should add it to the @react-native-firebase plugin.

My attempted solution

import { withInfoPlist } from '@expo/config-plugins'
import plist from 'plist'
import fs from 'fs'

const googleServicesPlist = fs.readFileSync(
  path.resolve(__dirname, googleServicesFile),
  'utf8'
)
const googleServicesJson = plist.parse(googleServicesPlist)
const { REVERSED_CLIENT_ID } = googleServicesJson

export default {
  plugins: [
    [
      withInfoPlist,
      (config) => {
        if (!config.modResults) {
          config.modResults = {}
        }

        if (!config.modResults.CFBundleURLTypes) {
          config.modResults.CFBundleURLTypes = []
        }

        const hasReverseClientId = config.modResults.CFBundleURLTypes.some(
          (urlType) => urlType.CFBundleURLSchemes.includes(REVERSED_CLIENT_ID)
        )
        if (!hasReverseClientId) {
          config.modResults.CFBundleURLTypes.push({
            CFBundleURLSchemes: [REVERSED_CLIENT_ID]
          })
        }

        return config
      }
    ]
  ]
}

If this works, we should add it to @react-native-firebase's config plugin. Here are some ideas for APIs:

Possible APIs

1. Add an option in the config plugin

export default {
  plugins: [['@react-native-firebase/app', { enableCaptcha: true }]]
}

2. Create a separate auth plugin

export default {
  plugins: [['@react-native-firebase/auth', { enableCaptcha: true }]]
}

In this case, do we even need them to pass enableCaptcha? Can this be done automatically?

3. Automatically include this behavior

We could also include this behavior by default in the @react-native-firebase/app plugin. Unless there is a reason not to add the URL Scheme, this would be the easiest for users. Is there a reason not to do this?

export default {
  plugins: ['@react-native-firebase/app'] // πŸͺ„ works like magic
}

@mikehardy
Copy link
Collaborator

A lot of people use app without auth, auth config / functionality should not be included in app

@barthap
Copy link
Contributor

barthap commented Mar 30, 2022

Nitpick regarding your plugin code - you might want to read the location of your Google services file from config.ios.googleServicesFile to make this solution more generic.

@nandorojo
Copy link
Contributor Author

Nitpick regarding your plugin code - you might want to read the location of your Google services file from config.ios.googleServicesFile to make this solution more generic.

Definitely – this is just in my own app's code to see if it works for now

@nandorojo
Copy link
Contributor Author

That fixed it! Should I open a PR for an auth plug-in?

@mikehardy
Copy link
Collaborator

Sure - I wish Expo support were frictionless (that is: required no maintenance at all or config plugins or anything) but until then, this can help people. Thanks!

@nandorojo
Copy link
Contributor Author

Yeah I hear you. I personally like that it lets me encapsulate all the native config steps using JS APIs. Opened a PR at #6167

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants