diff --git a/apps/demo/src/app.ts b/apps/demo/src/app.ts index 4394a423..e1b06fa1 100644 --- a/apps/demo/src/app.ts +++ b/apps/demo/src/app.ts @@ -1,6 +1,5 @@ import { Application, Utils } from '@nativescript/core'; import { firebase } from '@nativescript/firebase-core'; -import '@nativescript/firebase-admob'; import '@nativescript/firebase-analytics'; import '@nativescript/firebase-auth'; import '@nativescript/firebase-crashlytics'; diff --git a/apps/demo/src/plugin-demos/firebase-admob.ts b/apps/demo/src/plugin-demos/firebase-admob.ts index def21568..2eaaeec7 100644 --- a/apps/demo/src/plugin-demos/firebase-admob.ts +++ b/apps/demo/src/plugin-demos/firebase-admob.ts @@ -1,9 +1,6 @@ import { Observable, EventData, Page, View, Label } from '@nativescript/core'; import { DemoSharedFirebaseAdmob } from '@demo/shared'; -import { firebase } from '@nativescript/firebase-core'; -import '@nativescript/firebase-admob'; -import { AdEventType, InterstitialAd, RewardedInterstitialAd, RewardedAd, BannerAd, BannerAdSize, Admob, AdsConsent, NativeAd, NativeAdLoader, NativeAdView } from '@nativescript/firebase-admob'; -import { AdChoicesPlacement, NativeAdEventType } from '@nativescript/firebase-admob'; +import { AdChoicesPlacement, NativeAdEventType, AdEventType, InterstitialAd, RewardedInterstitialAd, RewardedAd, BannerAd, BannerAdSize, Admob, AdsConsent, NativeAd, NativeAdLoader, NativeAdView } from '@nativescript/firebase-admob'; export function navigatingTo(args: EventData) { const page = args.object; @@ -33,10 +30,7 @@ export class DemoModel extends DemoSharedFirebaseAdmob { } else { testDevices.push('EMULATOR'); } - const admob = firebase().admob(); - admob.setRequestConfiguration({ - testDevices, - }); + Admob.getInstance().requestConfiguration = { testDevices }; } nativeAdLayoutChanged(event) { diff --git a/packages/firebase-admob/README.md b/packages/firebase-admob/README.md index 7eb35d59..cb21a4d7 100644 --- a/packages/firebase-admob/README.md +++ b/packages/firebase-admob/README.md @@ -506,12 +506,11 @@ Use the argument `tagForChildDirectedTreatment: undefined` or do not set this ta The following example indicates that you want your content treated as child-directed for purposes of COPPA: ```ts -import { firebase } from '@nativescript/firebase-core' -import '@nativescript/firebase-admob' +import { Admob, RequestConfiguration } from '@nativescript/firebase-admob'; const requestConfiguration: RequestConfiguration = { tagForChildDirectedTreatment: true } -firebase().admob().setRequestConfiguration(requestConfiguration) +Admob.getInstance().requestConfiguration = requestConfiguration; ``` ### Users under the age of consent @@ -527,12 +526,11 @@ Use the argument `tagForUnderAgeOfConsent: false` to indicates that you don't wa Use the argument `tagForUnderAgeOfConsent: undefined` or do not set this tag to indicate that you have not specified whether the ad request should receive treatment for users in the European Economic Area (EEA) under the age of consent. The following example indicates that you want TFUA included in your ad request: ```ts -import { firebase } from '@nativescript/firebase-core' -import '@nativescript/firebase-admob' +import { Admob, RequestConfiguration } from '@nativescript/firebase-admob'; const requestConfiguration: RequestConfiguration = { tagForUnderAgeOfConsent: true } -firebase().admob().setRequestConfiguration(requestConfiguration) +Admob.getInstance().requestConfiguration = requestConfiguration; ``` The tags to enable the Child-directed setting and `tagForUnderAgeOfConsent` should not both simultaneously be set to true. If they are, the child-directed setting takes precedence. @@ -551,12 +549,11 @@ AdMob ads returned for these requests have a content rating at or below that lev The following code configures a `RequestConfiguration` object to specify that ad content returned should correspond to a digital content label designation no higher than G: ```ts -import { firebase } from '@nativescript/firebase-core' -import { MaxAdContentRating } from '@nativescript/firebase-admob' +import { Admob, MaxAdContentRating, RequestConfiguration } from '@nativescript/firebase-admob'; const requestConfiguration: RequestConfiguration = { maxAdContentRating: MaxAdContentRating.G } -firebase().admob().setRequestConfiguration(requestConfiguration) +Admob.getInstance().requestConfiguration = requestConfiguration; ``` ## License diff --git a/packages/firebase-admob/index.android.ts b/packages/firebase-admob/index.android.ts index 6b464528..d903067d 100644 --- a/packages/firebase-admob/index.android.ts +++ b/packages/firebase-admob/index.android.ts @@ -1,6 +1,5 @@ import { Application, Utils } from '@nativescript/core'; import lazy from '@nativescript/core/utils/lazy'; -import { firebase, FirebaseApp, FirebaseError } from '@nativescript/firebase-core'; import { IAdmob, AdEventListener, RequestConfiguration, AdShowOptions, IInterstitialAd, RequestOptions, IRewardedAd, IRewardedInterstitialAd, IRewardedItem, ServerSideVerificationOptions, AdapterStatus } from '.'; import { AdEventType, BannerAdBase, RewardedAdEventType, MaxAdContentRating, unitIdProperty, BannerAdSizeBase, sizeProperty } from './common'; @@ -9,11 +8,30 @@ export { MaxAdContentRating, AdEventType }; export * from './adsconsent'; export * from './nativead'; +export class AdmobError extends Error { + #native: java.lang.Exception; + static fromNative(native: java.lang.Exception, message?: string) { + const error = new AdmobError(message || native?.getMessage?.()); + error.#native = native; + return error; + } + + get native() { + return this.#native; + } + + intoNative() { + if (!this.#native) { + return new java.lang.Exception(this.message); + } + return this.#native; + } +} + let defaultAdmob: Admob; -const fb = firebase(); -if (!fb.admob) { - Object.defineProperty(fb, 'admob', { +if (!global.__admob) { + Object.defineProperty(global, '__admob', { value: () => { if (!defaultAdmob) { defaultAdmob = new Admob(); @@ -52,7 +70,7 @@ class AdListener extends com.google.android.gms.ads.AdListener { this._owner?.get?.().notify({ eventName: BannerAd.onAdFailedToLoadEvent, object: this._owner?.get?.(), - error: FirebaseError.fromNative(error), + error: AdmobError.fromNative(error as any), }); } @@ -179,7 +197,7 @@ export class InterstitialAd implements IInterstitialAd { owner?._setLoaded(false); break; case AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT: - owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, AdmobError.fromNative(dataOrError), owner); break; case AdEventType.IMPRESSION: owner?._onAdEvent(AdEventType.IMPRESSION, null, owner); @@ -188,7 +206,7 @@ export class InterstitialAd implements IInterstitialAd { owner?._onAdEvent(AdEventType.OPENED, null, owner); break; case AdEventType.FAILED_TO_LOAD_EVENT: - owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(dataOrError), owner); break; } }, @@ -270,7 +288,7 @@ export class RewardedInterstitialAd implements IRewardedInterstitialAd { owner?._setLoaded(false); break; case AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT: - owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, AdmobError.fromNative(dataOrError), owner); break; case AdEventType.IMPRESSION: owner?._onAdEvent(AdEventType.IMPRESSION, null, owner); @@ -279,7 +297,7 @@ export class RewardedInterstitialAd implements IRewardedInterstitialAd { owner?._onAdEvent(AdEventType.OPENED, null, owner); break; case AdEventType.FAILED_TO_LOAD_EVENT: - owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(dataOrError), owner); break; } }, @@ -386,7 +404,7 @@ export class RewardedAd implements IRewardedAd { owner?._setLoaded(false); break; case AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT: - owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, AdmobError.fromNative(dataOrError), owner); break; case AdEventType.IMPRESSION: owner?._onAdEvent(AdEventType.IMPRESSION, null, owner); @@ -395,7 +413,7 @@ export class RewardedAd implements IRewardedAd { owner?._onAdEvent(AdEventType.OPENED, null, owner); break; case AdEventType.FAILED_TO_LOAD_EVENT: - owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(dataOrError), owner); + owner?._onAdEvent(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(dataOrError), owner); break; } }, @@ -627,8 +645,6 @@ export class BannerAd extends BannerAdBase { } export class Admob implements IAdmob { - #app: FirebaseApp; - constructor() { if (defaultAdmob) { return defaultAdmob; @@ -653,6 +669,10 @@ export class Admob implements IAdmob { }); } + static getInstance(): Admob { + return new Admob(); + } + set requestConfiguration(requestConfiguration: RequestConfiguration) { try { const parsedConfiguration: any = { ...requestConfiguration }; @@ -736,11 +756,7 @@ export class Admob implements IAdmob { return this.requestConfiguration; } - get app(): FirebaseApp { - if (!this.#app) { - // @ts-ignore - this.#app = FirebaseApp.fromNative(com.google.firebase.FirebaseApp.getInstance()); - } - return this.#app; + get app() { + return global?.__defaultFirebaseApp; } } diff --git a/packages/firebase-admob/index.d.ts b/packages/firebase-admob/index.d.ts index 881e99b8..5d350a0e 100644 --- a/packages/firebase-admob/index.d.ts +++ b/packages/firebase-admob/index.d.ts @@ -1,6 +1,5 @@ import { Application } from '@nativescript/core'; -import { FirebaseApp } from '@nativescript/firebase-core'; import { IAdmob, IInterstitialAd, IRewardedAd, BannerAdBase, IRewardedInterstitialAd, RewardedAdEventType, MaxAdContentRating, ServerSideVerificationOptions } from './common'; export { MaxAdContentRating, RewardedAdEventType }; @@ -214,10 +213,12 @@ export declare class BannerAdSize extends BannerAdSizeBase { } export declare class Admob implements IAdmob { - readonly app: FirebaseApp; + readonly app: any; static init(): Promise<{ [key: string]: AdapterStatus }>; + static getInstance(): Admob; + requestConfiguration: RequestConfiguration; /** @@ -231,10 +232,6 @@ export declare class Admob implements IAdmob { getRequestConfiguration(requestConfiguration: RequestConfiguration); } -declare module '@nativescript/firebase-core' { - export interface Firebase extends FirebaseAdmob {} -} - export enum AdapterStatusState { NOT_READY, READY, @@ -245,7 +242,3 @@ export interface AdapterStatus { latency: number; initializationState: AdapterStatusState; } - -export interface FirebaseAdmob { - static admob(): Admob; -} diff --git a/packages/firebase-admob/index.ios.ts b/packages/firebase-admob/index.ios.ts index 503b226d..01c34e7f 100644 --- a/packages/firebase-admob/index.ios.ts +++ b/packages/firebase-admob/index.ios.ts @@ -1,5 +1,4 @@ import { Utils } from '@nativescript/core'; -import { firebase, FirebaseApp, FirebaseError } from '@nativescript/firebase-core'; import { IAdmob, AdEventListener, RequestConfiguration, AdShowOptions, IInterstitialAd, RequestOptions, IRewardedAd, ServerSideVerificationOptions, IRewardedInterstitialAd, IRewardedItem, AdapterStatus } from '.'; import { MaxAdContentRating, AdEventType, BannerAdBase, RewardedAdEventType, unitIdProperty, BannerAdSizeBase, sizeProperty } from './common'; import { topViewController, toSerializeRequestOptions } from './utils'; @@ -9,10 +8,38 @@ export { MaxAdContentRating, AdEventType }; export * from './adsconsent'; export * from './nativead'; +export class AdmobError extends Error { + #native: NSError; + static fromNative(native: NSError, message?: string) { + const error = new AdmobError(message || native?.localizedDescription); + error.#native = native; + return error; + } + + get native() { + return this.#native; + } + + intoNative() { + if (!this.#native) { + const exception = NSException.exceptionWithNameReasonUserInfo(NSGenericException, this.message, null); + const info = {}; + info['ExceptionName'] = exception.name; + info['ExceptionReason'] = exception.reason; + info['ExceptionCallStackReturnAddresses'] = exception.callStackReturnAddresses; + info['ExceptionCallStackSymbols'] = exception.callStackSymbols; + info['ExceptionUserInfo'] = exception.userInfo; + const error = NSError.alloc().initWithDomainCodeUserInfo('NativeScript', 1000, info as any); + return error; + } + return this.#native; + } +} + let defaultAdmob: Admob; -const fb = firebase(); -if (!fb.admob) { - Object.defineProperty(fb, 'admob', { + +if (!global.__admob) { + Object.defineProperty(global, '__admob', { value: () => { if (!defaultAdmob) { defaultAdmob = new Admob(); @@ -102,7 +129,7 @@ export class InterstitialAd implements IInterstitialAd { const request = toSerializeRequestOptions(this.#requestOptions); GADInterstitialAd.loadWithAdUnitIDRequestCompletionHandler(this.#adUnitId, request, (ad, error) => { if (error) { - ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(error)); + ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(error)); } else { ad.fullScreenContentDelegate = ref.get()?._delegate; ref.get()?._setNative(ad); @@ -169,7 +196,7 @@ export class RewardedInterstitialAd implements IRewardedInterstitialAd { const request = toSerializeRequestOptions(this.#requestOptions); GADRewardedInterstitialAd.loadWithAdUnitIDRequestCompletionHandler(this.#adUnitId, request, (ad, error) => { if (error) { - ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(error)); + ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(error)); } else { ad.fullScreenContentDelegate = ref.get()?._delegate; ref.get()?._setNative(ad); @@ -258,7 +285,7 @@ export class RewardedAd implements IRewardedAd { const ref = new WeakRef(this); GADRewardedAd.loadWithAdUnitIDRequestCompletionHandler(this.#adUnitId, request, (ad, error) => { if (error) { - ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, FirebaseError.fromNative(error)); + ref.get()?._onAdEvent?.(AdEventType.FAILED_TO_LOAD_EVENT, AdmobError.fromNative(error)); } else { ad.fullScreenContentDelegate = ref.get()?._delegate; ref.get()?._setNative(ad); @@ -367,23 +394,23 @@ export class BannerAdSize extends BannerAdSizeBase { } static get BANNER(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeBanner); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.Banner)); } static get FULL_BANNER(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeFullBanner); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.FullBanner)); } static get LARGE_BANNER(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeLargeBanner); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.LargeBanner)); } static get LEADERBOARD(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeLeaderboard); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.LeaderBoard)); } static get MEDIUM_RECTANGLE(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeMediumRectangle); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.MediumRectangle)); } static createAnchoredAdaptiveBanner(width: number, orientation: 'portrait' | 'landscape' | 'device' = 'device'): BannerAdSize { @@ -409,15 +436,15 @@ export class BannerAdSize extends BannerAdSizeBase { } static get FLUID(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeFluid); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.Fluid)); } static get WIDE_SKYSCRAPER(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeSkyscraper); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.WideSkyScraper)); } static get INVALID(): BannerAdSize { - return BannerAdSize.fromNative(GADAdSizeInvalid); + return BannerAdSize.fromNative(TNSGA.createBanner(NSCGABannersSize.Invalid)); } static get SEARCH(): BannerAdSize { @@ -498,11 +525,7 @@ export class BannerAd extends BannerAdBase { } } -declare const FIRApp; - export class Admob implements IAdmob { - #app: FirebaseApp; - constructor() { if (defaultAdmob) { return defaultAdmob; @@ -531,6 +554,10 @@ export class Admob implements IAdmob { }); } + static getInstance(): Admob { + return new Admob(); + } + #requestConfiguration: RequestConfiguration = {}; set requestConfiguration(requestConfiguration: RequestConfiguration) { @@ -615,12 +642,8 @@ export class Admob implements IAdmob { return this.requestConfiguration; } - get app(): FirebaseApp { - if (!this.#app) { - // @ts-ignore - this.#app = FirebaseApp.fromNative(FIRApp.defaultApp()); - } - return this.#app; + get app() { + return global?.__defaultFirebaseApp; } } @@ -641,7 +664,7 @@ class GADFullScreenContentDelegateImpl extends NSObject implements GADFullScreen } adDidFailToPresentFullScreenContentWithError(ad: GADFullScreenPresentingAd, error: NSError): void { - this._owner?.get()?._onAdEvent?.(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, FirebaseError.fromNative(error), this._owner.get?.()); + this._owner?.get()?._onAdEvent?.(AdEventType.FAILED_TO_SHOW_FULL_SCREEN_CONTENT, AdmobError.fromNative(error), this._owner.get?.()); } adDidPresentFullScreenContent(ad: GADFullScreenPresentingAd): void { @@ -681,7 +704,7 @@ class GADBannerViewDelegateImpl extends NSObject implements GADBannerViewDelegat this._owner?.get()?.notify?.({ eventName: AdEventType.FAILED_TO_LOAD_EVENT, object: this._owner?.get(), - error: FirebaseError.fromNative(error), + error: AdmobError.fromNative(error), }); } diff --git a/packages/firebase-admob/platforms/ios/src/TNSGA.swift b/packages/firebase-admob/platforms/ios/src/TNSGA.swift index 39614502..c5941049 100644 --- a/packages/firebase-admob/platforms/ios/src/TNSGA.swift +++ b/packages/firebase-admob/platforms/ios/src/TNSGA.swift @@ -98,4 +98,38 @@ public class TNSGA:NSObject { return GADCurrentOrientationInlineAdaptiveBannerAdSizeWithWidth(width) } } + + @objc(NSCGABannersSize) + public enum NSCGABannersSize: Int8, RawRepresentable { + public typealias RawValue = Int8 + case Banner + case FullBanner + case LargeBanner + case LeaderBoard + case MediumRectangle + case Fluid + case WideSkyScraper + case Invalid + } + + public static func createBanner(_ size: NSCGABannersSize) -> GADAdSize { + switch size { + case .Banner: + return GADAdSizeBanner + case .FullBanner: + return GADAdSizeFullBanner + case .LargeBanner: + return GADAdSizeLargeBanner + case .LeaderBoard: + return GADAdSizeLeaderboard + case .MediumRectangle: + return GADAdSizeMediumRectangle + case .Fluid: + return GADAdSizeFluid + case .WideSkyScraper: + return GADAdSizeSkyscraper + case .Invalid: + return GADAdSizeInvalid + } + } } diff --git a/packages/firebase-admob/typings/objc!TNSGA.d.ts b/packages/firebase-admob/typings/objc!TNSGA.d.ts index 0088d3a6..0d7c10aa 100644 --- a/packages/firebase-admob/typings/objc!TNSGA.d.ts +++ b/packages/firebase-admob/typings/objc!TNSGA.d.ts @@ -1,24 +1,38 @@ +declare const enum NSCGABannersSize { + Banner = 0, -declare const enum AdLoaderAdType { + FullBanner = 1, + + LargeBanner = 2, + + LeaderBoard = 3, + + MediumRectangle = 4, + + Fluid = 5, + + WideSkyScraper = 6, + Invalid = 7, +} + +declare const enum AdLoaderAdType { CustomNative = 0, GAMBanner = 1, - Native = 2 + Native = 2, } declare const enum Orientation { - Portrait = 0, Landscape = 1, - Device = 2 + Device = 2, } declare class TNSGA extends NSObject { - static alloc(): TNSGA; // inherited from NSObject static createAnchoredAdaptiveBanner(width: number, orientation: Orientation): GADAdSize; @@ -28,4 +42,6 @@ declare class TNSGA extends NSObject { static AdLoaderAdTypeToString(type: AdLoaderAdType): string; static new(): TNSGA; // inherited from NSObject + + static createBanner(size: NSCGABannersSize): GADAdSize; } diff --git a/packages/firebase-core/index.android.ts b/packages/firebase-core/index.android.ts index 299f22eb..3609c60a 100644 --- a/packages/firebase-core/index.android.ts +++ b/packages/firebase-core/index.android.ts @@ -181,6 +181,7 @@ export class Firebase { } Firebase.#onResumeQueue.push(callback); } + static #appDidLaunch = false; static #inForeground = false; static get inForeground() { return Firebase.#inForeground; @@ -192,6 +193,7 @@ export class Firebase { firebaseInstance = this; Application.android.on('activityResumed', (args) => { Firebase.#inForeground = true; + Firebase.#appDidLaunch = true; Firebase.#onResumeQueue.forEach((callback) => { callback(); }); @@ -393,6 +395,8 @@ export class Firebase { if (!defaultApp) { defaultApp = fbApp; + // For backward compat remove @v3 + global.__defaultFirebaseApp = fbApp; } resolve(fbApp); } catch (e) { @@ -400,6 +404,11 @@ export class Firebase { } }); } + + // For backward compat remove @v3 + admob() { + return global?.__admob; + } } export function firebase() { diff --git a/packages/firebase-core/index.ios.ts b/packages/firebase-core/index.ios.ts index e3814606..ce72ef31 100644 --- a/packages/firebase-core/index.ios.ts +++ b/packages/firebase-core/index.ios.ts @@ -269,6 +269,7 @@ export class Firebase { Firebase.#onResumeQueue.push(callback); } static #inForeground = false; + static #appDidLaunch = false; static get inForeground() { return Firebase.#inForeground; } @@ -287,6 +288,7 @@ export class Firebase { Application.on('resume', (args) => { Firebase.#inForeground = true; + Firebase.#appDidLaunch = true; }); Application.on('suspend', (args) => { @@ -397,6 +399,8 @@ export class Firebase { if (isDefault) { defaultApp = fbApp; + // For backward compat remove @v3 + global.__defaultFirebaseApp = fbApp; } if (!isDefault) { @@ -504,6 +508,11 @@ export class Firebase { } }); } + + // For backward compat remove @v3 + admob() { + return global?.__admob; + } } export function firebase() { diff --git a/packages/firebase-database/index.android.ts b/packages/firebase-database/index.android.ts index d50b7442..5239f789 100644 --- a/packages/firebase-database/index.android.ts +++ b/packages/firebase-database/index.android.ts @@ -191,7 +191,7 @@ export class Query implements IQuery { off(eventType?: EventType, callback?: (a: DataSnapshot, b: string) => void, context?: Record): void { const handle = callback?.['__fbHandle']; const event = callback?.['__fbEventType']; - if (typeof handle === 'number' && event === eventType) { + if (handle && event === eventType) { if (this.#handles.has(callback)) { this.native.removeEventListener(handle as any); callback['__fbHandle'] = undefined; diff --git a/packages/firebase-dynamic-links/platforms/ios/src/TNSFirebaseDynamicLinksAppDelegate.swift b/packages/firebase-dynamic-links/platforms/ios/src/TNSFirebaseDynamicLinksAppDelegate.swift index d13b9835..8279b9bc 100644 --- a/packages/firebase-dynamic-links/platforms/ios/src/TNSFirebaseDynamicLinksAppDelegate.swift +++ b/packages/firebase-dynamic-links/platforms/ios/src/TNSFirebaseDynamicLinksAppDelegate.swift @@ -25,17 +25,18 @@ public class TNSFirebaseDynamicLinksAppDelegate: UIResponder , UIApplicationDele } - @objc public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - - return application(app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String ?? "", annotation: options[UIApplication.OpenURLOptionsKey.annotation]) - } - - @objc public func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { - - var dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url); + static func handleLink(url: URL) -> Bool { + let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url); if (dynamicLink == nil) { - dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromUniversalLink: url); + DynamicLinks.dynamicLinks().dynamicLink(fromUniversalLink: url, completion: { dynamicLink, error in + if (dynamicLink?.url != nil) { + DispatchQueue.main.async { + TNSFirebaseDynamicLinksAppDelegate.onLinkCallback?(dynamicLink!) + } + } + }) + return false } if (dynamicLink == nil) { @@ -47,7 +48,14 @@ public class TNSFirebaseDynamicLinksAppDelegate: UIResponder , UIApplicationDele } return false - + } + + @objc public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool { + return TNSFirebaseDynamicLinksAppDelegate.handleLink(url: url) + } + + @objc public func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { + return TNSFirebaseDynamicLinksAppDelegate.handleLink(url: url) } @objc public func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { diff --git a/packages/firebase-messaging-core/index.android.ts b/packages/firebase-messaging-core/index.android.ts index 68ea5ef2..43e611a3 100644 --- a/packages/firebase-messaging-core/index.android.ts +++ b/packages/firebase-messaging-core/index.android.ts @@ -37,7 +37,7 @@ function ensureCallback() { } } }; - if (!MessagingCore.inForeground) { + if (!MessagingCore.inForeground || !MessagingCore.appDidLaunch) { MessagingCore.addToResumeQueue(exec); } else { exec(); @@ -91,9 +91,13 @@ export class MessagingCore implements IMessagingCore { MessagingCore.#onResumeQueue.push(callback); } static #inForeground = false; + static #appDidLaunch = false; static get inForeground() { return MessagingCore.#inForeground; } + static get appDidLaunch() { + return MessagingCore.#appDidLaunch; + } constructor() { if (defaultInstance) { @@ -105,6 +109,7 @@ export class MessagingCore implements IMessagingCore { Application.android.on('activityResumed', (args) => { MessagingCore.#inForeground = true; + MessagingCore.#appDidLaunch = true; MessagingCore.#onResumeQueue.forEach((callback) => { callback(); }); diff --git a/packages/firebase-messaging-core/index.ios.ts b/packages/firebase-messaging-core/index.ios.ts index 6d9690bc..d3e29876 100644 --- a/packages/firebase-messaging-core/index.ios.ts +++ b/packages/firebase-messaging-core/index.ios.ts @@ -76,9 +76,13 @@ export class MessagingCore implements IMessagingCore { MessagingCore.#onResumeQueue.push(callback); } static #inForeground = false; + static #appDidLaunch = false; static get inForeground() { return MessagingCore.#inForeground; } + static get appDidLaunch() { + return MessagingCore.#appDidLaunch; + } constructor() { if (defaultInstance) { @@ -95,6 +99,7 @@ export class MessagingCore implements IMessagingCore { Application.on('resume', (args) => { MessagingCore.#inForeground = true; + MessagingCore.#appDidLaunch = true; }); Application.on('suspend', (args) => {