Local Notifications in Ionic2

Getting Familiar with Local Notifications in Ionic 2



·

If you’ve used pretty much any app at all, then you would have received a notification from it at some point. Notifications are little messages from a specific app that can pop up on the user’s device to alert them to something, even if they are not currently using that particular app.

There are two main types of notifications when it comes to mobile applications: local notifications and push notifications. It can be a little confusing to understand the difference, but essentially:

  • Local notifications can only be triggered by events on the user’s device
  • Push notifications can be triggered by events outside of the user’s device

When using a local notification, the app talks to that user’s device and tells it to set up a notification that will be triggered later. When the user exits that app, the device knows when it needs to trigger that notification, so it’s not a problem that the app is no longer open. However, let’s say the app in question is a messaging app. If the user receives a message from another user after they have already exited the app, there is no way to notify that user of the new message with a local notification.

Local Notifications

On the other hand, a push notification (although it looks identical to a local notification) can be sent to a user at any time, even if they have the app closed. This relies on an external service like the Apple Push Notification Service (APNS) or Google Cloud Messaging (GCM, which has now become Firebase Cloud Messaging), and those services can send a notification to a registered device at any time. This means that even though the user might have the application completely closed, a push notification can still be used to alert a user to a message that has been sent by another user.

Push Notifications

Implementing push notifications can be quite tricky, and developers generally use a 3rd party service (like Ionic Push or Pushwoosh) to make the process a little easier. However, these services generally charge a monthly fee. Although local notifications are less flexible than push notifications, they are super simple to set up and don’t require any external services.

In this tutorial, we are going to walk through how you can create a basic application that can schedule local notifications. The application will allow the user to choose a time that they would like to be notified and days of the week that they would like to receive that notification for. In the end, it will look something like this:

Local Notifications in Ionic 2

Before We Get Started

Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 set up on your machine.

If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.

1. Generate a New Ionic 2 Application

Let’s start by generating a new Ionic 2 application. The application is just going to have one screen, so we are going to just stick with a simple blank application.

Run the following command to generate a new application:

ionic start ionic2-local-notifications blank --v2

Run the following command to make the new project your current directory:

cd ionic2-local-notifications

We are also going to have to install a couple of dependencies. We will be making use of the Local Notifications plugin, so we will need to install that.

Run the following command to install the Local Notifications plugin:

ionic plugin add de.appplant.cordova.plugin.local-notification

We are going to be working with dates and times as well, so to make our job a little easier we are going to install the Moment.js library, which makes working with dates and times a lot easier.

Run the following command to install Moment.js:

npm install moment --save

2. Set up the Home Page

All the functionality for this application is going to be contained within our HomePage component. Generally, it would probably be a better idea to separate the logic for handling local notifications into its own provider (which would then allow for reuse outside of the home page). The requirements for this app are quite simple though, so it’s going to be more straight forward to just add all the functionality into the home page.

We’re going to start off by creating a basic skeleton of the TypeScript file for the home page, and then we will add in the functionality bit by bit.

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { NavController, Platform, AlertController } from 'ionic-angular';
import { LocalNotifications } from 'ionic-native';
import * as moment from 'moment';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    notifyTime: any;
    notifications: any[] = [];
    days: any[];
    chosenHours: number;
    chosenMinutes: number;

    constructor(public navCtrl: NavController, public platform: Platform, public alertCtrl: AlertController) {

        this.notifyTime = moment(new Date()).format();

        this.chosenHours = new Date().getHours();
        this.chosenMinutes = new Date().getMinutes();

        this.days = [
            {title: 'Monday', dayCode: 1, checked: false},
            {title: 'Tuesday', dayCode: 2, checked: false},
            {title: 'Wednesday', dayCode: 3, checked: false},
            {title: 'Thursday', dayCode: 4, checked: false},
            {title: 'Friday', dayCode: 5, checked: false},
            {title: 'Saturday', dayCode: 6, checked: false},
            {title: 'Sunday', dayCode: 0, checked: false}
        ];

    }

    ionViewDidLoad(){

    }

    timeChange(time){

    }

    addNotifications(){

    }

    cancelAll(){

    }

}

