Capacitor Local Notifications don't fire on Android, and randomly fire on iOS

Hey all,

I have a React/Ionic app that uses capacitor local notifications v4.0.0. I have followed the instructions on the documentation to the best of my ability and included the

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

In my androidManifest.xml

For whatever reason, my android monthly notifications never fire, despite passing all my jest tests. My iOS notifications fire every few days. Both should fire off a local notification on the 27th of every month. I am absolutely confused, and unfortunately this is a mission critical part of my application. What am I doing wrong?

Here is my code:

import { useEffect } from 'react';
import { LocalNotifications } from '@capacitor/local-notifications';
import { Storage } from '@capacitor/storage';

import getNextNotificationDates from '../util/getNextNotificationDates';

const useLocalNotifications = () => {
  const STORAGE_KEY = '2023-04-23';

  const scheduleYearlyNotifications = async () => {
    const pending = await LocalNotifications.getPending();
    const currentDate = new Date().toISOString();

    if (
      !pending.notifications.some((notification) =>
        [1, 2, 3].includes(notification.id)
      )
    ) {
      const [nextJuly1st, nextSept23rd, nextSept30th] =
        getNextNotificationDates(currentDate);

      LocalNotifications.schedule({
        notifications: [
          {
            title: `Claim Your Tax Refund Now! šŸ’°`,
            body: 'You can now file for your fuel refund! Press the file return button to get started.',
            id: 1,
            schedule: { at: nextJuly1st, allowWhileIdle: true },
          },
          {
            title: 'One Week Left To File! ā³',
            body: 'You have one week left to file for your fuel refund!',
            id: 2,
            schedule: { at: nextSept23rd, allowWhileIdle: true },
          },
          {
            title: 'One Day Left To File! šŸšØā°',
            body: 'Today is the last day to file for your fuel refund!',
            id: 3,
            schedule: { at: nextSept30th, allowWhileIdle: true },
          },
        ],
      });
    }
  };

  const scheduleMonthlyNotification = async () => {
    const pending = await LocalNotifications.getPending();

    if (!pending.notifications.some((notification) => notification.id === 4)) {
      LocalNotifications.schedule({
        notifications: [
          {
            title: 'Monthly Receipt Reminder ā›½ļø',
            body: `Don't forget to upload your fuel receipts!`,
            id: 4,
            schedule: {
              on: {
                day: 27,
                hour: 12,
                minute: 0,
                second: 0,
              },
              allowWhileIdle: true,
            },
          },
        ],
      });
    }
  };

  useEffect(() => {
    (async () => {
      try {
        const permissions = await LocalNotifications.requestPermissions();

        if (permissions.display === 'granted') {
          const pending = await LocalNotifications.getPending();
          console.log('pending', pending);
          const { value } = await Storage.get({ key: 'notificationsSetUp' });
          console.log('value', value);
          if (!value || value !== STORAGE_KEY) {
            await scheduleYearlyNotifications();
            await scheduleMonthlyNotification();
            await Storage.set({
              key: 'notificationsSetUp',
              value: STORAGE_KEY,
            });
          }

          console.log('pending', await LocalNotifications.getPending());

          LocalNotifications.addListener(
            'localNotificationReceived',
            async (notification) => {
              switch (notification.id) {
                case 1:
                case 2:
                case 3:
                  // Remove the current yearly notifications
                  await LocalNotifications.cancel({
                    notifications: [{ id: 1 }, { id: 2 }, { id: 3 }],
                  });

                  // Reschedule the yearly notifications
                  await scheduleYearlyNotifications();
                  break;

                case 4:
                  // Remove the current yearly notifications
                  await LocalNotifications.cancel({
                    notifications: [{ id: 4 }],
                  });

                  // Reschedule the monthly notifications
                  await scheduleMonthlyNotification();
                  break;

                default:
                  console.log(
                    'Unknown notification ID received:',
                    notification.id
                  );
              }
            }
          );
        } else {
          console.log('User denied local notifications');
        }
      } catch (e) {
        console.log('e', e);
      }
    })();
  }, []);

  return null;
};

export default useLocalNotifications;

1 Like

Quickly looking at things, itā€™s had to tell without seeing a full working demo.

Can you isolate it in a git repo and we can test that?

EDIT: Also adding that @capacitor/storage was deprecated and renamed to @capacitor/preferences last year, so not sure if that could be causing issues.

1 Like

Interesting catch on the deprecated capacitor package. Let me update that later in the day and see if that clears up any issues. If not, I will try and report back with a working sample

hi! Iā€™m currently having the same issue as you with the last version of Capacitor 4. On IOS, it fires radomnly ignoring the schedule completlyā€¦ could you help me with a fix for this?

Seems that the documentation for Android is not completed on the page. Iā€™m doiong exactly what is documented on the guide but iā€™m not able to get any notification scheduled on Android. Itā€™s working pretty good on IOS but Androidā€¦

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

