06/20/2020
I want to document how I got custom push notification sounds working on iOS and Android with the React Native Firebase library and Firebase Cloud Messaging (FCM).
The process is not difficult once you understand it. It's just hard to find a complete set of instructions on how to do it. I hope this will save you time when setting it up in your project.
First step is an obvious one. Make sure you install React Native Firebase core library and the Cloud Messaging modules. Be sure to follow all instructions for each platform carefully.
Apple supports .wav
.aiff
or .caf
files. Android supports .mp3
, .ogg
or .wav
.
Since our back-end server used the same push body for both platforms, it was easier to use a format that both platforms could share, so I chose .wav
.
your_project_root/ios
and place the sound in there. This doesn't yet add it to the project, it just places the sound in a logical spot.Setup on Android is a little more complex than iOS. As of Android API level 26, notifications need to be set to a notification channel in order for them to work at all.
The channel defines the sound to use, vibration pattern, and other things about your push notifications. React Native Firebase creates its own channel internally to help provide a basic push functionality with their Cloud Messaging library, but if you want a custom sound or other customization, this channel is not accessible to you to edit. You must create your own custom notification channel. On native Android, this is Java code that executes at runtime, it's not done on a configuration file.
React Native Firebase does not provide an API to this native API for creating a channel. It used to provide it on v5, but since v6 it has stopped supporting it because it was out of scope of Firebase.
Luckily it's pretty simple to add you own without the need of a custom library to do it. You simply need to update your MainActivity.java
file in your project so a notification channel is created and configured.
Before you can create your notification channel. You must do the following:
It can be anything, and you will use it to target your custom channel from your push message, so that the custom sound you will tie to your channel plays.
I should note that after installing the app the channel name will remain registered until the app is uninstalled. So keep that in mind if you change channel names later.
Drag your .wav
sound into the your_project_root/android/app/src/main/res/raw
folder (if it doesn't exist, create it).
The code below must be placed inside the onCreate
method of your MainActivity
class. Fill in the text surrounded in brackets with your own, and customize anything else you need on your notification:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("{ENTER THE NOTIFICATION NAME HERE}", "{ENTER APP NAME HERE}", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setShowBadge(true);
notificationChannel.setDescription("");
AudioAttributes att = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
notificationChannel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/raw/{ENTER SOUND NAME HERE WITHOUT EXTENSION}"), att);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{400, 400});
notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
Here's a code example with some values filled in:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("new_email_arrived_channel", "My Emailer", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setShowBadge(true);
notificationChannel.setDescription("");
AudioAttributes att = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
notificationChannel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/raw/my_custom_sound"), att);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{400, 400});
notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
Notice my_custom_sound
is not written with the extension (.wav
). You just need to add the file name.
With the sound in place you can now test your implementation. Before you can test it you will need to fish out Firebase's FCM token from your app. This token is used by FCM to route the push notification to the correct device.
Firebase lets you retreive the FCM token using the messaging().getToken()
function. Use this function to log the FCM token to your console:
const _getToken = async () => {
try {
const token = await messaging().getToken();
console.log('FCM token registered:', token);
...
} catch (error) {
console.error('Error getting FCM token:', error);
}
};
Do this for both Android and iOS devices. It will create a different token for both.
We will use this token to test sending push notifications directly to our test devices.
I will show how to test with the FCM REST API because it will give you all the flexibility you need to edit the push body as you would need to set it on your server.
Follow the steps below to get testing.
Postman is a great API testing application for your desktop computer and will make setting up the FCM REST API very easy. Download and install the Postman app.
Before we configure Postman we need that our API push requests are authenticated against our project. For this we need to authenticate with our server key. To find it:
https://fcm.googleapis.com/fcm/send
Now that your request is configured, let's add the push body. This is done in the body tab in the empty area under the data type options. Here is an example body:
{
"to": "<FCM TOKEN>",
"notification": {
"title": "Some title",
"body": "Some body",
"sound": "my_custom_sound.wav",
"android_channel_id": "new_email_arrived_channel"
},
"data": {
"field1": "value1",
"field2": "value2"
},
"content_available": true,
"priority": "high"
}
The key items here to get your sound to work is the "to", "sound", and "android_channel_id" fields.
to field
Here you will need to add your device's FCM token in quotes
sound field
Your sound field must set the sound name including the extension.
android_channel_id field
The "android_channel_id" field must have the name of your custom Android push channel you created earlier. These names must match or else your channel won't be found and your device will simply play a default sound.
NOTE: You may find some forums out on the internet saying the field name should be "channel_id" and not "android_channel_id". This is half true. They are correct that the REAL name for the push field is "channel_id" on Android. But remember, we are using FCM and FCM handles both iOS and Android. We are not making a request to Google's push server directly.
So for FCM, the field name is "android_channel_id", but once the push is delivered to the Android device this field comes in as "channel_id". FCM translates the field name correctly for us once its delivered.
Hit the send button on Postman, you should receive the push with custom sound.
If you get any errors on the API call, make sure you resolve those
If you are having trouble getting the sound to play or the push doesn't come through here are some suggestions you can try to help you troubleshoot.
MainActivity.java
does NOT have the file extension on itHope this helps you in your push journey. It's strangely a confusing and complex feature to set up.
package io.myapp;
import com.facebook.react.ReactActivity;
import org.devio.rn.splashscreen.SplashScreen;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import androidx.core.app.NotificationCompat;
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("myapp_notification", "MyApp", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setShowBadge(true);
notificationChannel.setDescription("");
AudioAttributes att = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
notificationChannel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/raw/myapp"), att);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{400, 1000, 400});
notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
SplashScreen.show(this);
super.onCreate(savedInstanceState);
}
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "MyApp";
}
}