Aside from our normal imports, we are also importing LocalNotifications from Ionic Native which will make the local notifications plugin easier to work with, and we are also importing the moment library that we installed.

We’ve set up a few member variables here as well, so let’s go through what each of those do:

  • notifyTime will contain an ISO datetime string to set a default time for the <ion-datetime> input field we will be using
  • notifications is an array that we will add all of the local notifications we plan to add to
  • days is an array that will contain objects representing the days of the week
  • chosenHours will represent the hour of the day that the user wants to be notified
  • chosenMinutes will represent the minutes of the hour that the user wants to be notified

We then set up some default values for this in the constructor function. In the case of notifyTime we are making use of the Moment.js library here, by creating a new “moment” using the Date object, and then calling the format method on that. This will give us a datetime string in ISO format, which reflects the time in the user’s local timezone. We need the date to be in ISO format in order to work with the <ion-datetime> input, and it’s actually quite difficult to get an ISO string in the user’s local time zone, so we use Moment to handle that for us.

Rather than just having an array of strings representing the days of the week, we make them objects with additional properties. The dayCode represents the day of the week in number format, which reflects how days of the week are represented when working with dates in Javascript. The checked property will allow us to keep track of whether the user has selected a specific day or not.

We also set up a few more functions that we will implement in a moment. The timeChange function will listen for changes to the <ion-datetime> input that we will add to the template, addNotifications will handle calculating the notification times and scheduling them, and cancelAll will simply delete all currently scheduled notifications.

We are going to come back and finish implementing these functions soon, but first, we are going to implement our template.

3. Set up the Template

Our template is reasonably straight forward, we are just going to add a <ion-datetime> component to allow the user to select a time of the day, a checkbox for each day of the week, and two buttons: one to schedule the notifications, and one to cancel them.

Modify src/pages/home/home.html to reflect the following:

<ion-header>
  <ion-navbar color="primary">
    <ion-title>
      Notifications
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

    <ion-list no-lines>

        <ion-item>
          <ion-label>Notify me at: </ion-label>
          <ion-datetime displayFormat="h:mm A" pickerFormat="h mm A" [(ngModel)]="notifyTime" (ionChange)="timeChange($event)"></ion-datetime>
        </ion-item>

        <ion-item>
            <ion-label>on the following days:</ion-label>
        </ion-item>

        <ion-item *ngFor="let day of days">
            <ion-label>{{day.title}}</ion-label>
            <ion-checkbox [(ngModel)]="day.checked" color="primary"></ion-checkbox>
        </ion-item>

    </ion-list>

</ion-content>

<ion-footer>

    <button (click)="addNotifications()" ion-button color="primary" full>Schedule</button>
    <button (click)="cancelAll()" ion-button color="danger" full>Leave me alone!</button>

</ion-footer>

The <ion-datetime> component is super powerful and flexible. We are using displayFormat to control how the input displays after a user has selected it, and pickerFormat to control what the datetime picker looks like when the user is selecting a time. The format we are using will allow a user to pick a time using the 12 hour format. There are tons of configuration options available for <ion-datetime> so I would highly recommend taking a look at the documentation.

We also hook <ion-datetime> up to our notifyTime variable with ngModel so that we can set the current time to whatever the time is right now. Usually we would use this two way data binding to then access the value the user has chosen from the TypeScript file, but instead we are going to use the (ionChange) event to keep track of changes. This allows us to grab the hours and minutes that the user has chosen specifically, which is easier for us to work with than trying to manipulate the ISO string.

The only other thing of interest here is our checkboxes. We create a checkbox for each day in our days array, and then we set up two-way data binding on the checked property. This way, if a user selects or unselects a specific day it will be reflected in our days array.

4. Implement the Functions

Now let’s work through implementing the rest of the functions in the TypeScript file, we are going to do them one at a time and talk through them.

Modify the timeChange function in src/pages/home/home.ts to reflect the following:

    timeChange(time){
        this.chosenHours = time.hour.value;
        this.chosenMinutes = time.minute.value;
    }

