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

Update plugin to support multiple plugins in the same project #41

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 0 additions & 2 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ end

target 'Plugin' do
capacitor_pods

pod 'Firebase/Core'
pod 'Firebase/Analytics'
end

Expand Down
141 changes: 49 additions & 92 deletions src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export class FirebaseAnalyticsWeb extends WebPlugin
implements FirebaseAnalyticsPlugin {
private not_supported_mssg = "This method is not supported";
private options_missing_mssg = "Firebase options are missing";
private duplicate_app_mssg = "Firebase app already exists";
private analytics_missing_mssg =
"Firebase analytics is not initialized. Make sure initializeFirebase() is called once";

Expand All @@ -32,35 +31,9 @@ export class FirebaseAnalyticsWeb extends WebPlugin
name: "FirebaseAnalytics",
platforms: ["web"],
});

this.ready = new Promise((resolve) => (this.readyResolver = resolve));
this.configure();
}

/**
* Configure and Initialize FirebaseApp if not present
* @param options - web app's Firebase configuration
* @returns firebase analytics object reference
* Platform: Web
*/
initializeFirebase(options: FirebaseInitOptions): Promise<any> {
return new Promise(async (resolve, reject) => {
await this.ready;

if (this.hasFirebaseInitialized()) {
reject(this.duplicate_app_mssg);
return;
}

if (!options) {
reject(this.options_missing_mssg);
return;
}

const app = window.firebase.initializeApp(options);
this.analyticsRef = app.analytics();
resolve(this.analyticsRef);
});
this.loadScripts();
}

/**
Expand Down Expand Up @@ -248,57 +221,34 @@ export class FirebaseAnalyticsWeb extends WebPlugin
resolve();
});
}

//
// Note: The methods below are common to all Firebase capacitor plugins. Best to create `capacitor-community / firebase-common`,
// move the code there and add it as module to all FB plugins.
//

/**
* Ready resolver to check and load firebase analytics
* Configure and Initialize FirebaseApp if not present
* @param options - web app's Firebase configuration
* @returns firebase analytics object reference
* Platform: Web
*/
private async configure() {
try {
await this.loadScripts();

if (
window.firebase &&
window.firebase.analytics &&
this.hasFirebaseInitialized()
) {
this.analyticsRef = window.firebase.analytics();
}
} catch (error) {
throw error;
}

const interval = setInterval(() => {
if (!window.firebase) {
return;
}
clearInterval(interval);
this.readyResolver();
}, 50);
async initializeFirebase(options: FirebaseInitOptions): Promise<any> {
if (!options)
throw new Error(this.options_missing_mssg);

await this.firebaseObjectReadyPromise();
const app = this.isFirebaseInitialized() ? window.firebase : window.firebase.initializeApp(options);
this.analyticsRef = app.analytics();
this.readyResolver();
return this.analyticsRef;
}

/**
* Check for existing loaded script and load new scripts
*/
private loadScripts() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you split this out into a separate PR, can you add some details about what is going wrong and what you did to fix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add some details about what is going wrong and what you did to fix?

If I remember correctly, we did this in order to create a reusable code between firebase-analytics and firebase-remote-config for their common needs (loading the scripts, etc.).

In an ideal world someone would create a firebase-common project, move all the common code there and would use that project both firebase-analytics, firebase-remote-config and anywhere else firebase needs to load scripts (likely everywhere). Over time more common code would move there.

When you split this out into a separate PR,

I apologize for the single PR. We issue those fixes in the same PR as we needed to proceed with our work and the original project was discarded. Different PRs would have never been merged on time.

You are rightfully asking for splitting, but I'm afraid I wont have time for it anytime soon, as I'm already over loaded with work 😬. At this point I can suggest either to create a branch on the original project, merge the whole PR to it and test/refactor as much as needed, and only than merge to the master, or to cherry pick the commits from the PR. If it was me, I'd go for the first option, as the entire PR is already tested and used on our project.

I hope you find our code helpful.

const firebaseAppScript = this.scripts[0];
const firebaseAnalyticsScript = this.scripts[1];

return new Promise(async (resolve, _reject) => {
const scripts = this.scripts.map((script) => script.key);
if (
document.getElementById(scripts[0]) &&
document.getElementById(scripts[1])
) {
return resolve();
}

await this.loadScript(firebaseAppScript.key, firebaseAppScript.src);
await this.loadScript(
firebaseAnalyticsScript.key,
firebaseAnalyticsScript.src
);
resolve();
});
private loadScripts(): Promise<Array<any>> {
return Promise.all( this.scripts.map( s => this.loadScript(s.key, s.src) ) );
}

/**
Expand All @@ -308,30 +258,37 @@ export class FirebaseAnalyticsWeb extends WebPlugin
*/
private loadScript(id: string, src: string): Promise<any> {
return new Promise((resolve, reject) => {
const file = document.createElement("script");
file.type = "text/javascript";
file.src = src;
file.id = id;
file.onload = resolve;
file.onerror = reject;
document.querySelector("head").appendChild(file);
if (document.getElementById(id)){
resolve(null);
} else {
const file = document.createElement("script");
file.type = "text/javascript";
file.src = src;
file.id = id;
file.onload = resolve;
file.onerror = reject;
document.querySelector("head").appendChild(file);
}
});
}

/**
* Returns true/false if firebase object reference exists inside window
*/
private hasFirebaseInitialized() {
if (!window.firebase) {
return false;
}

const firebaseApps = window.firebase.apps;
if (firebaseApps && firebaseApps.length === 0) {
return false;
}
private firebaseObjectReadyPromise(): Promise<void> {
var tries = 100;
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (window.firebase?.analytics) {
clearInterval(interval);
resolve( null );
} else if (tries-- <= 0) {
reject("Firebase fails to load");
}
}, 50);
} );
}

return true;
private isFirebaseInitialized() {
const length = window.firebase?.apps?.length;
return length && length > 0;
}
}

Expand Down