EddyVerbruggen / nativescript-plugin-firebase

:fire: NativeScript plugin for Firebase
https://firebase.google.com
MIT License
1.01k stars 444 forks source link

[iOS] Background notifications #382

Open mfradcourt opened 7 years ago

mfradcourt commented 7 years ago

I'm facing a problem to setup the background notifications on a real iphone device (iphone 5s, iOS 10.3.2).

I receive notification messages from FCM when the app is open but i don't receive any notifications from APN when the app is closed.

I'm trying to debug this for few days now but cannot figure out where is the problem... Looks like Firebase is not sending the message to APN but my .p12 key is correctly setup...

  1. Does the plugin takes care to register the device with APN? (This should be fully handled by FCM swizzling right?)
  2. Is there an easy way to get the APN Device Token (to send a message directly to APN)
  3. Any other idea of how i can debug this?
EddyVerbruggen commented 7 years ago

Please share a repo I can clone and run/debug as it can be anything really.

KingAndroid commented 7 years ago

Hello @EddyVerbruggen Please check this example repository. Thanks. https://github.com/KingAndroid/nativescript-plugin-firebase-demo/tree/fcm_ios_background

EddyVerbruggen commented 7 years ago

Thanks @KingAndroid, I'll check it out soon!

EddyVerbruggen commented 7 years ago

Hey @KingAndroid, have you seen this comment? It's exactly what may fix the problem for you as you also run init before the app is launched. It seems to work nicely for the others in that thread.

mfradcourt commented 7 years ago

@EddyVerbruggen I was also running the init before the launch of the app... Executing the init after the loading of the app is working for me. I get the background notifications as expected.

KingAndroid commented 7 years ago

Still same issue. I moved init method to first view pageloaded method. But I still did not get notification on background. My test case is as follow.

  1. remove app fully from iDevice or Simulator
  2. launch app
  3. test in forground -> works well
  4. press Home or CMD + L => App will be on background.
  5. test again -> not work!

What is still missing? Thanks.

mfradcourt commented 7 years ago

@KingAndroid App setup looks good (for the demo project you provided)

I would confirm the following:

Apple setup

Firebase setup

KingAndroid commented 7 years ago

Hello @mfradcourt Thank you for your response. Because fcm works well on forground, I think bundle id or certification issue can be ignored. am i right ? or ...

KingAndroid commented 7 years ago

One more, do I should test on real device? I am trying on ios simulator, it works on forground and not on background Thanks

mfradcourt commented 7 years ago

Firebase notifications are different than Apple Notifications. Firebase is sending a notification to Apple which is then sending it to your device.

You need a real device to test the background notifications (as far as i know) because you need to register the test device in your apple developer account and add it to your apple profile.

You should get this message in the command line: [Firebase/Messaging][I-IID002002] Failed to fetch APNS token Error Domain=NSCocoaErrorDomain Code=3010 "remote notifications are not supported in the simulator" UserInfo={NSLocalizedDescription=remote notifications are not supported in the simulator}

KingAndroid commented 7 years ago

I've tried as below.

  1. all revoke certs and try again
  2. test on real device -> got token, works on foreground, but not on background. I don't know what missing or wrong is! This issue makes me crazy!

Yes, but we can try with iOSEmulatorFlush: true on ios simulator.

KingAndroid commented 7 years ago

I've test using native push notification by creating new swift project. Used same app bundle id, and used same certifications. All works well perfectly includes background and killed case.

Which case would we try then? Thanks

spstratis commented 7 years ago

I also tried moving the init method so that it was called in one of the starting view models and am still not able to receive notifications while the application is running in the background.

I'm using NS 3.0.3, iOS 10.3.2 and the 3.11.4 version of the Firebase plugin.

KingAndroid commented 7 years ago

I am using NS 3.0.2, ios 10.3.2 and 3.12.0

KingAndroid commented 7 years ago

In fact this issues was fixed on this plugin, but after merging, it was removed suddenly.

spstratis commented 7 years ago

bah even after updating to 3.12.0 I'm still not getting the notifications while the app is in the background. I guess I could try downgrading NS to 3.0.2 but I feel as though that isn't the problem.

spstratis commented 7 years ago

I can now confirm that on ver 3.12.0 the plugin, moving the firebase initialization outside of the app.ts file and into one of my first onloaded events fixed the issue. Notifications are now displaying.

sserdyuk commented 7 years ago

