Calendar Events and reminders with React Native

I recently was working on a Hospital Management System where I was responsible for building the frontend of both the web app and cross-platform mobile applications. On the mobile applications, I was required to implement a reminder to help remind patients to take their prescriptions at certain intervals, and also help with appointments. I thought of numerous ways of implementing this feature — such as using background events with push notifications to notify the user at certain periods, but the most efficient way I came up with was to make the devices’ inbuilt calendars do all the heavy lifting.

This guide will leverage the features provided by the following module:
* react-native-calendar-events
Other libraries used in this guide include lodash and moment

Setup

Kindly follow the instructions here to add the package to your react native project.

Implementation

Create a file in any suitable directory within your project, this file would be referred to as ReminderService.js
In this file, we’ll implement all the features required to manage calendar events.
— Create a class with the following construction like so…

import {Platform} from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events';
import moment from 'moment';
const _ = require('lodash');
const MY_CALENDAR = 'MY_CALENDAR';
class ReminderService {
constructor() {
this.hasAccess = false;
this.calendarId = null;
}
}

Next, within the constructor, we will request the device’s permission to use the calendar and also create our custom calendar if it doesn’t already exist.

Genereally, most smartphones are capable of having multiple calendar “instances” running on the device. We will be implementing these feature using a “custom calendar instance” on the device.

import {Platform} from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events';
import moment from 'moment';
const _ = require('lodash');
const MY_CALENDAR = 'CUSTOM CALENDAR';
class ReminderService {
constructor() {
this.hasAccess = false;
this.calendarId = null;
RNCalendarEvents.requestPermissions(false)
.then((fulfiled) => {
if (fulfiled == 'authorized' || fulfiled == 'undetermined') {
this.hasAccess = true;
// check if device has custom calendar
this.getCalendar(MY_CALENDAR)
.then(async ({calendar, googleCal}) => {
if (!calendar) {
// create custom calendar if it doesn't exist
var calendar = {
title: MY_CALENDAR,
color: '#666',
entityType: 'event',
};
if (Platform.OS == 'android') {
calendar = {
...calendar,
name: 'Custom Events',
accessLevel: 'owner',
ownerAccount: googleCal ? googleCal.source : 'default',
source: {
name: googleCal ? googleCal.source : 'App Name',
type: "com.package_name"
},
};
}
RNCalendarEvents.saveCalendar(calendar)
.then((calendar) => {
this.calendarId = calendar;
console.log('Calendar created', calendar);
})
.catch((error) => {
console.error(error);
});
} else {
this.calendarId = calendar.id;
}
})
.catch((error) => {
console.error(error);
});
}
})
.catch((error) => {
console.error(error);
});
}

In the block above, we made a call to a function getCalendar(), this function depends on a second function getCalendars() both functions will be created below.

import {Platform} from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events';
import moment from 'moment';
const _ = require('lodash');
const MY_CALENDAR = 'MY_CALENDAR';
class ReminderService {
constructor() {
...
}
getCalendar(name) {
return new Promise((resolve, reject) => {
let googleCal = null;
this.getCalendars()
.then(async (calendars) => {
let calendar = await _.find(calendars, function (cal) {
return cal.title == name;
});
// get google calendar account on android
if (Platform.OS == 'android') {
googleCal = _.find(calendars, function (cal) {
return cal.type == 'com.google';
});
}
resolve({calendar, googleCal});
})
.catch((error) => {
reject(error);
});
});
}
getCalendars() {
return new Promise((resolve, reject) => {
RNCalendarEvents.findCalendars()
.then((calendars) => {
resolve(calendars);
})
.catch((error) => {
reject(error);
});
});
}
...
}

The getCalendars() function calls the findCalendars() method in RNCalendarEvents class and this is a promise that resolves an array of all calendar instances on the device. We will then take advantage of that response in the getCalendar() function to return a calendar instance of our choice.

After that, we’ll create a function to handle creating events. Within the ReminderService class, create a function addEvent() like so:

addEvent(data) {
return new Promise((resolve, reject) => {
try {
this.getCalendar(MY_CALENDAR)
.then(async ({calendar}) => {
if (!calendar)
throw Error(`${MY_CALENDAR} does not exist`);
const {title, note, startDate, endDate} = data;
RNCalendarEvents.saveEvent(title, {
title: title,
notes: note,
description: note,
startDate: startDate,
endDate: endDate,
calendarId: calendar.id,
event: true,
alarms: [{date: startDate}, {date: endDate}],
})
.then((event) => {
console.log('Event created', event);
resolve(event);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
reject(error);
});
} catch (error) {
reject(error);
}
});
}

Now that we can successfully create events on our custom calendar instance, we can proceed to fetch those events from the custom instance within our application. To do this, we create another function getEvents() as shown below.

getEvents(startDate, endDate) {
return new Promise((resolve, reject) => {
this.getCalendar(MY_CALENDAR).then(({calendar}) => {
RNCalendarEvents.fetchAllEvents(startDate, endDate, [calendar.id]).then(events => {
resolve(_.orderBy(events, function(event){ return moment(event.startDate)}))
}).catch(error => {
reject(error);
});
}).catch(error => {
reject(error);
})
});
}

I added a little modification to the response, to help order the object. By default, RNCalendarEvents returns an unordered array of events with the last created event as the last entry in the array. In real-life applications, these events would be best presented in an ordered format preferrably in a chronological form; hence the line

resolve(_.orderBy(events, function(event){ return moment(event.startDate)}))

The final outcome of the ReminderService file is shown below.

Usage

In the module or component in which you intend to manage the calendar events, kindly add the following snippet.

import ReminderService from './ReminderService';...const Reminder = new ReminderService();
Reminder.addReminder({
title: 'Event subject',
note: 'some details about the event here',
startDate: '04/10/2021 10:00',
endDate: '10/10/2021 14:30',
})
.then((reminder) => {
// do something here
})
.catch((error) => {
// handle errror here
});
...

Conclusion

An advantage of implementing the reminder service using this approach as opposed to other methods is that you will not be concerned with handling background app processes and push notifications when the app is not running. Also, this is really a cool way to implement reminders because you indirectly take advantage of current cloud services such as iCloud and Google’s services to synchronize the events created from your app, this implies that the user would not only be notified on the device which your app lives but on all of their synced devices too! how cool is that?

I hope this guide was helpful.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Efezino Ukpowe

Efezino Ukpowe

5 Followers

I'm a full stack Javascript developer who is deeply in love with react. I have been into programming professionally for 5 years.