Hey! These docs are for version 7.0, which is no longer officially supported. Click here for the latest version, 9.0!

Firebase

Integrate OneSignal with Firebase

If you are currently using Firebase or any type of database, CRM, DMP or CDP, you will want to be able to communicate user data and send messages as data changes.

📘

Extension From Main Docs

This is an extension of the concepts within our overview on Internal Database, DMP, & CRM integrations.

📘

Notification Analytics Guide

For sharing notification data between OneSignal see our Google Analytics for Firebase integration guide.

🚧

Interactions with Firebase Messaging SDK

OneSignal uses FCM Data messages. If you implemented the Firebase Messaging SDK and send push from OneSignal, it will trigger the FCM onMessageReceived public method.

In this guide we are going to look at integrating OneSignal with Firebase. We will go over:

  1. Storing the Firebase User ID into OneSignal (External User ID)
  2. Storing the OneSignal User ID (Player ID) into Firebase
  3. Leveraging Firebase Cloud Functions to trigger push notifications using OneSignal’s API

If interested in following along with a Demo, we have an iOS Example Demo in Swift you can download and try here. You will need to add your own GoogleService-Info.plist and a Firebase Blaze Plan for making API calls with Cloud Functions.

Firebase Setup

When a user signs up for an account with your app or website, you likely use Authentication to identify and create a record for them in your database with a User ID.

Firebase Create User Example

With Firebase Authentication we can use the createUser method to create the user record and return the Firebase User ID upon successful completion.

func createUserWithEmail(email: String, password: String, completion: @escaping (String) -> (), err: @escaping (Error)->()) {
    Auth.auth().createUser(withEmail: email, password: password, completion: { (authResult, error) in
        if error != nil {
            err(error!)
            return
        }
        let firebaseUserId = authResult!.user.uid
        completion(firebaseUserId)
    })
}

Firebase Sign In User Example

If the user is already registered with the app and is logging in. We can use the signIn method to authenticate the user and return the Firebase User ID.

🚧

IMPORTANT: User Id vs Device Id

Currently OneSignal stores device records, not user records. Your Firebase User ID will be a 1-many-relationship with the OneSignal Player ID record as users sign-in with multiple devices.

func loginWithEmail(email: String, password: String, completion: @escaping (String) -> (), err: @escaping (Error) -> ()) {
    Auth.auth().signIn(withEmail: email, password: password, completion: { (authResult, error) in
        let firebaseUserId: String!
        if error != nil {
            err(error!)
            return
        }
        if authResult != nil {
            firebaseUserId = authResult!.user.uid
            completion(firebaseUserId)
        }
    })
}

Firebase Get Current User Example

If the user is already registered and we want to verify they are a current user, before allowing access to the app, we can use the currentUser method to pass the Firebase User ID upon successful verification of the user.

func getCurrentUserId(completion:@escaping (String?) -> ()) {
    if let user = Auth.auth().currentUser {
        let firebaseUserId = user.uid
        completion(firebaseUserId)
    }
}

Firebase Update User Example

When user data changes, you likely have a method to update the user record within Firebase with this new information.

This example uses Firebase Realtime Database but can easily work with Firebase Cloud Firestore as well.

Here is our simple method for updating a User in the Realtime Database with their Firebase ID.

func updateUserInDatabaseWithUID(firebaseUserId: String, values: [String: AnyObject], completion: @escaping() ->()) {
    let ref = Database.database().reference()
    let usersReference = ref.child("users").child(firebaseUserId)
    usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
        if err != nil {
            return
        }
        completion()
    })
}

Once you have the Firebase ID, you can store the Firebase ID into OneSignal as the external_user_id and/or you can store the OneSignal ID into Firebase. We recommend doing both:

OneSignal Setup

OneSignal Set External User ID Example

OneSignal provides the concept of an external_user_id which is your single database User ID that can be associated with multiple OneSignal Device Records (Player IDs).

The setExternalUserId method will take the Firebase User ID and set it to each OneSignal Player ID when the user creates an account, logs in or is verified as the current user.

