browser-polyfill
browser-polyfill copied to clipboard
Firebase JS SDK breaks when importing @expo/browser-polyfill
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:
- Import
@expo/browser-polyfill
somewhere deep in the jungle of packages - sign in using Firebase JS SDK
- observe some Firestore entity using
onSnapshot
, then unsubscribe (gracefully) - 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
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
More related issues: https://github.com/expo/browser-polyfill/issues/10 https://github.com/expo/expo/issues/7371
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
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!
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!
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. 🎉