browser-polyfill icon indicating copy to clipboard operation
browser-polyfill copied to clipboard

Firebase JS SDK breaks when importing @expo/browser-polyfill

Open idudinov opened this issue 4 years ago • 5 comments

This happened some time ago: https://github.com/expo/expo-three/issues/53 https://github.com/expo/browser-polyfill/issues/2

and comes back again! @EvanBacon

WTR

Basically, one need to:

  1. Import @expo/browser-polyfill somewhere deep in the jungle of packages
  2. sign in using Firebase JS SDK
  3. observe some Firestore entity using onSnapshot, then unsubscribe (gracefully)
  4. sign out

This way is a 100% repro, but I have seen other random cases when that happened during Firebase sign in session.

Libraries

"@expo/browser-polyfill": "^0.1.0",
"expo": "~37.0.3",
"firebase": "7.9.0",
Code snippet to reproduce

import '@expo/browser-polyfill';

import firebase from 'firebase/app';

import 'firebase/auth';
import 'firebase/firestore';

const validFirebaseConfig = {
/* ... */
};

export async function doFirebaseTest() {
    // Initialize Firebase App
    const instance = firebase.initializeApp(validFirebaseConfig);

    // Do a simple log in
    console.log('logging in...');
    if (!instance.auth().currentUser) {
        await instance.auth().signInWithEmailAndPassword('[email protected]', '123456');
    }

    const uid = instance.auth().currentUser?.uid;

    // IMPORTANT: Add onSnapshot observer on any Firestore entity
    // only with this it will break
    console.log('subscribing to the user in Firestore...');
    const unsub1 = instance.firestore().doc(`users/${uid}`).onSnapshot(snapshot => {
        console.log('Got the User!!!', snapshot.data());
    });

    // just wait a bit to avoid race conditions
    console.log('waiting for 2 seconds...');
    setTimeout(async () => {
        unsub1();

        console.log('logging out...');
        // HERE you will see crash on Android or error RedBox for iOS simulator / Android emulator
        await instance.auth().signOut(); 
    
        console.log('completed!!!');
    }, 2000)
}

Actual Result

iOS

Device – nothing bad happens that a user can see Simulator:

No suitable URL request handler found for (null)

-[ABI37_0_0RCTNetworking networkTaskWithRequest:completionBlock:]
    ABI37_0_0RCTNetworking.mm:654
-[ABI37_0_0RCTImageLoader _loadURLRequest:progressBlock:completionBlock:]
__127-[ABI37_0_0RCTImageLoader _loadImageOrDataWithURLRequest:size:scale:resizeMode:progressBlock:partialLoadBlock:completionBlock:]_block_invoke.183
_dispatch_call_block_and_release
_dispatch_client_callout
_dispatch_lane_serial_drain
_dispatch_lane_invoke
_dispatch_workloop_worker_thread
_pthread_wqthread
start_wqthread

Screenshot

image

Android feels much worse about it

Device – crash/reload Virtual Device – app hangs with the error:

abi37_0_0.com.facebook.react.bridge.ReadableNativeMap cannot be cast to java.lang.String
Screenshot

image

idudinov avatar Apr 24 '20 08:04 idudinov

More related issues: https://github.com/expo/browser-polyfill/issues/10 https://github.com/expo/expo/issues/7371

idudinov avatar Apr 24 '20 09:04 idudinov

ah, yeah.. probably this makes firebase think that it is running in a web environment... i don't know if there is much we can do about this, there might just be some fundamental incompatibility between these packages

brentvatne avatar Apr 24 '20 19:04 brentvatne

ok, any idea about possible workaround?

currently, I use @expo/browser-polyfill implicitly within expo-three package, so basically my Expo app uses Firebase JS SDK and some Three.js rendering. So it's a general case that should be covered I suppose.

I'd be happy to submit a PR if someone could point on a direction for that, and if there's only at least a small chance to get this working.

Looking forward!

idudinov avatar Apr 25 '20 19:04 idudinov

hey @brentvatne @EvanBacon ! I've just found out more info about the issue, and that looks like a RN bug 🕵️‍♂️ . Please consider this, and maybe find a minute to think about possible fix or workaround. Can't wait to share my optimism!

Very internally, Firebase tries to track something in the moment we' closing the connection. Also this happen periodically at some random point, in the way it often done in web browsers: they're creating an Image (HTMLImageElement) with src that looks like

https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel?database=...&VER=8&gsessionid=...&RID=...&TYPE=terminate&zx=...

Obviously they won't be able to do this if there's no Image/HTMLImageElement defined in global. @expo/browser-polyfill fairly tries to load at least a size of the requested image:

https://github.com/expo/browser-polyfill/blob/b17ebc6852c8ca5fda1c37be48f9ea901c32f142/src/DOM/HTMLImageElement.js#L105-L115

And finally, it looks like React-Native's Image.getSize can't handle this, it looks like that request returns or redirects to something null-ish, based on this evidence:

No suitable URL request handler found for (null)

-[ABI37_0_0RCTNetworking networkTaskWithRequest:completionBlock:]
    ABI37_0_0RCTNetworking.mm:654
-[ABI37_0_0RCTImageLoader _loadURLRequest:progressBlock:completionBlock:]
__127-[ABI37_0_0RCTImageLoader _loadImageOrDataWithURLRequest:size:scale:resizeMode:progressBlock:partialLoadBlock:completionBlock:]_block_invoke.183

Also what I found is that no issue occurs on Firebase side if I just comment this line:

https://github.com/expo/browser-polyfill/blob/b17ebc6852c8ca5fda1c37be48f9ea901c32f142/src/window.js#L13

but I'm totally unsure how this particular polyfill is used elsewhere in dependencies, especially in expo-three which I actively using by myself.

So I've tried a dumb thing somewhere in imports after importing expo-three:

(global as any).Image = undefined;

and it worked so far both for the test project and for my real project 🤞

🎉🎉🎉

of course I'll be observing that and get back if there're any pitfalls.

but anyway looking forward to any thoughts on this!

idudinov avatar Apr 27 '20 17:04 idudinov

Finding this Github issues thread saved me. Similar experience:

  • In a React Native project I'm using expo-three which has @expo/browser-polyfill as a dependency.
  • Using Firebase for authentication and database.
  • Logging in with Firebase crashes the app on Android.

I did what @idudinov recommended above: re-assigning global.Image after importing expo-three.

import { Renderer, THREE } from "expo-three";
global.Image = undefined;

No more crashes. 🎉

jayharr-is avatar Sep 19 '20 20:09 jayharr-is