func setFirebaseUserIdToOneSignal(firebaseUserId: String) {
    OneSignal.setExternalUserId(firebaseUserId, withCompletion: { results in
      // The results will contain push and email success statuses
      print("External user id update complete with results: ", results!.description)
      // Push can be expected in almost every situation with a success status, but
      // as a pre-caution its good to verify it exists
      if let pushResults = results!["push"] {
        print("Set external user id push status: ", pushResults)
      }
      if let emailResults = results!["email"] {
          print("Set external user id email status: ", emailResults)
      }
    })
}

OneSignal Get Player ID Example

Using the OneSignal User Status Methods getPermissionSubscriptionState we can detect the Player ID of the device and update our Firebase User Record.

func getOneSignalPlayerId(completion: @escaping(String?) ->()) {
    let status: OSPermissionSubscriptionState = OneSignal.getPermissionSubscriptionState()
    if let osPlayerId = status.subscriptionStatus.userId {
        completion(osPlayerId)
    }
}

Firebase-OneSignal Setup

Combining the above example methods, we now have a working integration between OneSignal and Firebase which will pass the User IDs between both systems upon account creation, sign-in and current user verification.

Firebase-OneSignal Create User Example

createUserWithEmail(email: email, password: password, completion: { firebaseUserId in
  getOneSignalPlayerId({ osPlayerId in
    print("got OS Player ID: ", osPlayerId)
    let values: [String: AnyObject] = ["osPlayerId": osPlayerId as AnyObject]
    updateUserInDatabaseWithUID(firebaseUserId: firebaseUserId, values: values, completion: {
      print("osPlayerId \(osPlayerId) added to Firebase User Id \(firebaseUserId)")
      self.setFirebaseUserIdToOneSignal(firebaseUserId)
    })
}, err: { error in
  print("error = \(error)")
})

Firebase-OneSignal Sign In User Example

loginWithEmail(email: email, password: password, completion: { firebaseUserId in
  getOneSignalPlayerId({ osPlayerId in
    print("got OS Player ID: ", osPlayerId)
    let values: [String: AnyObject] = ["osPlayerId": osPlayerId as AnyObject]
    updateUserInDatabaseWithUID(firebaseUserId: firebaseUserId, values: values, completion: {
      print("osPlayerId \(osPlayerId) added to Firebase User Id \(firebaseUserId)")
      self.setFirebaseUserIdToOneSignal(firebaseUserId)
    })
}, err: { error in
  print("error = \(error)")
})

Firebase-OneSignal Get Current User Example

getCurrentUserId(completion: { firebaseUserId in
  getOneSignalPlayerId({ osPlayerId in
    print("got OS Player ID: ", osPlayerId)
    let values: [String: AnyObject] = ["osPlayerId": osPlayerId as AnyObject]
    updateUserInDatabaseWithUID(firebaseUserId: firebaseUserId, values: values, completion: {
      print("osPlayerId \(osPlayerId) added to Firebase User Id \(firebaseUserId)")
      self.setFirebaseUserIdToOneSignal(firebaseUserId)
    })
})

Sending Push Notification Using Firebase Cloud Functions

Leveraging Firebase Cloud Functions we can setup our app/site to send push when updates occur in our database. We will also use Firebase Storage to add images to push notifications.

Since we now have the Firebase User ID set in OneSignal, we can target users directly with the include_external_user_ids or include_player_ids parameter or we can target groups of users via included_segments or filters.

In this example, we will send push to devices using filters based on Data Tags. The idea is our app has many different "categories" that users can get notifications about. When users select these categories, we tag and target them with push when new posts about those categories are published from other users.

First we setup our Cloud Functions with Cloud Storage.

const admin = require("firebase-admin");
const functions = require("firebase-functions");
const serviceAccount = require("./your-service-account.json");
const gcs = require("@google-cloud/storage");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  storageBucket: "momentapro.appspot.com",
});

const getImageUrl = (data) => {
  const bucket = admin.storage().bucket("momentapro.appspot.com");
  const filePath = `post_images/${data.postImageUrl}.jpg`;
  const file = bucket.file(filePath);
  const config = {
    action: "read",
    expires: "08-23-2030",
    content_type: "image/jpeg",
  };
  return file.getSignedUrl(config);
};

exports.sendNotificationFromOS = functions.https.onCall(
  async (data, context) => {
    const signedUrl = await getImageUrl(data);
    const imageUrl = signedUrl[0];
    const sendNotification = (data) => {
      var headers = {
        "Content-Type": "application/json; charset=utf-8",
        Authorization: "Basic YOUR_ONESIGNAL_REST_API_KEY",
      };

      var options = {
        host: "onesignal.com",
        port: 443,
        path: "/api/v1/notifications",
        method: "POST",
        headers: headers,
      };

      var https = require("https");
      var req = https.request(options, (res) => {
        console.log("statusCode:", res.statusCode);
        console.log("headers:", res.headers);
        res.on("data", (data) => {
          console.log("Response:");
          console.log(JSON.parse(data));
        });
      });

      req.on("error", (e) => {
        console.log("ERROR:");
        console.log(e);
      });

      req.write(JSON.stringify(data));
      req.end();
    };

    var message = {
      app_id: "YOUR_ONESIGNAL_APP_ID",
      contents: { en: data.contents },
      data: {
        category: data.category,
        creatorId: data.creatorId,
        postId: data.postId,
        postImageUrl: data.postImageUrl,
        imageUrl: imageUrl,
      },
      filters: [{ field: "tag", key: data.category, relation: "exists" }],
      ios_attachments: { id1: imageUrl },
      big_picture: imageUrl,
      ios_badgeType: "Increase",
      ios_badgeCount: 1,
    };
    return sendNotification(message);
  }
);

Within our app, when the user publishes a post, we setup a function to pass in the data we want to include in the push notification:

  1. The "contents" or message of the push. This will be post description.
  2. The "category" which is what we will use for targeting all users interested in this category.
  3. The "postId" which we will use to deep link into the post when clicking the push.
  4. The "creatorId" which is the Firebase User ID of the person that created the post.
  5. The "postImageUrl" which we use to fetch the filepath for the image in Cloud Storage.
func sendOneSignalNotificationThroughFirebaseFunctions(values: [String: AnyObject]) {
    let data = ["contents": values["postDescription"],
                "category": values["category"],
                "postId": values["postId"],
                "creatorId": values["creatorId"],
                "postImageUrl": values["postImageUrl"]
    ]
    self.functions.httpsCallable("sendNotificationFromOS").call(data) { (result, error) in
      // [START function_error]
      if let error = error as NSError? {
        if error.domain == FunctionsErrorDomain {
          let code = FunctionsErrorCode(rawValue: error.code)
            print("Functions error code: ", code.debugDescription)
          let message = error.localizedDescription
            print("Functions error message: ", message.debugDescription)
          let details = error.userInfo[FunctionsErrorDetailsKey]
            print("Functions error details: ", details.debugDescription)
        }
        // [START_EXCLUDE]
        print(error.localizedDescription)
        return
        // [END_EXCLUDE]
      }
      // [END function_error]
      if let operationResult = (result?.data as? [String: Any])?["operationResult"] as? Int {
        print("operationResult: \(operationResult)")
      }
    }
    // [END function_add_numbers]
}

Upon receiving the push, we can use the OneSignal Notification Opened Handler to get the postId, feed it into our PostDataModel and deep link to the post. Example deep link below if your app contains a UITabBarController and UINavigationController

//inside the AppDelegate.swift didFinishLaunchingWithOptions function
let notificationOpenedBlock: OSHandleNotificationActionBlock = { result in
    // This block gets called when the user reacts to a notification received
    if let additionalData = result!.notification.payload!.additionalData {
        print("additionalData: ", additionalData)        
        if let postId = additionalData["postId"] as? String {
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            if  let postDetailVC = storyboard.instantiateViewController(withIdentifier: "PostDetailViewController") as? PostDetailViewController,
                let tabBarController = self.window?.rootViewController as? UITabBarController,
                let navController = tabBarController.selectedViewController as? UINavigationController {
                    let dataModel = PostDataModel()
                    dataModel.postId = postId
                    postDetailVC.dataModel = dataModel
                    navController.popViewController(animated: false)
                    navController.pushViewController(postDetailVC, animated: true)
            }
        }
    }
}

Feel free to test out the iOS Example Demo in Swift you can download and try here.

📘

Notification Analytics Guide

For sharing notification data between OneSignal see our Google Analytics for Firebase integration guide.