I am experiencing the same issue. Tried moving firebase.init() to application.on(application.launchEvent, but that doesn't help. I am weary of moving the init further in the app, as I am planning to use firebase to store app state, and I need to be able to load it early on. Only I am using version 4.0.2.

sserdyuk commented 7 years ago

@EddyVerbruggen To follow up on my previous comment, downgrading to 3.12 helped me, so you may give 4.0.2 a hard look. Also, I'd recommend to change the README, to call firebase.init on application.launchEvent event as this corresponds with what Firebase docs are recommending. Cheers and Thank you for a great plugin!

Configure a FIRApp shared instance, typically in your application's application:didFinishLaunchingWithOptions: method

sserdyuk commented 7 years ago

@EddyVerbruggen While initing on the launch event works in iOS, it doesn't in Android (token never gets assigned). In my app, I made two branches based on the platform. Again, this is with version 3.12.

I am still having issues with some edge cases. For example, on iOS background notification appears, and I can launch the app from it, but once launched, the app doesn't get the details of the message.

Another strange issue is that on Android, the data part isn't being passed when the app is active. Background messages work fine.

briandilley commented 7 years ago

I'm having a similar problem. Everything works fine for me on both platforms in development. But on iOS once I've submitted it to the app store and install it via TestFlight I don't receive the notifications (registration works and I get an FCM push token though).

sserdyuk commented 7 years ago

@briandilley do you have the correct apn certificate loaded into firebase settings for iOS production?

briandilley commented 7 years ago

@sserdyuk /facepalm - that was it :(

briandilley commented 7 years ago

Except now it only works on the first launch of the app - and only in the foreground. never in the background.

briandilley commented 7 years ago

I wrote some wrapper/helper code to make using nativescript-plugin-firebase easier for notifications. You'll find said code below. Basically, just call setupNotificationManager() in your main.ts before starting the application and then to listen to notifications use NotificationManager.instance.notifications() anywhere else in the app that you'd like to act on them.

This all works perfectly on Android. But on iOS there are no notififcations delivered in the foreground or background on subsequent app launches.

firebase.ts

import {Subscriber} from "rxjs";
import {TeardownLogic} from "rxjs/Subscription";

export type ObservableProducer<T> = {(subscriber: Subscriber<T>) : TeardownLogic};

let _firebase: any = null;

export function firebaseProducer(): ObservableProducer<any> {
    return (subscriber) => {
        if (!_firebase) {
            _firebase = require("nativescript-plugin-firebase");
        }
        subscriber.next(_firebase);
        subscriber.complete();
    };
}

export function createPushtokenProducer(firebase: any): ObservableProducer<string> {
    return (subscriber) => {
        firebase.getCurrentPushToken().then(
            (token) => {
                subscriber.next(token);
                subscriber.complete();
            },
            (error) => subscriber.error(error));
    }
}

export function createInitializationProducer(firebase: any, handler: {(message: any)}): ObservableProducer<any> {
    return (subscriber) => {
        firebase.init({
                onMessageReceivedCallback: (message) => {
                    console.log(`MESSAGE[`
                        + `message.title:${message.title}, `
                        + `message.body:${message.body}]`);
                    handler(message);
                },

                // handles the tokens
                onPushTokenReceivedCallback: (pushToken) => {
                    console.log(`TOKEN[${pushToken}]`);
                }
            })
            .then(
                (value) => {
                    console.log('firebase.init() success');
                    subscriber.next(value);
                    subscriber.complete();
                },
                (error) => {
                    console.log('firebase.init() failure');
                    subscriber.error(error);
                });
    }
}

manager.ts

import {Observable} from "rxjs";
import {createInitializationProducer, createPushtokenProducer, firebaseProducer} from "./firebase";

export type NotificationListener = {(notification: Notification) : void};

export class Notification {
    public constructor(
        public title: string,
        public message: string,
        public data: any) {
    }
}

export class NotificationManager {

    private static _instance: NotificationManager = null;

    public static get instance() : NotificationManager {
        if (NotificationManager._instance == null) {
            NotificationManager._instance = new NotificationManager();
        }
        return NotificationManager._instance;
    }

    private listeners: NotificationListener[] = [];
    private _registered: boolean = false;

    private constructor() {

    }

    public get registered(): boolean {
        return this._registered;
    }

    public notifications(): Observable<Notification> {
        return new Observable((sub) => {
            let listener: NotificationListener = (notification) => sub.next(notification);
            this.addNotificationListener(listener);
            return () => this.removeNotificationListener(listener);
        });
    }

    public broadcastNotification(notification: Notification) {
        this.listeners.slice().forEach((l) => l(notification));
    }

    public broadcastMessage(notification: Notification) {
        this.listeners.slice().forEach((l) => l(notification));
    }

    public addNotificationListener(listener: NotificationListener) {
        this.listeners.push(listener);
    }

    public removeNotificationListener(listener: NotificationListener) {
        let index = this.listeners.indexOf(listener);
        if (index != -1) {
            this.listeners.splice(index, 1);
        }
    }

    public getPushToken(): Observable<string> {
        return new Observable(firebaseProducer())
            .flatMap((firebase) => new Observable(createPushtokenProducer(firebase)));
    }

    public registerForPushNotifications(): Observable<string> {
        return new Observable(firebaseProducer())
            .flatMap((firebase) => Observable.if(
                // if
                () => this._registered == true,
                    // then
                    new Observable(createPushtokenProducer(firebase)),
                    // else
                    new Observable(createInitializationProducer(firebase,(message) => {
                        this._registered = true;
                        this.broadcastNotification(new Notification(message.title, message.body, message.data));
                    }))
                    .flatMap(() => new Observable(createPushtokenProducer(firebase)))
                ));
    }

    public getCurrentPushToken(): Observable<string> {
        return new Observable(firebaseProducer())
            .flatMap((firebase) => new Observable(createPushtokenProducer(firebase)));
    }
}

autoregister.ts

import {NotificationManager} from "./";

export function setupNotificationManager(): void {
    console.log("Autoregistering for push notifications");
    let notificationManager: NotificationManager = NotificationManager.instance;
    notificationManager.registerForPushNotifications()
        .subscribe(
            (pushToken) => console.log(`push notification registration successful: ${pushToken}`),
            (error) => console.error(error, "error autoregistering push notifications"),
            () => console.log("push notification autoregistration completed"));

}
briandilley commented 7 years ago

Any update on this one?

simonox commented 7 years ago

nativescript-push-plugin worked for me. It gives you a APNS token for iOS devices (not a FCM token) and a FCM token for Android devices on registration. You can convert APNS tokens to FCM tokens (via a REST API) to use FCM as a single remote push provider.

sudhanshu-15 commented 7 years ago

I am also facing the same issue, background notification on iOS has stopped working. It was working before and my organization has been using push notifications for some time now. I followed the instructions mentioned above and change the init from before app start to after app start. This made Android to stop receiving notifications in foreground. Where as iOS was not able to receive background notification in either case. Any help will be appreciated. I hope we can resolve this issue again.

briandilley commented 7 years ago

Its working for me now, did a fresh setup.

On Aug 19, 2017 5:56 PM, "mfradcourt" notifications@github.com wrote:

@briandilley https://github.com/briandilley Are you sure you use setup the apple certificate to be production? Common error is to configure for development... The notifications will not work with unregistered devices

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/EddyVerbruggen/nativescript-plugin-firebase/issues/382#issuecomment-323556476, or mute the thread https://github.com/notifications/unsubscribe-auth/ABBNz-0sD4mZdBGeZBmueWyo3tpWEYS0ks5sZ4ROgaJpZM4NuEgu .

simonettoa commented 7 years ago

hi @briandilley, I am in the same situation. In android it's working but in iOS is not, I receive the token but no notifications arrive. I just delete and updated also the certificates and update the firebase *.p12 files, still no success.

What do you mean by "did a fresh setup"? Thanks

sundargsv commented 7 years ago

Hi @briandilley, If you could share your Push Notification (Demo app - iOS) It would be very grateful. Request you to help us by sharing working samples ASAP.

And now I'm confused ? Like should I use nativescript-plugin-firebase or any other alternatives for iOS push notifications (Background and Foreground). Thanks in advance.

shiamalon commented 6 years ago

I ended up on this thread a lot when I was trying to figure out how to get it working. IOs background notifications I could only get them working on a real device.

EddyVerbruggen commented 6 years ago

@shiamalon Background notifications on iOS sim are not possible. Not related to this plugin nor Firebase.

shiamalon commented 6 years ago

@EddyVerbruggen I never saw that info very clear anywhere that's why I'm saying it. Maybe it is. It's just my 2 cents noob.

Btw thanks for your plugin!

giordanocardillo commented 6 years ago

Here I have iOS 11.2 on an iPhone 5S and I cannot get the background notifications to work. Plugin version is 5.0.2... Please @EddyVerbruggen gimme some help! I've configured the FCM with APNs Auth Key (as suggested by Google) and not with the two certs (I can't get the p12 for the development one because it hasn't a key). The plugin firebase.init method is being called in the onpageloaded method of the first application view... What can I do to get it working? On Android is all working and on iOS i can successfully get the notification inside the app. Is there something needed for the "heads-up" notification to show?

giordanocardillo commented 6 years ago

Ok, solved it, thanks to this https://discourse.nativescript.org/t/how-to-implement-push-notification-on-nativescript-angular-2/173/23

I just had to put a setTimeout around the firebase.init method. I also call it inside the main app bootstrap file.

Frederiks96 commented 5 years ago

I had this problem as well, checked the provisioning profile validity, added the required background modes in the Info.plist, added entitlements to the AppName.entitlements and placed it in the right folder. My setup is using NativeScript-Vue with Typescript and I have come to the conclusion that the Typescript import import Firebase from "nativescript-plugin-firebase"; doesn't seem to work for background notifications? Changing the import to const Firebase = require("nativescript-plugin-firebase"); did the trick. Just wanted to add this in case other users have similar problems..

EddyVerbruggen commented 5 years ago

Thanks for sharing that, @Frederiks96 - I hope the docs in this repo make this clear as well (use require instead of import because the latter gets removed by the TS transpiler if the import is not used). Cheers!

Frederiks96 commented 5 years ago

Thanks for the reply @EddyVerbruggen, I must have missed that then.. My mistake.. But could it be that the import is case sensitive? I imported Firebase (with a capital F) instead of firebase - I am new to Typescript/JS (coming from native iOS), so I am figuring it out as I go..

EddyVerbruggen commented 5 years ago

No worries mate. The import depends a bit on what parts of the plugin you want to use. The demo apps in this repo show several alternatives. But as you found out, the import in the entry component (main.ts / app.ts) must be require unless you also use the import on that page.