This function is super simple, we just grab the hour and minute values from the datetime object that is passed in.

Modify the addNotifications function in src/pages/home/home.ts to reflect the following:

    addNotifications(){

        let currentDate = new Date();
        let currentDay = currentDate.getDay(); // Sunday = 0, Monday = 1, etc.

        for(let day of this.days){

            if(day.checked){

                let firstNotificationTime = new Date();
                let dayDifference = day.dayCode - currentDay;

                if(dayDifference < 0){
                    dayDifference = dayDifference + 7; // for cases where the day is in the following week
                }

                firstNotificationTime.setHours(firstNotificationTime.getHours() + (24 * (dayDifference)));
                firstNotificationTime.setHours(this.chosenHours);
                firstNotificationTime.setMinutes(this.chosenMinutes);

                let notification = {
                    id: day.dayCode,
                    title: 'Hey!',
                    text: 'You just got notified :)',
                    at: firstNotificationTime,
                    every: 'week'
                };

                this.notifications.push(notification);

            }

        }

        console.log("Notifications to be scheduled: ", this.notifications);

        if(this.platform.is('cordova')){

            // Cancel any existing notifications
            LocalNotifications.cancelAll().then(() => {

                // Schedule the new notifications
                LocalNotifications.schedule(this.notifications);

                this.notifications = [];

                let alert = this.alertCtrl.create({
                    title: 'Notifications set',
                    buttons: ['Ok']
                });

                alert.present();

            });

        }

    }

This function contains the real meat of the application, it’s what handles actually setting up the notifications. Scheduling a notification is actually quite easier, all we have to do is create an object that looks like this:

    let notification = {
        id: day.dayCode,
        title: 'Hey!',
        text: 'You just got notified :)',
        at: firstNotificationTime,
        every: 'week'
    };

and then schedule it like this:

LocalNotifications.schedule(notification);

We can also schedule multiple notifications at once by providing an array of notification objects, instead of just a single object. The hardest part of what we are trying to do is figuring out when to schedule the notification. We need to set the at property to the date and time that we want to display the notification using the Date object, and then that notification will repeat based on whatever we set as the every property. So, the hard bit is actually calculating what that at property should be.

To do this, we first get a reference to the what the date and time are right now by creating a new Date object, and we also keep a reference to the number value of the current day. By referring to Monday as 1, and Wednesday as 3, we can very easily work out the distance between days.

Next, we loop through all of the days in our days array and check for any that have been selected. For any that have been selected, we first get a reference to the current date and time, and then we figure out how many days away from the current day the selected day is. Wednesday is two days away from Monday, but we also need to account for the case where we go into the next week, so if the resulting number is negative we add 7 to it. This way, Monday will be 5 days away from Wednesday (which is correct if we go through to the next week).

Once we have that calculated, we add 24 hours to the date time for however many days away the selected day is from the current date. Then we also need to set the hours and minutes of the day that the user wants to be notified on that specific day, so we do that as well. We then create the notification object and add it to the array.

Finally, once we have looped through all of the days and created the notifications array, we first cancel any existing notifications and then schedule the new ones.

Now let’s add the final function.

Modify the cancelAll function in src/pages/home/home.ts to reflect the following:

    cancelAll(){

        LocalNotifications.cancelAll();

        let alert = this.alertCtrl.create({
            title: 'Notifications cancelled',
            buttons: ['Ok']
        });

        alert.present();

    }

This is also a very simple function, we just call the cancelAll method of the plugin to delete any existing notifications.

The application should now be finished and ready to use! In order to test this application, you will need to run it on a device or simulator, it won’t work through the browser since it is using a Cordova plugin.

Summary

Local notifications, although not quite as powerful as push notifications, have a lot of uses. They are obviously viable in use cases like this where we are scheduling reminder type notifications up front, but you can even get a little more creative with them.

