Expo + Firebase Push Notifications

Expo + Firebase Push Notifications

Here we are with another text, this time related to Push Notifications, and integrating the Firebase Push Notifications inside Expo apps. This text or guide is for those that want to avoid using Expo Services for push notifications, and use the Firebase instead.

Before we start implementing the Firebase messaging inside our Expo app, there are few things you need to be aware of:

  • you wont be able to use Expo Go app when working locally to develop your app, and you need to switch to developer builds instead, which means you need to prebuild your app (Expo Go vs Development builds explained)
  • you will need paid apple developer account where you need to generate some Push Notifications related things
  • you need a firebase account

iOS setup

First lest start with iOS. As we have mentioned above, before we start, we need to go to to the Apple Developer Account, inside the Keys section, and click on the "+" button to create a new Key, and there enter they Key Name (it doesn't matter what you type) and check the "Apple Push Notifications service (APNs)", then click on the Continue, and then Register.

In the next screen you will be able to see the Key ID, which you will need to put inside the Firebase iOS app, and there is Download button, which will download the p8 certificate, which you will also need to put inside the iOS Firebase App, so its best to open a Folder on the Desktop or inside your project, with a text file, where you will put all the needed things, like the Key ID, and put the p8 file in the same folder, because we will need it for later (if you forgot to copy the Key ID or download the file, you can go back in the keys section click on the Key we have just created and you can see the Key ID and download the p8 file from there).

Next thing is to enable the Push Notifications Capability to our bundle identifier, assuming that we have already created one, so go in the Identifiers section inside the Apple Developer Account, select our identifier, then scroll a bit till the end inside the Capabilities list, and check the Push Notifications (you don't need to click on Configure and create Certificates from there).

Now since we have updated our identifier capabilities, we need to regenerate our provision profile, so go in the Profiles section, and create a new Provision Profile for our bundle identifier (assuming that you use manual signing).

You will also need the Apple Team Id for the Firebase iOS app, and you can find this info in the top right corner, or just open the link, and inside the Membership Overview, scroll until you find the Membership details, and you will see your Team ID there, so copy it inside the text document where we keep the Key ID.

Firebase iOS App

With this we are done with the Apple stuff, now lets focus on the Firebase.

Go to the Project Overview inside the Firebase Console, and there should be "Add app" button on the top of the page, from where you can select "Apple" (before creating an app, you need to create a Project inside the Firebase Console).

On the next screen just add the bundle identifier and optionally put an app name (you don't have to put the App Store ID).

After this you need to click on the gear icon next to the "Project Overview" and select "Project Settings", and there select the "Cloud Messaging" tab.

From here, select the ios app you have just created, and click on the Upload button next to the "No APNs auth key"

And here you need to upload the p8 file, and put the Key ID you have created inside the Apple Developer inside the Key ID field, and the Team Id inside the Team ID field, and click the Upload button.

Now go inside the General tab, scroll down until you see your apps, select the ios app we have just created and configured, and click on the "GoogleService-Info.plist" file do download it, and put this file inside the root of your project (at the same level with Expos app.json).

Android setup

For Android the setup should be easier, since the only thing you need it the android app identifier, or the package name as it is called, so I will assume you already have that one, and we will continue straight to the Firebase console, where same like with the ios app, we will create a new app, but will select "Android" this time, and you need to fill the Android package name, and put some app name, and leave the rest empty. At the end of this process, download the "google-services.json" file, or if you have miss it, go inside the Project Settings, then inside the General tab, under "Your apps" section, select the android app we have just created, and click on the "google-services.json".

Same like with the plist file, put this file inside the root of the project.

Expo setup

We have finished with the Firebase and iOS and Android specific setups, now we need to configure our expo app. So to do this, you will need to open the app.json file.

First we need to put the plist and json files we have downloaded from firebase inside the app.json. Lest start with the plist file, and you need to put it under expo.ios.googleServicesFile

{
  "name": "myApp",
  "displayName": "myApp",
  "expo": {
    "name": "myApp",
    "slug": "myApp",
    ...
    "ios": {
     ...
      "googleServicesFile": "./GoogleService-Info.plist",
     ...        

and similarly we put the google-services.json, under expo.android.googleServicesFile

{
  "name": "myApp",
  "displayName": "myApp",
  "expo": {
    "name": "myApp",
    "slug": "myApp",
    ...
    "android": {
     ...
      "googleServicesFile": "./google-services.json",
     ...        

next for ios inside the app.json, we need to add additional property (entitlements.aps-environment)

{
  "name": "myApp",
  "displayName": "myApp",
  "expo": {
    "name": "myApp",
    "slug": "myApp",
    ...
    "ios": {
     ...
      "googleServicesFile": "./GoogleService-Info.plist",
      "entitlements": {
        "aps-environment": "production"
         },
     ...        

and for android, we need to add the permissions property ("POST_NOTIFICATIONS")

{
  "name": "myApp",
  "displayName": "myApp",
  "expo": {
    "name": "myApp",
    "slug": "myApp",
    ...
    "android": {
     ...
      "googleServicesFile": "./google-services.json",
      "permissions": ["POST_NOTIFICATIONS"],
     ...        

Next we need to install the firebase messaging npm package, i have also installed the "@react-native-firebase/app" but i am not sure if i need it.

?yarn add @react-native-firebase/messaging ?        

After installing the firebase packages, we need to add some additional configuration to our app.json

expo:{
...
android:{
...
}
ios:{
...
},
"plugins": [
     ...
      [
        "expo-build-properties",
        {
          "ios": {
            "newArchEnabled": false,
            "flipper": false,
            "useFrameworks": "static"
          },
          "android": {
            "newArchEnabled": false,
            "compileSdkVersion": 34,
            "targetSdkVersion": 34,
            "buildToolsVersion": "34.0.0"
          }
        }
      ],
      ...
      "@react-native-firebase/app",
      "@react-native-firebase/messaging"
    ],        

After this one you can install the expo dependencies, so run the following command from root of your project:

npx expo install --fix        

Next is to implement the push notifications in our code, so i have created a custom hook, which you can also use:

import messaging from "@react-native-firebase/messaging"
import { useEffect } from "react"
import { Alert, PermissionsAndroid, Platform } from "react-native"

export const usePushNotifications = () => {
  const requestPermission = async () => {
    if (Platform.OS === "android" && Platform.Version >= 33) {
      const hasPermission = await PermissionsAndroid.check(
        PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
      )

      if (!hasPermission) {
        const granted = await PermissionsAndroid.request(
          PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
        )

        if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
          console.warn("Push notification permissions are not granted.")
          return false
        }
      }
    }

    const authStatus = await messaging().requestPermission()
    const isAuthorized =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL

    if (!isAuthorized) {
      console.warn("Push notification permissions are not granted.")
    }
    return isAuthorized
  }

  const getToken = async () => {
    try {
      if (Platform.OS === "ios") {
        const apnsToken = await messaging().getAPNSToken()
        if (!apnsToken) {
          console.warn("APNs token is null. Check APNs setup.")
          return
        }
        console.log("APNs Token:", apnsToken)
      }

      const fcmToken = await messaging().getToken()
      console.log("FCM Token:", fcmToken)

      await saveTokenToServer(fcmToken)
      return fcmToken
    } catch (error) {
      console.error("Error fetching push notification token:", error)
    }
  }

  const saveTokenToServer = async (token: string) => {
    try {
      await fetch("https://your-server.com/api/saveToken", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token }),
      })
    } catch (error) {
      console.error("Error saving token to server:", error)
    }
  }

  const handleForegroundNotification = () => {
    const unsubscribe = messaging().onMessage(async (remoteMessage) => {
      console.log("Foreground message received:", remoteMessage)
      Alert.alert("New notification", JSON.stringify(remoteMessage.notification))
    })
    return unsubscribe
  }

  const handleBackgroundNotifications = () => {
    messaging().setBackgroundMessageHandler(async (remoteMessage) => {
      console.log("Background message received:", remoteMessage)
    })

    messaging().onNotificationOpenedApp((remoteMessage) => {
      console.log("Notification opened from background:", remoteMessage)
    })

    messaging()
      .getInitialNotification()
      .then((remoteMessage) => {
        if (remoteMessage) {
          console.log("Notification opened from quit state:", remoteMessage)
        }
      })
  }

  useEffect(() => {
    ;(async () => {
      const hasPermission = await requestPermission()
      if (hasPermission) {
        await getToken()
      }
    })()

    handleBackgroundNotifications()
    const unsubscribe = handleForegroundNotification()

    return () => {
      unsubscribe()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
}        

so basically just call the hook inside your app.tsx:

function App(props: AppProps) {
 ...
  usePushNotifications()
...        

Now we need to prebuild our app, and make a development build, since we can't use Expo Go anymore, so you simply run:

npx expo prebuild --clean        

and to run ios or android development builds you use the following commands:

 npx expo run:ios
 npx expo run:android        

Test the Push Notifications

To test and see if the notifications are working, there are two ways. First as you can see in my code i am logging the FCM token, so once you start the app, if everything is configured right, you will see something like this:

FCM Token: e_JWmTCQeEXKgp5-YdxkhL:APA91bFxKmtYuFbAO9AZ7rk15w_6Jk0C4JkRdymRDHGeYw5TzVJgpWwpI6QLIFYn00MWQN28zHAVloz6B3U23K79ROrHeU8Hd74YsYEuFTeRWhTaJ5P5TSU         

Now go back to the Firebase Console, and select the Run in the left side menu, and from there select "Messaging"

From here click on the "New campaign" and select "Notifications"

Now fill in the Title and some text for the notification.

New, the first option is to send a notification to specific devices, and to do so, click the "Send test message" button.


Now in the next screen add the FCM token we got from the console under the "Add an FCM registration token", and press the "+" button. After that, just check the newly created token, and click the "Test" button. With the app on our device in the background, you should receive the Push Notification in few seconds.

The next option is to continue with the creation of the Notifications Campaign, so after filling the title and the text click on the Next button, after that select the app you want to trigger notifications at, and in our case we have two Firebase apps, the ios and the android one, and just click on the "Review" and then "Publish". Now after 2-3 minutes you should get the notification on all the devices you have your app installed.

Again bare in mind that the Push Notifications may not work on the Simulators, but if you are in the app, and send test notification from Firebase, you should get an Alert with the notification in the App.

要查看或添加评论,请登录