Send Push Notifications to your Application using Atlas App Services & iOS Realm SDK
Introduction
In a serverless application, one of the important features that we must implement for the success of our application is Push Notifications.?
Realm allows us to have a complete push notification system directly in our Services App. To do this, we will make use of several components that we will explain here and that we will develop in this tutorial. But first, let’s describe what our application does.
Context
The application consists of a public list of books that are stored locally on the device thanks to Atlas Device Sync , so we can add/delete or update the books directly in our Atlas collection and the changes will be reflected in our application.
We can also add as favorites any book from the list, although to do so it will be necessary to register previously using an email and password that we will also implement in our application through the Atlas App Services authentication providers .
The books, as they belong to a collection synchronized with Atlas Sync, will not only be stored persistently on our device but will also be synchronized with our user and we can retrieve the list of favorites on any device where we register with our credentials. Changes made to our favorites using other devices are automatically synchronized.
Firebase
The management of push notifications will be done through Firebase Cloud Messaging . In this way, we benefit from a single service to send notifications to iOS, web, and Android.?
The configuration is similar to the one we would follow for any other application, the difference is that we will install the firebase-admin SDK in our Atlas App Services application.
Triggers
The logic of this application for sending push notifications will be done through triggers. To do this, we will define two use cases:
Functions
The Atlas Triggers will have a function linked that will apply the logic for sending push notifications to the end devices. We will make use of the Firebase Admin SDK that we will install in our App Sevices App through the use of dependencies .
Overall application logic
This application will show how we can integrate push notifications with an iOS application developed in Swift. We will discuss how we have created each part with code, diagrams, and example usage.
At the end of this tutorial, you will find a link to a Github repository where you will find both the code of the iOS application as well as the code of the App Services application.
When we start the application for the first time, we log in using anonymous authentication , since to view the list of books it is not necessary to register using email/password, however, an anonymous user will still be created and saved in a collection Users in Atlas.
When we first access our application and enable push notifications, in our code we register with Firebase and this will generate a registration token that we will use later to send custom push notifications.
Messaging.messaging().token { token, error in
if let error = error {
print("Error fetching FCM registration token: \(error)")
} else if let token = token {
print("FCM registration token: \(token)")
// Save token in user collection
user.functions.updateFCMUserToken([AnyBSON(token), AnyBSON("add")], self.onCustomDataUpdated(result:realmError:))
}
}
Once we obtain the FCM token, we will use Call a Function using the SDK to send this token and save it in the user document corresponding to the logged-in user in the Users collection. Within the document for the user, we have defined a token field that will be composed of an array of FCM tokens.?
We have decided to save an array of tokens to be able to send a notification to each device that the same user has used to access the application.
The following is an example of a User document in the collection:
{
"_id": {
"$oid": "626c213ece7819d62ebbfb99"
},
"color": "#1AA7ECFF",
"fullImage": false,
"userId": "6268246e3e0b17265d085866",
"bookNotification": true,
"FCMToken": [
"as3SiBN0kBol1ITGdBqGS:APA91bERtZt-O-jEg6jMMCjPCfYdo1wmP9RbeATAXIQKQ3rfOqj1HFmETvdqm2MJHOhx2ZXqGJydtMWjHkaAD20A8OtqYWU3oiSg17vX_gh-19b85lP9S8bvd2TRsV3DqHnJP8k-t2WV",
"e_Os41X5ikUMk9Kdg3-GGc:APA91bFzFnmAgAhbtbqXOwD6NLnDzfyOzYbG2E-d6mYOQKZ8qVOCxd7cmYo8X3JAFTuXZ0QUXKJ1bzSzDo3E0D00z3B4WFKD7Yqq9YaGGzf_XSUcCexDTM46bm4Ave6SWzbh62L4pCbS"
]
}
Send notification to a?topic
Firebase allows us to subscribe to a topic so that we can send a notification to all devices that have ever subscribed to it without the need to send the notification to specific device tokens.
In our application, once we have registered using an email and password, we have the option to subscribe to receive notifications every time a new book is added to the book list or a previously created one is deleted.
When we activate this option, what happens is that we use the Firebase SDK to register in the topic books.
领英推荐
When we activate this option, what happens is that we use the Firebase SDK to register in the topic books.
static let booksTopic = "books"
@IBAction func setBookPushNotification(_ sender: Any) {
if booksNotificationBtn.isOn {
Messaging.messaging().subscribe(toTopic: SettingsViewController.booksTopic)
print("Subscribed to \(SettingsViewController.booksTopic)")
} else {
Messaging.messaging().unsubscribe(fromTopic: SettingsViewController.booksTopic)
print("Unsubscribed to \(SettingsViewController.booksTopic)")
}
}
How does it?work?
The logic we will follow will be as follows:
In our Atlas App Services App, we will have a Database trigger that will monitor the Books collection for any new inserts or deletes.?
Upon the occurrence of either of these two operations, the linked function shall be executed and send a push notification to the “books” topic.
Function logic:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert({
projectId: context.values.get('projectId'),
clientEmail: context.values.get('clientEmail'),
privateKey: context.values.get('fcm_private_key_value').replace(/\\n/g, '\n'),
}),
});
const topic = 'books';
var message = {
topic: topic
};
if (changeEvent.operationType === "insert") {
const name = changeEvent.fullDocument.volumeInfo.title;
const image = changeEvent.fullDocument.volumeInfo.imageLinks.smallThumbnail;
message.notification = {
"body": `${name} has been added to the list`,
"title": `New book added`
};
if (image !== undefined) {
message.apns = {
payload: {
aps: {
'mutable-content': 1
}
},
fcm_options: {
image: image
}
};
}
} else if (changeEvent.operationType === "delete") {
console.log(JSON.stringify(changeEvent));
const name = changeEvent.fullDocumentBeforeChange.volumeInfo.title;
message.notification = {
"body": `${name} has been deleted from the list`,
"title": `Book deleted`
};
}
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
return true;
})
.catch((error) => {
console.log('Error sending message:', error);
return false;
});
Example:
Send notification to a specific?device
To send a notification to a specific device, the logic will be somewhat different.?
For this use case, what we will do is that every time a book is updated, we will search if it belongs to the list of favorites of any user, and for those to which it belongs, we will send a notification to all the registered tokens.
This will ensure that only users who have added the updated book to their favorites will receive a notification alerting them that there has been a change.
How does it?work?
For this part, we will need a Database trigger that will monitor for updates operations on the books collection.?
When such an operation occurs, we will proceed to check if there is any user in the user collection who has added that book to his or her favorites list. If there is, we will create a new document in the Push Notifications auxiliary collection.
This auxiliary collection is used to optimize the sending of push notifications and manage if an error has occurred and even be able to perform a monitoring system as well as retries.?
Every time a notification needs to be sent, a document will be inserted in this collection containing:
An example of a Push Notification document will be the following:
{
"_id": {
"$oid": "62a0da5d860040b7938eab87"
},
"token": [
"e_OpA2X6ikUMk9Kdg3-GGc:APA91bFzFnmAgAhbtbqXOwD6NLnDzfyOzYbG2E-d6mYOQKZ8qVOCxd7cmYo8X3JAFTuXZ0QUXKJ1bzSzDo3E0D00z3B4WFKD7Yqq9YaGGzf_XSUcCexDTM46bm4Ave6SWzbh62L4pCbS",
"fQvffGBN2kBol1ITGdBqGS:APA91bERtZt-O-jEg6jMMCjPCfYdo1wmP9RbeATAXIQKQ3rfOqj1HFmETvdqm2MJHOhx2ZXqGJydtMWjHkaAD20A8OtqYWU3oiSg17vX_gh-19b85lP9S8bvd2TRsV3DqHnJP8k-t2WV"
],
"date": {
"$date": {
"$numberLong": "1654708829678"
}
},
"processed": true,
"changes": {
"volumeInfo": {
"title": "Pacific on Linguistics",
"publishedDate": "1964",
"industryIdentifiers": [
{
"type": "OTHER",
"identifier": "UOM:39015069017963"
}
],
"readingModes": {
"text": false,
"image": false
},
"categories": [
"Linguistics"
],
"imageLinks": {
"smallThumbnail": "https://books.google.com/books/content?id=aCVZAAAAMAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
"thumbnail": "https://books.google.com/books/content?id=aCVZAAAAMAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
},
"language": "en"
}
}
}
To process the notifications, we will have a database trigger that will monitor the Push Notifications collection and each new document will send a notification to the tokens of the client devices.
exports = async function(changeEvent) {
const admin = require('firebase-admin');
const db = context.services.get("mongodb-atlas").db("product");
const id = changeEvent.documentKey._id;
const bookCollection = db.collection("book");
const pushNotification = db.collection("pushNotification");
admin.initializeApp({
credential: admin.credential.cert({
projectId: context.values.get('projectId'),
clientEmail: context.values.get('clientEmail'),
privateKey: context.values.get('fcm_private_key_value').replace(/\\n/g, '\n'),
}),
});
const registrationToken = changeEvent.fullDocument.token;
console.log(JSON.stringify(registrationToken));
const title = changeEvent.fullDocument.changes.volumeInfo.title;
const image = changeEvent.fullDocument.changes.volumeInfo.imageLinks.smallThumbnail;
const message = {
"notification":{
"body": `One of your favorites changed`,
"title": `${title} changed`
},
tokens: registrationToken
};
if (image !== undefined) {
message.apns = {
payload: {
aps: {
'mutable-content': 1
}
},
fcm_options: {
image: image
}
};
}
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().sendMulticast(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
pushNotification.updateOne({"_id": BSON.ObjectId(`${id}`)},{
"$set" : {
"processed": true
}
});
})
.catch((error) => {
console.log('Error sending message:', error);
});
};
Example of a push notification to a user
Repository
The complete code for both the App Services App, as well as for the iOS application can be found at the following link: https://github.com/josmanperez/BookRealmTutorial
If you have found this tutorial useful, let me know so I can continue to add more information as well as step-by-step videos on how to do this.
Student at model engineering college
1 年Josman P. Thank you ??
Student at model engineering college
1 年Is there any videos to sync mongodb atlas and local realm database