Sending Parse Push with Cloud Code - ios

I cannot find any documentation on Parse.Push used in Parse Cloud Code. The usage case that I see is like this...
// Send the push notification to results of the query
Parse.Push.send({
where: pushQuery,
data: {
alert: message
}
}).then(function() {
response.success("Push was sent successfully.")
}, function(error) {
response.error("Push failed to send with error: " + error.message);
});
What I am trying to do is send a push notification if a recipient user is setup for notifications (i.e. has a valid Installation instance, associated to their user).
At the moment I create the query and pass that into the above with pushQuery. What I notice is that a Push is created in the Parse dashboard but the Pushes sent is 0.
Really I just want to create the Push if a user exists. I have created the query and can run this and return if I get results or not like this...
Parse.Cloud.define("sendTurnNotificationToUser", function(request, response) {
var senderUser = request.user;
var recipientUserId = request.params.recipientId;
var message = request.params.message;
// Validate the message text.
// For example make sure it is under 140 characters
if (message.length > 140) {
// Truncate and add a ...
message = message.substring(0, 137) + "...";
}
// Send the push.
// Find devices associated with the recipient user
var recipientUser = new Parse.User();
recipientUser.id = recipientUserId;
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("user", recipientUser);
pushQuery.find({
success: function(results) {
response.success("push user lookup was ok");
response.success(results);
},
error: function() {
response.error("push user lookup failed");
}
});
I could add the Parse.Push.send call to the success of the query. However the Parse.Push.send has a where clause and I do not know what is required there? I do not want to run the query twice.

You're on the right track. Push "advanced targeting" allows the app to push to installations resulting from a query. That's what the where clause is for...
// don't run find on the pushQuery. set it up as you have it
// then, assuming it returns some installation(s)...
Parse.Push.send({ where: pushQuery, data: "hello" }).then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
Incidentally, you can use createWithoutData on Parse.User as a shortcut ...
var recipient = Parse.User.createWithoutData(request.params.recipientId);
but the longer form you have should work, too.

It seems like you may be overthinking this. There's no harm in sending a push notification to 0 installations as the push query will not match any recipients. I wouldn't worry too much about this and I wouldn't add such a pre-check to your code. It would add an unnecessary delay to your code and of course would result in the query being run twice.
If you want to do this anyway -- maybe you wish to keep your push logs free of clutter - you can indeed query over the Installation class to check if the query would have matched a set of installations, and if it does, you can then pass that same query to Parse.Push.send().
Yes, that will result in the query run twice, but that's to be expected, as you can't know how many objects will be matched without running the query.

Related

Query Session class with cloud code Parse

I am trying to send a push notification using a user's id. I have already tested sending with installationId, querying the _Installation class, but i would like to query the session class of the user pointer, to then turn around and query the installation class.
My problem is lying in the restrictions of querying the session class. I have successfully used createWithoutData() found here, and I know it is working because i can output that user. However, even after using the master key found here, the results are always empty.
The general practise for sending Push Notification to specific user is that you will store pointer to User in Installation class... for example when user register do this
Swift
if let installation = PFInstallation.current() {
installation["user_id"] = PFUser.current()!
installation.saveInBackground()
}
Cloudcode
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo('user_id', tarUser);
pushQuery.exists("deviceToken");
pushQuery.limit(1); // in case there are more Installation with the user ID, use only the latest
pushQuery.descending("createdAt");
Parse.Push.send({
where: pushQuery, // Set our Installation query
data: {
alert: "Some push text"
}
}, {
success: function() {
// Push was successful
response.success();
},
error: function(error) {
console.error("Got an error " + error.code + " : " + error);
response.error(error);
},
useMasterKey: true
});
if I remember correctly you have to query the pointer in Cloud code with pointer structure, like this
var tarUser = {
__type: 'Pointer',
className: '_User',
objectId: 'insertObjectIDHere'
};

Parse: Dynamic datas in push notification

Notification Using Parse from Server.
I have to send different message for a set of users
like,
you got 1st rank - to User A
you got 2nd rank - to User B
you got 3rd rank - to User C
In Parse, i have save the rank details for each users like
device_id userA rank1
device_id userB rank2
device_id userc rank3
While pushing the message, i have the send the message with the rank value
url = 'https://api.parse.com/1/push'
message = 'you got Xth rank'
data = {:data => message}.to_json
HTTParty.post(url,:body => data)
I can able to send the static message with the above code.
But how to send the message with dynamic values from parse database of respective record.
Looping through users and sending them push one by one may be the solution to your problem, Here is an example code in JS
var installationQuery = new Parse.Query(Parse.Installation);
installationQuery.each(
function(result) {
var tempQuery = new Parse.Query(Parse.Installation);
tempQuery.equalTo("username", result.get('username'));
// Send push notification to query
Parse.Push.send({
where: tempQuery,
//
data: {
alert: "Hey "+result.get('name')+" your Rank ."+ result.get('rank')
}
}, {
success: function() {
response.success("Pushed to "+result.get('name'));
},
error: function(error) {
// Handle error
response.success("Error in push to"+result.get('name'));
}
});
},
{
success: function(result) {
console.log("push sent to all users.");
response.success();
},
error: function() {
} }
);
And if your data for ranks isn't in installation e.g. it's in Users Table then your first query would be on Users table and in function on each to send push you second query will remain on installation table.
Any query?

Push Notification Not Arriving from Parse Cloud Code

I wrote a simple job to try sending a push notification to myself. Here's the code:
Parse.Cloud.job("testPush", function(request, status) {
Parse.Cloud.useMasterKey();
var installationQuery = new Parse.Query(Parse.Installation);
installationQuery.equalTo("user", "6t1JIuNqe1"); // I triple checked - this is the value of my user in the installation table.
Parse.Push.send({
where: installationQuery,
data: {
alert: "Test"
},
}, {
success: function() {
console.log("The Push Test Worked!");
status.success("All done with the push test!");
}, error: function(error) {
console.error("Something bad happened " + error);
status.error("Something bad happened during the Parse test...");
}
});
});
Although it logs in Parse that the job was run successfully, I never see a notification appear on my iPhone. I checked in Settings - it's all set up properly there (notifications are allowed to appear and should appear as banners, they should show up in notification center, they should show up on my lock screen). And yet the notification never appears.
What more do I need to check? What am I missing?
Pointer field should work with an instance.
Try replacing installationQuery.equalTo("user", "6t1JIuNqe1"); with the following:
var user = new Parse.User();
user.id = '6t1JIuNqe1';
installationQuery.equalTo('user', user);

Parse iOS SDK: How to set user "online" status?

Scenario = I have an app that allows users to log in and view other users who are also "online". In order to set each user's online status I believe that I should set the code...
[PFUser currentUser] setObject:[NSNumber numberWithBool:YES] forKey:#"isOnline"];
[[PFUser currentUser] saveInBackground];
at certain times in the application when the user uses the app. Probably the appDelegate but I'm not sure.
(if this is not correct then please correct me)
Question = Where should this code be set inside the application so that it always keeps track of when a user is "online" and when they are "offline"?
(please include method names)
The most reliable way to handle this is to create a column named lastActive of type Date, then create a Cloud Code function to update this with the server time (so clock differences isn't an issue).
Then to get "online" users just have another Cloud Function that does a query where lastActive is greater than now - some time window like 2 minutes.
var moment = require("moment");
Parse.Cloud.define("registerActivity", function(request, response) {
var user = request.user;
user.set("lastActive", new Date());
user.save().then(function (user) {
response.success();
}, function (error) {
console.log(error);
response.error(error);
});
});
Parse.Cloud.define("getOnlineUsers", function(request, response) {
var userQuery = new Parse.Query(Parse.User);
var activeSince = moment().subtract("minutes", 2).toDate();
userQuery.greaterThan("lastActive", activeSince);
userQuery.find().then(function (users) {
response.success(users);
}, function (error) {
response.error(error);
});
});
Your client will want to call the registerActivity Cloud Function every 1.5 minutes to allow some overlap so users don't appear to go offline if their internet is a bit slow.
Of course you can adjust the time windows to suit your needs. You could also add the ability to filter which users are returned (e.g. online friends only).

ASP.NET MVC ajax chat

I built an ajax chat in one of my mvc website. everything is working fine. I am using polling. At certain interval i am using $.post to get the messages from the db. But there is a problem. The message retrieved using $.post keeps on repeating. here is my javascript code and controller method.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
$("#hdnLastMsgRec").val(Data.LastMsgRec);
}, "json");
t = setTimeout("GetMessages()", 3000);
}
and here is my controller method to get the data:
public JsonResult GetMessages(int roomId,DateTime lastRecMsg)
{
StringBuilder messagesSb = new StringBuilder();
StringBuilder newUserSb = new StringBuilder();
List<Message> msgs = (dc.Messages).Where(m => m.RoomID == roomId && m.TimeStamp > lastRecMsg).ToList();
if (msgs.Count == 0)
{
return Json(new { Messages = "", LastMsgRec = System.DateTime.Now.ToString() });
}
foreach (Message item in msgs)
{
messagesSb.Append(string.Format(messageTemplate,item.User.Username,item.Text));
if (item.Text == "Just logged in!")
newUserSb.Append(string.Format(newUserTemplate,item.User.Username));
}
return Json(new {Messages = messagesSb.ToString(),LastMsgRec = System.DateTime.Now.ToString(),newUser = newUserSb.ToString().Length == 0 ?"":newUserSb.ToString()});
}
Everything is working absloutely perfect. But i some messages getting repeated. The first time page loads i am retrieving the data and call GetMessages() function. I am loading the value of field hdnLastMsgRec the first time page loads and after the value for this field are set by the javascript.
I think the message keeps on repeating because of asynchronous calls. I don't know, may be you guys can help me solve this.
or you can suggest better way to implement this.
Kaivalya is correct about the caching, but I'd also suggest that your design could and should be altered just a tad.
I made a very similar app recently, and what I found was that my design was greatly enhanced by letting the controllers work with the fairly standard PRG pattern (post-redirect-get). Why enhanced? well, because POST methods are built to add stuff to an app, GET methods are supposed to be used to get information without side effects. Your polling should be just getting new messages w/o side effects.
So rather than your $.post call expecting data and handling the callback, what I'd recommend is having your controller expose a method for creating new chat messages via POST and then another method that get the last X chat messages, or the messages since a certain timestamp or whatever.
The javascript callback from the post action, then can update some variables (e.g. the last message id, timestamp of the last message, or even the whole URL of the next message based on the info contained in a redirect, whatever).
The $.post would fire only in response to user input (e..g type in a box, hit 'send') Then, you have (separately) a $.get call from jquery that's set up to poll like you said, and all it does is fetch the latest chat messages and it's callback updates the chat UI with them.
I got my answer here: ASP.NET AJAX CHAT
The names below i am referring to are from above link.
i think the actual problem was with the timestamp thing and asynchronous behaviour of $.post. after calling "GetMessages()" method, even if the previous request to retrive chat message was not complete anathor call to same method used to fire due to setting timeout for "GetMessages()" method outside the $.post method. In my question you can see that timeout for "GetMessages()" method is set outside the $.post method. Now i set the timeout for "GetMessages()" method inside the $.post method. so that next call to "GetMessages()" only occur after 3 seconds of completion of current $.post method. I have posted the code below.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.LastMsgRec.length != 0)
$("#hdnLastMsgRec").val(Data.LastMsgRec);
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
t = setTimeout("GetMessages()", 3000);
}, "json");
}
I addition to that i also changed few things. As suggested by ignatandrei i placed $("#hdnLastMsgRec").val(Data.LastMsgRec); immediately after function(Data) {.
and also
as said by MikeSW i changed the data retrieval process. Previously i was extracting data on the basis of timespan(retrieve all the data associated with
this room id that has greater timespan than last data retrieved message timespan) but now i keep track of the messageid. Now i retrieve only those data that
has message id greater than last retrieved message id.
and guess what no repeataion and perfectly working chat application so far on my intranet.
I still got to see it's performance when deployed on internet.
i think it solved my problem.
i will still test the system and let u guys know if there is any problem.
By default $.post() caches the results
You can either call $.ajaxSetup ({ cache: false}); before JS GetMessages function call to ensure caching is disabled or change the $.post to $.ajax and set cache attribute to false. In the end $.post() is a short cut to this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: success
dataType: dataType
});

Resources