firestore cloud messaging iOS Swift - ios

I cannot get a new record entry into my firestore document db to generate an alert to users.
IOS app fetches and updates firestore data with no issues
If I manually send a message from firebase my app gets the message no issues
I can deploy my cloud function to firebase with no errors
What am I doing wrong? Thanks for any help.
let functions = require('firebase-functions')
let admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
let db = admin.firestore()
exports.announceAlert = functions.database
.ref('/alerts/{documentId}')
.onCreate((snapshot, context) => {
let alert = snapshot.val()
sendNotification(alert)
})
function sendNotification(alert) {
let title = alert.Incident_Number
let summary = alert.Flash_Summary
let status = alert.Incident_Status
let payload = {
notification: {
title: 'Critical Incident: ' + title,
body: 'Summary: ' + summary,
sound: 'default'
}
}
console.log(payload)
let topic = "Alerts"
admin.messaging().sendToTopic(topic, payload)
}

This is what I did and it worked. Please keep in mind that the user of your iOS app has to subscribe to the topic and you do that through the app. The code below is just a function telling firebase to send a notification to subscribed users when a new document is created in a certain repository.
let functions = require('firebase-functions')
let admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
let db = admin.firestore()
exports.announceMessage = functions.firestore
.document('/myData/{documentId}')
.onCreate((snapshot, context) => {
let message = snapshot.data()
sendNotification(message)
})
function sendNotification(message) {
let title = message.column1
let notification = message.column2
let payload = {
notification: {
title: 'Some title: ' + title,
body: 'Some header: ' + notification
},
}
console.log(payload)
let topic = "yourTopic"
return admin.messaging().sendToTopic(topic, payload)
}

Related

Firebase -how to run a calculation using a Cloud Function

I have a food app with a star system. Every time a user selects a rating/star I send their rating to the database and then run a firebase transaction to update the total amount of users who left a rating.
let theirRating: Double = // whatEverStarRatingTheySelected
let dict = [currentUserId: theirRating]
let reviewsRef = Database.database().reference().child("reviews").child(postId)
reviewsRef.updateChildValues(dict) { (error, ref) in
let postsRef = Database.database().reference().child("posts").child(postId).child("totalUsersCount")
postsRef.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in
// ...
})
}
When displaying the actual rating I pull the the post which has the totalUsersCount as a property, I pull all of the users actual ratings, add them all together, and then feed both numbers into an algo to spit out the actual rating. I do all of this on the client. How can I do the same thing "I pull all of the users actual rating, add them all together" with a Cloud Function, that is similar to this answer?
Database.database().reference().child("posts").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String:Any] else { return }
let post = Post(dict: dict)
let totalUsers = post.totalUsersCount
Database.database().reference().child("reviews").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
var ratingSum = 0.0
// *** how can I do this part using a Cloud Function so that way I can just read this number as a property on the post instead of doing all of this on the client ***
for review in (snapshot.children.allObjects as! [DataSnapshot]) {
guard let rating = review.value as? Double else { continue }
ratingSum += rating
}
let starRatingToDisplay = self.algoToComputeStarRating(totalUsersCount: totalUsersCount, ratingsSum: ratingSum)
})
})
This isn't a question about the algoToComputeStarRating, that I can do on the client. I want to add all of the user's ratings together using a Cloud Function, then just add that result as a property to the post. That way when I pull the post all I have to do is:
Database.database().reference().child("posts").child(postId).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String:Any] else { return }
let post = Post(dict: dict)
let totalUsersCount = post.totalUsersCount
let ratingsSum = post.ratingsSum
let starRatingToDisplay = self.algoToComputeStarRating(totalUsersCount: totalUsersCount, ratingsSum: ratingSum)
})
Database structure:
#posts
#postId
-postId: "..."
-userId: "..."
-totalUsersCount: 22
-ratingsSum: 75 // this should be the result from the cloud function
#reviews
#postId
-uid_1: theirRating
-uid_2: theirRating
// ...
-uid_22: theirRating
This is what I tried so far:
exports.calculateTotalRating = functions.https.onRequest((data, response) => {
const postId = data.postId;
const totalUsersCtRef = admin.database().ref('/posts/' + postId + '/' + 'totalUsersCt');
const postsRef = admin.database().ref('/posts/' + postId);
admin.database().ref('reviews').child(postId).once('value', snapshot => {
if (snapshot.exists()) {
var ratingsSum = 0.0;
snapshot.forEach(function(child) {
ratingsSum += child().val()
})
.then(() => {
return postsRef.set({ "ratingsSum": ratingsSum})
})
.then(() => {
return totalUsersCtRef.set(admin.database.ServerValue.increment(1));
})
.catch((error) => {
console.log('ERROR - calculateTotalRating() Failed: ', error);
});
}
});
});
I got it to work using this answer:
exports.calculateTotalRating = functions.https.onRequest((data, response) => {
const postId = data.postId;
const postsRef = admin.database().ref('/posts/' + postId);
const totalUsersCtRef = admin.database().ref('/posts/' + postId + '/' + 'totalUsersCt');
var ratingsSum = 0.0;
admin.database().ref('reviews').child(postId).once('value', snapshot => {
if (snapshot.exists()) {
snapshot.forEach((child) => {
ratingsSum += child().val()
})
console.log('ratingsSum: ', ratingsSum);
return postsRef.update({ "ratingsSum": ratingsSum }).then(() => {
return totalUsersCtRef.set(admin.database.ServerValue.increment(1));
})
.catch((error) => {
console.log('ERROR - calculateTotalRating() Failed: ', error);
});
}
});
});
I think this should provide you a good start. I have used a trigger so all the updates will happen automatically and your swift listener will immediately get the new ratings. :) Though I am not sure how your let post = Post(dict: dict) is going to parse the [String: Any] dictionary.
//We are adding a event listner that will be triggered when any child under the reviews key is modified. Since the key for updated review can be anything we use wildcards `{autoId}`.
exports.calculateTotalRating = functions.database
.ref("reviews/{autoId}")
.onUpdate((snapshot, context) => {
if (snapshot.exists()) { //For safety check that the snapshot has a value
const ratingsDict = snapshot.val(); //The updated reviews object you set using `reviewsRef.updateChildValues(dict)`
const autoId = context.params.autoId; //Getting the wildcard postId so we can use it later
//Added due to error in understanding the structure of review object. Can be ignored.
//delete ratingsDict("totalUserCount");
//delete ratingsDict("ratingsSum");
const totalUsers = Object.keys(ratingsDict).length; //Counting how many reviews are there in the ratingsDict
//Sum over all the values in ratingsDict. This approach is supposedly slower, but was oneliner so used it. Feel free to modify this with a more efficient way to sum values once you are more comfortable with cloud functions.
const ratingsSum = Object.keys(ratingsDict).reduce(
(sum, key) => sum + parseFloat(ratingsDict[key] || 0),
0
);
//Saving the computed properties.
ratingsDict["totalUserCount"] = totalUsers;
ratingsDict["ratingsSum"] = ratingsSum;
//Updating posts with new reviews and calculated properties
return functions.database.ref("posts").child(autoId).set(ratingsDict);
}
});
I feel like you are new to this, so you can follow this guide to create cloud functions. In your app's top level directory, just run firebase init functions and selecting your project from Use an existing project. This should create a functions folder for you with an index.js file where you can add this code. Then just run firebase deploy --only functions:calculateTotalRating.