At the start of this tutorial, I said that you couldn’t use local notification to notify a user of a message sent by a different user of the app. That’s not entirely true. If you were to use a plugin that allows your app to continue running in the background when the user leaves your app but doesn’t completely close it, your app could listen for new messages coming in and then trigger a local notification from the background. That notification would then pop up alerting the user to the message even though they do not have the app open, but if the user had the app completely closed then they wouldn’t receive the notification (like they would with a push notification).

There’s also ton more you can do with the Local Notification plugin, like deleting specific notifications and checking if certain notifications are scheduled, so make sure to check out the documentation.

What to watch next...

  • Hi Josh!

    Nice tutorial. What do you can tell me about recursive notifications? For example, can I create an App to schedule a notification every monday at 8:00 am, for 5 weeks?

    How much this local notifications is reliable? I think I have set some notifications with this test app but I don’t see all of them.

    • You can certainly schedule a repeating notification with the ‘every’ property, e.g. every: ‘week’, I’m not sure if there is support for only repeating it X amount of times or not, or whether you would have to handle that logic when the user is inside your app.

      As far as reliability goes, I’ve never had any issues with scheduled notifications not appearing.

      • Hi Josh, thanks for the reply.

        I read the plugin docs and figured what happens: if I set a notification with the same id with, the last notification will override the first one. So, if I set 3 notifications with different times, but with the same id, in the end I have only one notification (the last one).

        About the repeating notification, I think I can cancel the notification if the user access the App after the end of iterations.

        (I have an idea for an App and I had delayed it because I thought need a server for push notification, but local notifications serves me well).

  • meetdilip

    Hi Josh, is it possible to get a notification when using an RSS feed app ? Like, you have a new RSS feed entry ( ie, unread message ) and the app gives a local notification.

  • Gengjun Wu

    Hi Josh,

    Nice tutorial! However, I don’t know why I cannot get notification, I even downloaded your complete code and tried with android studio on emulator. No notification at all. When I click the Schedule button, it is supposed to alert me with notifications set. I do not get anything, perhaps, the promise did not run successfully, so that the then function does not execute?

    • Daniel Archer

      me too. did you ever fix it?

    • Richelle Chia

      Same here. I have followed the tutorial and tried to display on an android device. However, I didn’t managed to received the notification.

      • Gengjun Wu

        Hey there, I managed to get it work somehow on Android Studio Simulator. I did not know the reason why it did not work at the beginning. Try follow the tutorial here and correctly setup your simulator should fix.

      • Richelle Chia

        I have managed to make it worked too but if i scheduled it to 3:00pm, it actually notified me at 3:00:55pm (which delayed for 55s)

  • Richelle Chia

    Hi Josh,
    I have managed to make it worked it out but if i scheduled it to 3:00pm, it actually notified me at 3:00:55pm (which delayed for 55s)

  • Devep Diska

    Hi Josh,

    I am trying to set a multi line text in the text attribute of the notification object, but n doesn’t work. May be this feature is not available yet?

  • Jonathan David Espitia Rivera

    hola de casualidad has echo notificaciones con ionic 2 – inoc cloud y push notifications

  • Nasser Alminji

    I belive these two should be in this order
    firstNotificationTime.setHours(this.chosenHours);
    firstNotificationTime.setHours(firstNotificationTime.getHours() + (24 * (dayDifference)));

  • Dhaval Shah

    Hi Joshua, what happens when there is a notification is sitting in tray which was sent via push notification mechanism and there is a local notification present. Is the onclick of either item result in the same handler to be called?

  • Gottsy

    Thanks for the tutorial…

    I need to use an event listener…but I cant find a logical place to initialize it (and have it work!)
    When its on the page it works…but that means its only going to listen after the page has been loaded, and also seems like multiple event listeners are initialized each time the page is loaded in the app.
    Makes sense to me to put it in a service – which is where I am creating my notifications – but when i put it in the service the event listener is never called???

    Any idea?

  • hima

    On execution I am getting error as follows:-
    Typescript Error:

    Property ‘cancelAll’ does not exist on type ‘typeof LocalNotifications’
    Property ‘schedule’ does not exist on’typeof
    LocalNotifications’
    Is this a version conflict.Can anyone guide me??

    • Julio C

      Hi, I have the same error.

      But reading pluging documentation (http://ionicframework.com/docs/v2/native/local-notifications/) I run following command after install plugin ($ ionic plugin add de.appplant.cordova.plugin.local-notification) :

      $ npm install –save @ionic-native/local-notifications

      The output from last command was:

      ionic-hello-world@ /Users/julio/ionic2-local-notifications
      ├── @[email protected]
      └── UNMET PEER DEPENDENCY [email protected]

      npm WARN @[email protected] requires a peer of typescript@~2.2.1 but none was installed.

      Also change this line:

      import { LocalNotifications } from ‘ionic-native’;

      by this other one:

      import { LocalNotifications } from ‘@ionic-native/local-notifications’;

      But I still having the same error than Hima.

      Any idea to solve it!

    • Raphael Arthur

      Hi, I am with the same error, you get the solution?

      • Dennis Muijs

        Hey Raphael, check my reply to Hima

    • Dennis Muijs

      Hi Hima, the error is resolved by adding ‘private LocalNotifications: LocalNotifications’ to the constructor and then calling the functions by prefixing them with this. eg: this.LocalNotifications.cancelAll()

      • hima

        Thank You Dennis..I haven’t tried with this..

  • Sachin Devinagar

    Nice tutorial…
    I need to add action buttons to a notification. is it possible?

  • Fernando Gomes

    Hi Josh

    Excelent tutorial! I’m having a problem running it thow, Trying to run it in the Android simulator or on a real device I only get a white page, no content nor notifications. Do you have any idea why this could happen? The device is a Sony Xperia with Android 5.0.

    Best regards

    Fernando

    • Dennis Muijs

      Hey Fernando, I expect you didn’t add the npm package.
      npm install @ionic-native/local-notifications –save
      then in the app.module.ts: add LocalNotifications to providers add the import: import { LocalNotifications } from “@ionic-native/local-notifications”; at the top
      add that same import at the top od the home.ts file, replacing the original import

  • Dennis Muijs

    Hey Josh,
    Good tutorial! unfortunately in the three months that passed it became a bit outdated.
    To make it work again on android and ios the following changes are required:

    – When installing the plugin, also run: npm install @ionic-native/local-notifications

    in app.module.ts
    – Add an import at the top: import { LocalNotifications } from “@ionic-native/local-notifications”;
    – Add LocalNotifications to the providers array

    in home.ts
    – Replace import { LocalNotifications } from ‘ionic-native’; with import {LocalNotifications} from “@ionic-native/local-notifications”;
    – Add private LocalNotifications: LocalNotifications to the constructor
    – Change ionViewDidLoad to look like this to let notifications work on ios 10:
    ionViewDidLoad(){
    this.platform.ready().then(() => {
    console.log(“—————–in view did load——————-“);
    this.LocalNotifications.hasPermission().then(function(granted) {
    if (!granted) {
    this.localNotifications.registerPermission();
    }
    });
    });
    }
    – add this. before every call to LocalNotifications eg: this.LocalNotifications.cancelAll()

    this works for me on april 3rd 2017

    • Darlington Uzoma

      I tried this, I get this error.. Uncaught Error: Cannot find module “@ionic-native/core”

  • Ivan

    Hi Josh.

    Great tutorials in general, thank you!
    I have question regarding push notifications. How would you trigger notifications from other device, for example in chat application when user receives message with Meteor as “backend” and MongoDB? How would you connect push notification provider like OneSignal with some event in database?
    Thanks once again.

  • Krissh Mehra

    @dennismuijs:disqus Hi Dennis,
    I applied the changes as you mentioned and tried to get notification but not able to received the notification. It’s also not showing any error when inspect. FYI I am executing this on android 6.0

  • millenseed

    Hi Josh,

    does this work on iOS as well, as it is? Or does it need the permission check?

  • Ian Marsh

    does anyone have this working on the latest iOS release?? I’m not having much luck

  • sergi

    Hello Josh, It’s a good tutorial. I implement code like you and when i schedule a notification it sends at once, it doesn’t wait for scheduled time and what can be a problem?