Skip to content
This repository has been archived by the owner on Sep 7, 2022. It is now read-only.

[firebase_analytics] Adblocker crashes the whole app. #335

Open
ciriousjoker opened this issue Aug 27, 2020 · 4 comments
Open

[firebase_analytics] Adblocker crashes the whole app. #335

ciriousjoker opened this issue Aug 27, 2020 · 4 comments

Comments

@ciriousjoker
Copy link

Describe the bug
A Flutter Web app will crash if the user has an adblocker enabled and <script src="https://www.gstatic.com/firebasejs/.../firebase-analytics.js"></script> can't be loaded. No widget is drawn, it crashes during the registerPlugin phase.

To Reproduce
Steps to reproduce the behavior:

  1. Create default app and add firebase_analytics as a dependency
  2. Skip the part where you would normally set up the script tags
  3. flutter run web

Expected behavior
App should still at least launch (even though analytics won't work obviously).

Additional context
Temporary solution is to add this anywhere before loading main.js:

// Add stub so the app doesn't crash when an adblocker is active
if (typeof firebase.analytics !== "function") {
    firebase.analytics = () => console.log("Adblocker detected. Created fake analytics so the app doesn't crash.");
}

Flutter doctor
Run flutter doctor and paste the output below:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel unknown, 1.19.0-4.1.pre, on macOS 11.0 20A5343i, locale de-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[!] Xcode - develop for iOS and macOS (Xcode 11.6)
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To install:
        sudo gem install cocoapods
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.0)
[✓] VS Code (version 1.48.1)
[✓] Connected device (2 available)

! Doctor found issues in 1 category.

This issue was originally created here:
firebase/flutterfire#3346

@aaahrens
Copy link

aaahrens commented Sep 6, 2020

Thanks for the workaround, it was driving me nuts

@ochmist
Copy link

ochmist commented Sep 11, 2020

Thank you so much for this workaround.

@ciriousjoker
Copy link
Author

@aaahrens @ochmist
Here's an improved version that also removes all the error messages (adapt as needed):

return {
   setAnalyticsCollectionEnabled: () => null,
   setCurrentScreen: () => null,
   logEvent: () => null,
};

@creativecreatorormaybenot
Copy link

creativecreatorormaybenot commented Mar 11, 2021

The previously proposed fixes are okay, however, they simply make the app not crash. Firebase Analytics will still be broken.

Here is a script that you can insert in your <body> after loading at least firebase-app.js. This is useful if you adhere to GDPR for example and asynchronously load Firebase analytics (say after the user consented via Google Tag Manager). Anyway, here is a mock that completely fixes the problem and allows loading Firebase Analytics later:

<!--
  We need to create a mock for Firebase Analytics that ensures that it *does not matter **when***
  the JS library is loaded. This is because Google Tag Manager will load firebase-analytics.js
  and this 1. happens asynchronously and 2. only after the user consented.
  The firebase.dart Dart library will crash if the firebase.analytics object does not exist,
  which is why this is absolutely crucial for starting the app.
  https://github.com/FirebaseExtended/firebase-dart/issues/335#issuecomment-797003830
-->
<script>
  // This mock ensures that if the firebase_analytics Flutter plugin uses this mock as its
  // instance (which does not change over time), the plugin will *still* be able to reach out
  // to the actual firebase.analytics() instance because the object will be overridden once the
  // firebase-analytics.js library is loaded in.
  firebase.analytics = function () {
    return {
      mock: true,
      app: function () {
        var instance = firebase.analytics()
        // Prevent infinite recursion if the real instance has not yet been loaded.
        if (instance.mock === true) return
        return instance.app
      },
      logEvent: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.logEvent(...arguments)
      },
      setAnalyticsCollectionEnabled: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setAnalyticsCollectionEnabled(...arguments)
      },
      setCurrentScreen: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setCurrentScreen(...arguments)
      },
      setUserId: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setUserId(...arguments)
      },
      setUserProperties: function () {
        var instance = firebase.analytics()
        if (instance.mock === true) return
        return instance.setUserProperties(...arguments)
      },
    }
  }
</script>

See also: https://stackoverflow.com/a/66589887/6509751

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants