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:
- Storing the Firebase User ID into OneSignal (External User ID)
- Storing the OneSignal User ID (Player ID) into Firebase
- 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:
- The "contents" or message of the push. This will be post description.
- The "category" which is what we will use for targeting all users interested in this category.
- The "postId" which we will use to deep link into the post when clicking the push.
- The "creatorId" which is the Firebase User ID of the person that created the post.
- 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.
Updated over 2 years ago