Table of Contents
In this five-part tutorial we'll see how we can create a sample React Native app (you might call it a "Hello World" or "boilerplate") that will be capable of receiving Push Notifications using Firebase: this can be a great headstart for React Native beginners, yet also for those intermediate or experienced developers who want to know more about push notifications: how they work, what are their best usage scenarios and how much they can be an improvement for almost any mobile app.
The entire source code shown throughout this tutorial, as well as a working sample app built with it, is available on GitHub under MIT license: if you happen to like it, don't forget to give it a star (and a like to this website)!
For the sake of simplicity this tutorial has been splitted into multiple parts, each one dedicated to a specific sub-topic:
- Part 1 of 5: Understanding Push Notifications, in which we'll try to understand what push notifications actually are and the impact they could have (when used properly) on a typical client-server app.
- Part 2 of 5: Setup React Native and Firebase, where we'll deal with the installation and configuration of our whole development stack: React Native, the Firebase SDK and the Visual Studio Code environment, together with the required settings to get our app ready to work with Android and iOS.
- Part 3 of 5: Configure the Firebase Service APIs, in which we'll create the Firebase Project and configure the Firebase Push Notification Services to ensure that our React Native app will be able to receive push notifications through the Firebase API on any Android and iOS device.
- Part 4 of 5: Design and Test the React Native UI, where we'll test our front-end skills by laying out a React-Native UI in order to make our app actually able to receive push notifications and send HTTP requests to the server to remotely keep track of our actions - and possibly fire subsequent push notifications to other clients.
- Part 5 of 5: Server-Side Handler, in which we'll use ASP.NET Core to create a sample, lightweight web service that will be able to receive HTTP requests issued by the clients and/or feed them with push notifications through the Firebase API.
In this second post we'll see how to setup and configure React Native, Firebase and Visual Studio Code.
Before we start
Before digging into the coding, it's worth spending a few words about the tools we'll be dealing with.
React Native is an open-source mobile application framework created by Facebook and widely used to develop applications for Android, iOS and UWP by enabling developers to use the React JavaScript library along with native platform capabilities. If you've missed my previous posts about React Native i strongly suggest to gave them a quick look before continuing:
- Should I use React Native in 2019?
- How to install React Native on Windows
- Hello World sample app with React Native and Visual Studio Code
Firebase is a mobile and web application development platform developed by Firebase, Inc. in 2011, then acquired by Google in 2014. As of October 2018, the Firebase platform has 18 products, which are used by 1.5 million apps. In a nutshell, it's a SaaS platform that operates together with a multi-plaform library set which can be used to build services to make your code to interact with web and mobile apps in various ways and in a seamless fashion. For additional info, visit the Firebase website.
Installing React Native
If you don't have React Native on your system yet, you can easily install it using our React Native Windows installation guide (be sure to do that using the Option #1 - Use React Native CLI) or the official guide, which also contains instruction for non-Windows environments such as Linux, MacOS and so on. Within the following paragraphs we'll take for granted that you have React Native ready and properly set up within your system PATH.
Installing the Packages
The next thing to do is to install the Firebase packages, as well as the other packages required for push notifications to properly work. That's something we can easily do using the built-in package.json file, which can be found in the project root, and Yarn, a great package manager that is quickly replacing NPM because of its greater stability, ease of use and speed (if you want to read a comparison between NPM and Yarn, read this post).
Here's the package.json content we'll be using for our boilerplate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
{ "name": "react_native_firebase_notifications", "version": "0.0.1", "private": true, "scripts": { "start": "react-native start", "test": "jest", "postinstall": "npx jetify" }, "dependencies": { "react": "16.8.6", "react-native": "0.60.3", "react-native-firebase": "5.5.5", "react-navigation": "3.11.0", "react-native-gesture-handler": "1.3.0", "jetifier": "1.6.2" }, "devDependencies": { "@babel/core": "^7.5.0", "@babel/runtime": "^7.5.0", "@react-native-community/eslint-config": "^0.0.3", "babel-jest": "^24.1.0", "jest": "^24.1.0", "metro-react-native-babel-preset": "^0.54.1", "react-test-renderer": "16.8.6" }, "jest": { "preset": "react-native" } } |
As we can see, we're going to use React Native v0.60.3 (the latest at the time of writing) and Firebase 5.5.5.
Dealing with AndroidX
Notice that, starting from 0.60.0, React-Native is supporting the AndroidX libraries by default - as opposed at the Android API 28 libraries which has been the default since version 0.59.10. This basically means that we'll have to deal with some minor issues regarding the Android API 28 references still present in the .java native code present within some NPM packages, including Firebase v5.x. To deal with such issues we've installed and configured the jetifier package, an AndroidX transition tool which will basically convert the API28 native code references into AndroidX ones in any NPM module present in our project.
In order to make it automatically do that job, we added the npx jetify command to the package.json 's postinstall, so that it will be executed right after any yarn install ... which is precisely what we need to do, in order to download the node_modules and go ahead!
Installing the NPM modules
After having properly configured the package.json file, navigate through the root folder of your React Native app and issue the following command:
1 |
> yarn install |
If you're using Visual Studio Code, like I strongly suggest you to, you can also to that from the GUI using the terminal tab or - even better - the Yarn VS Code extension by Gamunu Balagalla, which lets you install/update packages by right-clicking on the package.json file.
Configuring the Firebase Modules
The next thing to do is to modify some relevant configuration files to be able to deal with the Firebase Modules required for receiving Push Notifications:
- firebase-core
- firebase-messaging
- firebase-notifications
We'll need to do this for both the Android and the iOS builds of our React Native app.
Android
Let's start with the Android native app. The affected file would be the following:
- /android/build.gradle
- /android/app/build.gradle
- /android/app/src/main/AndroidManifest.xml
- /android/app/src/main/res/values/strings.xml
- /android/app/src/main/java/com/<your_app_name>/MainApplication.java
Let's deal with them one by one.
build.gradle (project)
The first file to modify is the build.gradle present in the /android/ folder root - not to be confused with the one having the same name in the /android/app/ folder root (we'll deal with it later on).
Open that file, then find this part:
1 2 3 |
dependencies { classpath("com.android.tools.build:gradle:3.4.1") } |
and replace it with the following content, adding the following single line:
1 2 3 4 |
dependencies { classpath("com.android.tools.build:gradle:3.4.1") classpath 'com.google.gms:google-services:4.2.0' } |
build.gradle (app)
Let's now deal with the other build.gradle file, the one present in the /android/app/ folder root.
Open it, then find this part:
1 2 3 |
dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules |
and replace it with the following content:
1 2 3 4 5 6 7 8 |
dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules implementation "com.google.android.gms:play-services-base:17.0.0" implementation "com.google.android.gms:play-services-gcm:17.0.0" implementation 'com.google.firebase:firebase-core:17.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.1' |
The versions of the mentioned packages are the latest at the time of writing (July 2019). However, you can check for their latest versions here:
Right after that, go to the end of the file and add the following line after everything else:
1 |
apply plugin: 'com.google.gms.google-services' |
AndroidManifest.xml
Once done, open the /android/app/src/main/AndroidManifest.xml file.
Look for this part:
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
and replace it with the following content:
1 2 3 4 |
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.VIBRATE" /> |
Further down on the same file, find this part:
1 2 3 4 5 |
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:windowSoftInputMode="adjustResize"> |
and replace it with this:
1 2 3 4 5 6 |
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTop"> |
Finally, find the following line:
1 |
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> |
and add the following lines below it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@mipmap/ic_launcher_round" /> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id" /> <service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <service android:name="io.invertase.firebase.messaging.RNFirebaseInstanceIdService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service> <service android:name="io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" /> <service android:name=".MyFcmListenerService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <receiver android:name="io.invertase.firebase.notifications.RNFirebaseNotificationReceiver"/> <receiver android:enabled="true" android:exported="true" android:name="io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> |
strings.xml
Since we added a new @string/default_notification_channel_id variable in the AndroidManifest.xml file, we need to properly define it in the strings file.
Open the /android/app/src/main/res/values/strings.xml file and add the following string:
1 |
<string name="default_notification_channel_id" translatable="false">fcm_default_channel</string> |
MainApplication.java
We're almost done! Open the /android/app/src/main/java/com/<your_app_name>/MainApplication.java, locate the following method:
1 2 3 4 5 6 7 8 |
@Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); return packages; } |
and add the relevant Firebase Android packages to the existing ones it in the following way:
1 2 3 4 5 6 7 8 9 10 |
@Override protected List<ReactPackage> getPackages() { //@SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); packages.add(new RNFirebaseMessagingPackage()); packages.add(new RNFirebaseNotificationsPackage()); return packages; } |
Before closing the file, don't forget to add the references to the Firebase native libraries at the top of the file, just below the other import statements:
1 2 3 |
import io.invertase.firebase.RNFirebasePackage; import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage; |
Right after that, we just need to configure the Firebase Service to ensure that our React Native application will be able to receive push notifications through the Firebase API.
iOS
Let's now switch to the iOS native app.
Auth Key, Certificates and Provisioning Profile
The first thing we need to understand while switching to iOS is that the Firebase Cloud Messaging APNs interface uses the Apple Push Notification service (APNs) to send messages. In order to use that service, we need the following stuff:
- An Apple Push Notification Authentication Key for your Apple Developer account. Firebase Cloud Messaging will use this token to send Push Notifications to the application identified by the App ID.
- A provisioning profile for that App ID.
Both of them can be created within the Apple Developer Member Center: in case you need some guidance(I bet you will!), you can massively ease your overall experience with one of the worst configuration nightmares I've ever seen by following the steps explained in this great guide from the Firebase website.
As soon as you obtain the key and the provisioning profile, you'll have to modify the following files:
- ios/Podfile
- ios/<your app name>/AppDelegate.m
The former to add the required Firebase dependencies, the latter to implement those classes within our iOS native app source code.
Podfile
The first thing to do is to add the Firebase SDKs to our app and install the Firebase libraries we need. The recommended way to do that is using CocoaPods, a widely-used dependency manager that can automate the installation of projects, modules and SDKs. Luckily enough, since we created our sample app using the react-native init command, our iOS project already has a Podfile installed - we just need to setup and install the pods!
Open the /ios/Podfile file and add the following modules:
1 2 |
pod 'Firebase/Core' pod 'Firebase/Messaging' |
Once done, open a terminal, navigate to that same folder and run pod install to install the pods. Doing this will add the prerequisite libraries needed to get Firebase up and running in your iOS app, along with push notifications support.
If CocoaPods is not installed on your Mac, install it by typing sudo gem install cocoapods on a terminal window.
Alternative method (no Cocoapods)
Alternatively, in case you don't want to use Cocoapods, you can still use the Firebase SDKs by importing the frameworks directly. To do this, follow these steps:
- Download the Firebase SDK for iOS (~1GB zip file - it could take a while).
- Unzip the file, then read the README for full instructions about setting up the Firebase APIs that you want to include in your app.
- Add the ObjC linker flag in the Other Linker Settings in your target's build settings.
AppDelegate.m
Now it's time to implement the Firebase classes within the "app-native" steps. Open the ios/<your app name>/AppDelegate.m file and add the following lines at the very top of it:
1 2 3 4 |
#import <Firebase.h> #import <UserNotifications/UserNotifications.h> #import "RNFirebaseNotifications.h" #import "RNFirebaseMessaging.h" |
Once done, locate the didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method and add the following lines at the beginning of it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[FIRApp configure]; [RNFirebaseNotifications configure]; [FIRMessaging messaging].delegate = self; if ([UNUserNotificationCenter class] != nil) { // iOS 10 or later // For iOS 10 display notification (sent via APNS) [UNUserNotificationCenter currentNotificationCenter].delegate = self; UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { // ... }]; } else { // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications. UIUserNotificationType allNotificationTypes = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; [application registerUserNotificationSettings:settings]; } [application registerForRemoteNotifications]; [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result, NSError * _Nullable error) { if (error != nil) { NSLog(@"Error fetching remote instance ID: %@", error); } else { NSLog(@"Remote instance ID token: %@", result.token); NSString* message = [NSString stringWithFormat:@"Remote InstanceID token: %@", result.token]; // self.instanceIDTokenMessage.text = message; } }]; |
The above code will register the iOS native app for push notifications and set the AppDelegate class as the delegate to handle the push notifications events. In order to properly catch those events, we also need to implement the following set of pre-defined methods:
- didReceiveRegistrationToken
- didReceiveLocalNotification
- didReceiveRemoteNotification
- didRegisterUserNotificationSettings
To do that, add the following lines of code right below the didFinishLaunchingWithOptions method (after the closing parenthesis):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken { NSLog(@"FCM registration token: %@", fcmToken); // Notify about received token. NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"]; [[NSNotificationCenter defaultCenter] postNotificationName: @"FCMToken" object:nil userInfo:dataDict]; // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{ [[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings]; } |
XCode
Last but not least, we have to add the following capabilities to our iOS native app using XCode:
- Push Notifications
- Background modes > Remote notifications
To add them, launch XCode, select your app root folder from the left-side file navigation panel, then navigate to Target > Capabilities: once you're there, scroll down until you find the capabilities you're looking for.
Team ID, Provisioning Profile and APNs Authentication issues
If you're doing this for your first time, there's a high chance that XCode will refuse to set the Push Notifications capability to on, asking you for a valid Team ID, a Provisioning Profile with Push Notifications enabled and - last but not least - a valid Authorization Key and/or a Signing Certificate to authenticate with the Apple Push Notifications Services (APNs). If you run into these warnings, you'll need to A) provide yourself with a valid Apple Developer subscription, and B) create all this stuff within the Apple Developer website by following this guide.
Conclusion
That's it! Now we need to navigate through the Google Firebase website and configure the Firebase Service.
Part 3 of 5: Configure the Firebase Service
when 3rd part is coming?
Part 3 is out!
https://www.ryadel.com/en/react-native-push-notifications-setup-firebase-3/
Part 4 and 5 will come up this weekend as well.
Part 4 is out as well, as well as the GitHub project containing a working app developed using this tutorial parts 1-2-3-4.
I will eventually release Part 5 as well: however, part 4 is more than enough to perform a full push notification roundtrip, therefore there is no urgency anymore. :)
Where are your other parts?
Part 3 is out! (see my answer on previous comment)
Part 4 is out as well, as well as the GitHub project containing a working app developed using this tutorial parts 1-2-3-4.
I will eventually release Part 5 as well: however, part 4 is more than enough to perform a full push notification roundtrip, therefore there is no urgency anymore. :)
Thanks for the first two chapters. I wonder when the new episodes will arrive.
Part 3 is out! (see my answer on previous comment)
Part 4 is out as well, as well as the GitHub project containing a working app developed using this tutorial parts 1-2-3-4.
I will eventually release Part 5 as well: however, part 4 is more than enough to perform a full push notification roundtrip, therefore there is no urgency anymore. :)
Hey what about the other chapters?
Part 3 is out! (see my answer on previous comment)
Part 4 is out as well, as well as the GitHub project containing a working app developed using this tutorial parts 1-2-3-4.
I will eventually release Part 5 as well: however, part 4 is more than enough to perform a full push notification roundtrip, therefore there is no urgency anymore. :)
The part in the AnroidManifest did not work with my application. I can not find it either in the docs, is it only for your application or is it an overall implementation?
Best regards
Hello,
it’s more or less a general purpose implementation.
It might slightly vary, depending if you plan to use scheduled notifications / headless notifications or not (I assumed YES).
Here are the relevant references from the official Firebase docs:
https://rnfirebase.io/docs/v5.x.x/notifications/android
Dear. you saved my life . I was searching for this kind of articles from months. I have tried almost twelve article but failed to implement Push notification.
Your article is one of the best gift i have got from internet and its because of you .
Thumbs up and keep it . I can help you with creating ASp.net core server side script for Push Notification.
Thanks a lot! If you like our contents, don’t forget to like us on Facebook and Twitter, it will be of great help for our community :)
Thanks for sharing the useful information with us! Keep us doing the good work.