From Airwatch SDK, how to I get the current logged in username/certificate

Airwatch documentation is gawdawful. So bear with me.
From Airwatch SDK, how to I get the currently logged in username/user certificate?
It's in the API
//setup authorization profile
var client = new RestClient("https://enterprise.telstra.com/api/mdm");
client.Authenticator=new HttpBasicAuthenticator("#####", "#######");
var request = new RestRequest("devices/search", DataFormat.Json);
request.AddHeader("aw-tenant-code", "##########");
//get the data as json
var response = client.Get(request);
if (response.IsSuccessful)
{
//serialize the data into the Devices[] array RootObject
RootObject ro = new RootObject();
ro = JsonConvert.DeserializeObject<RootObject>(response.Content);
//create a new DataTable with columns specifically required
DataTable dt = new DataTable("tblDevices");
dt.Columns.Add("AssetNumber");
dt.Columns.Add("DeviceFriendlyName");
dt.Columns.Add("IMEI");
dt.Columns.Add("MacAddress");
dt.Columns.Add("Model");
dt.Columns.Add("DeviceType");
dt.Columns.Add("OEMinfo");
dt.Columns.Add("Platform");
dt.Columns.Add("SerialNumber");
dt.Columns.Add("UserEmailAddress");
dt.Columns.Add("UserName");
//iterate through each device data and add it into a DataRow
foreach(Device d in ro.Devices)
{
DataRow dr = dt.NewRow();
dr["UserName"] = d.UserName.ToUpper(); //uppercase the userid
//add the row to the table
dt.Rows.Add(dr);
}
With godly help from the client
AWController.clientInstance().retrieveStoredPublicCertificates { payload, error in
if let _ = error {
return
}
guard let payload = payload as? [String : [PKCS12Certificate]] else {
return
}
processCertificates(payload)
NotificationCenter.default.post(name: .awSDKCeritificatesReceived,
object: payload)
}

Push Notifications using Firebase/Google Cloud Functions

I am trying to send a notification when a number is written to _random. I am able to get the device token, and the cloud function works perfectly. However, I do not receive the notification on the simulator. I am using the notification token that is pushed to Firebase to test with. If anybody can help, it will be highly appreciated.
https://i.stack.imgur.com/OB94c.png
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//Initial function call:
exports.makeRandomFigures = functions.https.onRequest((req, res) => {
    //create database ref
    var rootRef = admin.database().ref();
    var doc_count_temp = 0;
    var keys = [];
    var random_num = 0;
    //get document count
    rootRef.once('value', (snapshot) => {
        doc_count_temp = snapshot.numChildren();
        //real number of member. if delete _timeStamp then minus 2 not 3!
        var doc_count = doc_count_temp - 3;
        //get num array previous generated
        var xRef = rootRef.child("_usedFigures");
        xRef.once('value', function(snap) {
            snap.forEach(function(item) {
                var itemVal = item.val();
                keys.push(itemVal);
            });
            //get non-duplicated random number
            var is_equal = true;
            while (is_equal) {
                random_num = Math.floor((Math.random() * doc_count) + 1);
                is_equal = keys.includes(random_num);
            }
            //insert new random vaule to _usedFigures collection
            rootRef.child('_usedFigures').push(random_num);
            rootRef.child('_random').set(random_num);
        });
    });
    //send back response
    res.redirect(200);
});
exports.sendFigureNotification = functions.database.ref('_random').onWrite(event => {
const payload = {
notification: {
title: 'Title',
body: `Test`, //use _random to get figure at index key
badge: '1',
sound: 'default'
}
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24, //24 hours
content_available: true
};
const token = "cge0F9rUTLo:APA91bGNF3xXI-5uxrdj8BYqRPkxUPA5x9IQALtm3VEFJAdV2WQrQufNkzIclT5B671mBcvR6IDMbgSKyL7iG2jAuxRM3qR3MXhkNp1_utlXhCpE2VZqTw6Yw3d4iMMvHl1B-Cvik6NY";
console.log('Sending notifications');
return admin.messaging().sendToDevice(token, payload, options);
});
You can't get push notifications on the simulator.
To try some alternative ways check this link :
How can I test Apple Push Notification Service without an iPhone?

Like / Unlike Push Notifications issue

I have an app with a like functionality. Each time user A likes content X of user B:
It creates a notification request in my Firebase Database
Firebase functions observes the path on write & creates a push notification for user B
But when user A likes -> unlikes -> likes content X, user B gets another push notification.
How is it possible to decline further notification requests by user A for content X?
This is the Firebase Functions code:
exports.sendLikeNotification = likeRef.onWrite(event => {
if (event.data.previous.exists()) {
return;
}
var notificationId = event.params.likeNotificationId;
var notificationRequest = event.data.val();
var receiverId = notificationRequest.receiverId;
var message = notificationRequest.message
var data = notificationRequest.data
var topic = '/topics/user_' + receiverId;
var payload = {
notification: {
body: message,
sound: 'default'
},
data: { data }
};
var options = {
priority: "high",
contentAvailable: true
};
// Send a message to devices subscribed to the provided topic.
admin.messaging().sendToTopic(topic, payload, options)
.then(function(response) {
console.log("Successfully sent message:", response);
return event.data.adminRef.remove();
})
.catch(function(error) {
console.log("Error sending message:", error);
});
});
Thank you!

FCM custom notification for ios

I know this question have been ask a lot of time in android but not ios. So I already test out push notification with FCM it work fine. Mine problem is wether it's a way to create custom notification rather than letting the system to handle the notification with the notification payload?
-Users can send data messages with FCM.What you have to send is,
Sample PostMan Json data:
{ "data": {
"any key": "any value",
"any key 2": "any value 2"
},
"to" : "token received to mobile from FCM"
}
Payload inside "data" can change according to the requirement.
Mobile end:
application(_:didReceiveRemoteNotification:) override method will get fire.So it is unto developer to extract data from the userInfo property in iOS and use it.
For ios using c# working code:-
public bool NotificationToiosDevice()
{
bool str;
try
{
//Create the web request with fire base API
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
//serverKey - Key from Firebase cloud messaging server
tRequest.Headers.Add(string.Format("Authorization: key={0}", "AAAALNl-P7o:APA91bE38khU73UTdIj7L6LvVS3uI6f47REmxl.................................."));
//Sender Id - From firebase project setting
tRequest.Headers.Add(string.Format("Sender: id={0}", "12345"));
tRequest.ContentType = "application/json";
var payload = new
{
to = "dDDbFltwE5o:APA91bFmZZ-c1pNp................",
priority = "high",
content_available = true,
notification = new
{
title = "Title Of Notification",
},
data = new
{
body = new { Parameter1 = "FirstParameterValue", Parameter2 = "SecondParameterValue" },
},
};
string jsonNotificationFormat = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
Byte[] byteArray = Encoding.UTF8.GetBytes(jsonNotificationFormat);
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
if (dataStreamResponse != null) using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
// str = sResponseFromServer;
}
}
}
}
str = true;
}
catch (Exception ex)
{
str = false;
}
return str;
}

Resources