This was added to the AndroidManifest.xml.
and the next lines are the configuration and code that i did for schedule local notifications:

var notificationsLIST: LocalNotificationSchema[] = [];

if (this.device.includes("android")) {

        await LocalNotifications.createChannel({
          id: "alertas",
          name: "alertas",
          description: "Canal para notificaciones locales",
          sound: "beep.wav",
          importance: 3,
          lights: true,
          vibration: true
        });
}

var objectChedule: LocalNotificationSchema = {
              title: "Portal del empleado",
              body: "Recuerde registrar su jornada en el sistema de fichaje.",
              id: randomID (this is a variable),
              schedule: {
                every: "week",
                on: {
                  hour: hour (this is a variable),
                  minute: minute (this is a variable),
                  weekday: valorDia (this is a variable)
                }
              }
            };

if (this.device.includes("android")) {
              objectChedule.channelId = "alertas";
              objectChedule.schedule.allowWhileIdle = true;
 }

notificationsLIST.push(objectChedule);

 var scheduleResult: ScheduleResult = await LocalNotifications.schedule(
        {
          notifications: notificationsLIST
        }
);

This code is working pretty good on IOS, but Android seems like is not working. Not working on android 13, and the next configuration on variables.gradle.

    minSdkVersion = 22
    compileSdkVersion = 33
    targetSdkVersion = 33
    androidxActivityVersion = '1.4.0'
    androidxAppCompatVersion = '1.4.2'
    androidxCoordinatorLayoutVersion = '1.2.0'
    androidxCoreVersion = '1.8.0'
    androidxFragmentVersion = '1.4.1'
    coreSplashScreenVersion = '1.0.0-rc01'
    androidxWebkitVersion = '1.4.0'
    junitVersion = '4.13.2'
    androidxJunitVersion = '1.1.3'
    androidxEspressoCoreVersion = '3.4.0'
    cordovaAndroidVersion = '10.1.1'
    firebaseMessagingVersion = '23.0.5'
    playServicesLocationVersion = '20.0.0'
    androidPlayCore = '1.9.0'
}

image

1 Like

First, you need to upgrade to Capacitor 5 so you can upgrade Local Notifications to v5. v5 supports requesting and checking permissions that were added as a requirement in Android 13.

  • local-notifications: support Android 13+ permissions (#1189) (ace7d73)

Source

1 Like

So you think these notifications are not firing because iā€™m using capacitor 4?
Iā€™ll try to upgrade to capacitor 5 the application then.

is the code is properly applied? i mean, iā€™m not doing anything wrong for schedule notificationsā€¦ right?

Donā€™t bother upgrading. It does not work in 5 either. I noticed on android using the cron for notifications I had to subtract one from the number I needed. So August would be 7, etc.

I tried too with capacitor 5 and is not workingā€¦
so any way to schedule on android???

The problem what i see is that using capacitor 5 and the last version of Local notifications- capacitor plugin, if i use checkPermissions(), it always return ā€˜grantedā€™, never showed to me the propmpt asking for any permission. I uninstalled the applications and started again and it works as the same way. Always ā€˜grantedā€™. So i checked the app configuration on android and it has the notifications activated by default and the channel created correctly, but NEVER fire any schedule notifications.

1 Like

I am running Capacitor 5 and have both the permission prompt working on Android 13+ and have notifications working being scheduled daily at a specific time so the plugin definitely works :slight_smile:

I think we need a full isolated example in a Git repo as mhartington mentioned.

I do see though that you are using every and on for your schedule. I believe you only want to use one or the other. The documentation says: ā€œUse either at, on, or every to schedule notifications.ā€

1 Like

Iā€™ll try to prepare a git repo with an example, but as you said, maybe i should delete the ā€˜everyā€™ property.
The problem is that i want the schedule every week of the yearā€¦

do you think that with this:

on: {
     hour: hour (this is a variable),
     minute: minute (this is a variable),
     weekday: valorDia (this is a variable)
}

it will be fired every week? not necesary the ā€˜everyā€™ property?

I would say you are correct from my understanding. Adding weekday to on and removing every should do what you are looking for. weekday was added after I implemented reminders in my app so I havenā€™t used it yet but will be soon.

1 Like

Iā€™ve been facing a similar issue with Capacitor local notifications on Android, and itā€™s been quite a puzzle to solve. In my case, I found that ensuring the app is running in the foreground seems to help with Android notifications not firing consistently. Also, double-checking the notification scheduling code and permissions is crucial. Sometimes, seemingly unrelated code changes can affect notifications.

2 Likes

done! the mistake was the ā€˜everyā€™.
Using ā€˜onā€™ is everything i need as you said, and the ā€˜everyā€™ was broking the cron schedule on Android.
Maybe is necesary a return Object or something like that on the Schedule() from LocalNotificationsā€¦ hope capacitorā€™s team read this.

1 Like