question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Code inside notification on click does'nt execute when app is killed in ios.

See original GitHub issue

Hello, i want to execute some code inside notification.on(‘click’) this works well when the app is in front on in the background but does’nt when the app is killed on ios. This does work on android even if the app is killed but it does’nt on ios.

Your Environment

  • Plugin version: ^0.9.0-beta.3
  • Platform: IOS
  • OS version: 12.1
  • Device manufacturer / model: IPHONE 6
  • Cordova version (cordova -v): 8.1.2
  • Cordova platform version (cordova platform ls):4.5.5
  • Plugin config
  • Ionic Version (if using Ionic) ionic 4

Expected Behavior

When clicking on the notification we should get redirected to the specified page.

Actual Behavior

When clicking on the notification the app starts and opens the home page. It does’nt redirect.

Steps to Reproduce

In my app.component.ts :

initializeApp() {
    this.platform.ready().then(() => {
 this.localNotifications.on('click').subscribe((data) => {       
          this.router.navigateByUrl('/notification/' + data.id);
      });
});

This works well in android in the three different states of the app, but does’nt on IOS when the app is killed.

Context

Im trying to execute the code inside the click event, it does work when the app is opened or in the background but not when the app is killed.

Debug logs

Include iOS / Android logs

  • ios XCode logs
  • Android: $ adb logcat

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:7

github_iconTop GitHub Comments

1reaction
StefanReincommented, Jul 16, 2020

EDIT 2 Another thing I stumbled into: For me I had to schedule notifications for 3 days (~180 pushs). Did not work. There is a limit of 64 local notifications which can be scheduled.

So I ended up having 2 queues. The ones which are to be fired and the ones which should be fired. And then always updating onResume and on user interaction when the user canceled some out.

EDIT: Made some important additional notes at the end!

@guntherhoppe @pandeeswaran @adamalexander @kasback @petenickless @maniveltvl

There are different things one needs to consider if using a framework like Angular and Ionic.

  1. Setup event listener before the events fire
  2. Run code to these events in the right context

First app start: What happens?

Usual setup

  1. Our app starts, native code gets executed

  2. Native local push plugin code gets executed and stores (queues) native information for later when our HTML5 world is available

  3. Parallel a WebView gets instantiated and loads the index.html, which then loads everything we need for our HTML5 app. 3.1 Async loading all JS plugin files (the bridge between our HTML5 and the native world). 3.2 Async loading our angular / ionic application file/s.

  4. The local-notification.js file loaded first. 4.1 local-notification.js attaches and listens for the deviceready event. 4.2 All plugins loaded and the deviceready event fires. 4.3 By default the following code fireQueuedEvents gets executed on the deviceready event. 4.4* Now all queued events from our local push plugin fire: None at this point (first app start, remember?).

*We jump later back here

  1. Meanwhile the angular and ionic files necessary to bootstrap our application loaded. 5.1 Our application gets bootstrapped 5.2 We even maybe go crazy and lazy load a LocalPushModule where we store all our Push handling. 5.3 In Angular / Ionic: Wait for the platform ready await this.platform.ready(); 5.4 Now the plugins are there for sure. Setup the event listeners.

Run code in the right context

Angular uses zoneJS for change detection. Sometimes something is triggered in pure JS and your code gets executed (you would see an alert message or a console.log), but the change detection of angular does not get triggered and anything you would write in there to get updated in the angular / ionic world, would not take effect. Therefore we use NgZone and run our code to be executed in the angular zone and change detection will work - also the routing.

import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class PushService {
    constructor(
        private readonly platform: Platform,
        private readonly router: Router,
        private readonly ngZone: NgZone) {
        this.init();
    }

    private get localNotifications() {
        return cordova && cordova.plugins.notification && cordova.plugins.notification.local;
    }

    private async init(): Promise<void> {
        await this.platform.ready();

        this.localNotifications.on('click').subscribe(notification => {
            this.ngZone.run(() => {
                this.router.navigateByUrl('/notification/' + notification.id);
            });
        });
    }
}
  1. Any events? Nope. Let’s create and schedule some (2x: one push in 10 and another one in 30 seconds)! 6.1 App runs in background, bootstrapped and listening for our events. 6.2 Push comes - we do tap the push - the click event fires, our subscribe block gets executed in the angular context, change detection runs and voila, we did route to the notification detail page.

  2. Let’s force quit / close the app and wait for the 2nd push to arrive and tap it.


We start again at the top at point 1 and go up to point 4.4. This time we do start the app the 2nd time and we do have one queued event! Wooohoo! Proceed as before and fire and forget all the queued events!

Does it now work as before when the app was in front- or background? No

Did we already had setup our listeners in Angular / Ionic before the push plugin fired the event? No - We did not setup event listener before the events fired from local-notification.js.

Will the click subscription not just fire after we subscribed because there was some data and someone should have stored it? No - It makes no sense firing events just when someone did subscribe. Just because you subscribed multiple times, does not mean you clicked multiple times.

Is there some data now? No - We were too late. The push click event did fire just a moment before.

Can we fix this? Yes

Can we skip initial firing the events of the local push plugin and postpone the events until we know that we have setup everything in our application (HTML5 world)? Yes - As you’ve maybe seen the skipLocalNotificationReady is being checked before firing the events. Set the global skipLocalNotificationReady variable to true before the local push plugin is loaded.

Working setup

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        window.skipLocalNotificationReady = true;
    </script>
<body>
<app-root></app-root>
</body>
</html>

Can we now manually trigger the queued events of the local push plugin? Yes - Modify our PushService to fire the queued events with calling the fireQueuedEvents() method:

import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class PushService {
    constructor(
        private readonly platform: Platform,
        private readonly router: Router,
        private readonly ngZone: NgZone) {
        this.init();
    }

    private get localNotifications() {
        return cordova && cordova.plugins.notification && cordova.plugins.notification.local;
    }

    private async init(): Promise<void> {
        await this.platform.ready();

        console.log("launchDetails", cordova.plugins.notification.local.launchDetails);

        this.localNotifications.on('click').subscribe(notification => {
            this.ngZone.run(() => {
                this.router.navigateByUrl('/notification/' + notification.id);
            });
        });

        this.localNotifications.fireQueuedEvents();
        // ^ THIS IS NEW
    }
}

Hope this helps someone.

EDIT: Additional notes

The above ONLY applies to the “CLICK” event ( cordova.plugins.notification.local.on('click',..). I have setup alert messages for the click and trigger event. The click always came after a tap on the Notification (App foreground, App background and App was closed (did force quit the app)). The trigger event however did ONLY appeared when the app was in foreground. It did NOT fire the trigger event, when my app was in background and I then opened the app (via click or just opened the app by tapping the app icon).

Can we work around this? Yes

How? Save a list of all push alerts you want to trigger with their time locally on the device (persistently, not local storage). Update the list also, if you need to and times change etc.

Then on app start and on resume, compare the device time with the time of all notifications.

Attention: The timeout is needed, in case some native plugins will be called (for example reading the persistent data) or there will be some problems.

Source: Cordova 9 iOS resume event

When called from a resume event handler, interactive functions such as alert() need to be wrapped in a setTimeout() call with a timeout value of zero, or else the app hangs.

this.platform.resume.subscribe(() => setTimeout(() => {
    this.ngZone.run(() => {
        this.updateCurrentStatus();
    });
}, 100));

Get triggered notifications could be something like that (I used linqTS

async getTriggeredNotifications(): Promise<{ id: number, dateTime: string }[]> {
    const alerts = await this.settingsService.getAllAlerts();
    const now = moment().add(5, 'seconds'); // Just in case you had the app open and the device is super fast and compares everything in some milliseconds (which it does..).

    return new List(alerts)
                            .Where(x => x.dateTime && moment(x.dateTime).isBefore(now, 'seconds'))
                            .OrderByDescending(key => key.id).FirstOrDefault()
                            .ToArray();
}

// Then later somewhere:
const triggeredNotifications = await this.getTriggeredNotifications();
const latestTriggeredNotification = new List(triggeredNotifications).OrderByDescending(key => key.id).First();
1reaction
guntherhoppecommented, Feb 3, 2020

I determined this problem also on Android platform. My analysis point to a simple fact: The LocalNotification#fireEvent is performed before a listener could even be registered at app startup. I also registered my listener within app.component.ts, but unfortunately this is simply too late in real application scenarios. It’s a timing issue.

The “deviceready” event of the (native) plugin is called before the actual “deviceready” is performed on Typescript side (not Javascript side!). So there is actually no chance to handle the click event on app cold start with things like Ionic’s ```Platform.ready()`` within your app components at all.

I implemented a (really 😉 ) “hacky” workaround, which does the trick by intercepting the click event: (workaround goes directly into index.html)

  <body>
    ...

    <script>
      document.addEventListener("deviceready", function(){
        var cordovaPluginsNotificationLocalCoreFireEvent = cordova.plugins.notification.local.core.fireEvent;
        cordova.plugins.notification.local.core.fireEvent = function(name, notification, event) {
          window.appStart = { notification: notification };
          cordovaPluginsNotificationLocalCoreFireEvent.call(cordova.plugins.notification.local.core, name, notification, event);
        }
      });
    </script>
    
  </body>

With that you are able to retrieve an once occurred local notification afterwards when you app code actually is up an running by checking:

    let appStart: { notification: { id: number }};
    if(window['appStart']) {
      appStart = window['appStart'];
      if(appStart.notification) {
        callback(appStart.notification);
      }
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

ios - Notification - when app is killed - Stack Overflow
This is a normal behavior, and there is no workaround. If your app is killed by the user, it won't be able to...
Read more >
onNotification not being called when app was killed on iOS
It gets called when the app starts in background due to geofencing events and a local notification is shown. During this app start...
Read more >
App does not launch by data FCM | Apple Developer Forums
Hi all,. We used to launch our app in background by a silent FCM push and the app will display a local notification...
Read more >
Notifications Not Shown - Mobile Push
When an app is in a Force Stopped / Force Killed state most events including FCM messages for push notifications will not be...
Read more >
How to run code when your app is terminated
When you check that box and click Run, you've modified the Run schema so that your app is always run as a background...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found