Parse - Calling cloud code on app installation - ios

I have created some cloud code for my Parse database and I want it to be called when the a user installs the app. I have implemented Push so I want it to get triggered when the user registers for Push Notifications. I also want to be able to pass the "deviceToken" into my cloud function.
Here is the function that I have so far:
Parse.Cloud.define("newListing", function(request, response) {
var ListingClass = Parse.Object.extend("Listings");
var listing = new ListingClass();
listing.set("Name","--");
listing.set("DeviceID",request.params.param_DeviceID);
listing.save(null,{
success:function(listing) {
response.success(listing);
},
error:function(error) {
response.error(error);
}
});
});
Summary:
I want to call the cloud function "newListing" when the user registers for Push Notifications
Any ideas? Thanks!

You only get the deviceToken when the didRegisterForRemoteNotificationsWithDeviceToken method is called in the AppDelegate, so place your cloud code calling function in there.
Swift code:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// Do your normal token handling to set the device token in the Installation and save to Parse.com
let currentInstallation = PFInstallation.currentInstallation()
currentInstallation.setDeviceTokenFromData(deviceToken)
currentInstallation.saveInBackground()
// Call your cloud code function
let deviceTokenAsString = .... // You need to implement this code
PFCloud.callFunctionInBackground("newListing", withParameters: ["param_DeviceID": deviceTokenAsString]) { results, error in
// Error handling, and any other functionality you need when your cloud function is complete
}
}

Below is a solution that I came up with that didn't require the app to be modified. This cloud function gets called after a new installation occurs.
Parse.Cloud.afterSave(Parse.Installation, function(request, response) {
var newID = request.object.get("deviceToken");
var query = new Parse.Query("Listings");
query.equalTo("DeviceID", newID);
query.find({
success: function(results) {
if(results.length > 0){
response.success("ID already exists");
}else{
var ListingClass = Parse.Object.extend("Listings");
var listing = new ListingClass();
listing.set("Name","--");
listing.set("DeviceID",newID);
listing.save(null,{
success:function(listing) {
response.success(listing);
},
error:function(error) {
response.error(error);
}
});
}
},
error: function() {
response.error("ID fail");
}
});
});

Related

How do I run a Cloud Code on Heroku?

With the Parse's announcement of their retirement, I have migrated my Parse Server onto Heroku. With my still neophyte knowledge of Heroku, I do not know if they have a similar function to that of Cloud Code, but I do know that a few months ago Parse Introduced a Heroku + Parse feature that allows you to run Cloud Code on any node.js environment, particularly Heroku.
My dilemma is, I have already migrated my server from parse to Heroku prior to learning about this feature :/ , so I cannot run any parse cloud code form my terminal because there is no existing server there anymore. So the question is, how can I emulate this following Cloud Code in Heroku & How do I adjust my swift?
Cloud Code:
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("isLoginRedundant", function(request, response) {
Parse.Cloud.useMasterKey();
var sessionQuery = new Parse.Query(Parse.Session);
sessionQuery.equalTo("user", request.user);
sessionQuery.find().then(function(sessions) {
response.success( { isRedundant: sessions.length>1 } );
}, function(error) {
response.error(error);
});
});
and here is my swift back in xcode:
PFUser.logInWithUsernameInBackground(userName!, password: passWord!) {
(user, error) -> Void in
if (user != nil) {
// don't do the segue until we know it's unique login
// pass no params to the cloud in swift (not sure if [] is the way to say that)
PFCloud.callFunctionInBackground("isLoginRedundant", withParameters: [:]) {
(response: AnyObject?, error: NSError?) -> Void in
let dictionary = response as! [String:Bool]
var isRedundant : Bool
isRedundant = dictionary["isRedundant"]!
if (isRedundant) {
// I think you can adequately undo everything about the login by logging out
PFUser.logOutInBackgroundWithBlock() { (error: NSError?) -> Void in
// update the UI to say, login rejected because you're logged in elsewhere
// maybe do a segue here?
let redundantSession: String = "you are already logged in on another device"
self.failedMessage(redundantSession)
self.activityIND.stopAnimating()
self.loginSecond.userInteractionEnabled = true
}
} else {
// good login and non-redundant, do the segue
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
}
} else {
// login failed for typical reasons, update the UI
dispatch_async(dispatch_get_main_queue()) {
self.activityIND.stopAnimating()
self.loginSecond.userInteractionEnabled = true
if let message = error?.userInfo["error"] as? String
where message == "invalid login parameters" {
let localizedMessage = NSLocalizedString(message, comment: "Something isn't right, check the username and password fields and try again")
print(localizedMessage)
self.failedMessage(localizedMessage)
}else if let secondMessage = error?.userInfo["error"] as? String
where secondMessage == "The Internet connection appears to be offline." {
self.failedMessage(secondMessage)
}
}
}
}
I would first checkout the example repo and read the parse-server documentation. Parse server supports cloud code out of the box and you simply specify which file contains your functions and triggers in the parse-server config. The link you posted with the integration between parse and heroku is not relevant for parse-server.

How to handle sessions with QuickBlox in iOS

I am using Quickblox example writing in Swift for chat app. It uses QMServices.
Questions:
1) is there any approach for user login in iOS application with some session_token generated in any cloud backend ?
2) please help - how to renew user sessions in right way, I always get 422 error (validation error) multiple times (
In app using QuickBlox for iOS I try to login user in appdelegate:
if Storage.sharedInstance.currentUser != nil {
if !ServicesManager.instance().isAuthorized() {
let user =Storage.sharedInstance.currentUser
ServicesManager.instance().loginOrSignUp(user) {
[unowned self] success, error in
if (success) {
self.openMainScreen()
}
}
} else {
openMainScreen()
}
}
And loginOrSignUp function is:
func loginOrSignUp(user: QBUUser!, completion: ((success:Bool, errorMessage: String!) -> Void)!) {
self.logInWithUser(user) { (success:Bool, errorMessage: String!) -> Void in
if (success) {
Storage.sharedInstance.currentUser = self.currentUser()
completion(success: success, errorMessage: errorMessage)
} else {
self.authService.signUpAndLoginWithUser(user, completion: { (response: QBResponse!, user: QBUUser!) -> Void in
if user != nil {
Storage.sharedInstance.currentUser = self.currentUser()
completion(success: true, errorMessage: errorMessage)
} else {
completion(success: false, errorMessage: response.error?.error?.localizedDescription)
}
})
}
}
}
For example using Parse cloud code I can call function from cloud code:
Parse.Cloud.define("signUp", function(req, res) {
var phoneNumber = req.params.phoneNumber;
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.User);
query.equalTo('username', phoneNumber);
query.find({
success: function(results) {
if (results.length > 0) {
Parse.User.logIn(phoneNumber, secretWord).then(function (user) {
res.success(user.getSessionToken());
}, function (err) {
res.error(err);
});
} else {
var user = new Parse.User();
user.set("username",phoneNumber);
user.set("password",secretWord);
user.setACL({"*": { "read": true }});
user.signUp(null, {
success: function(user) {
res.success(user.getSessionToken());
// Hooray! Let them use the app now.
},
error: function(user, error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
},
error: function(error) {
res.error(err);
}
});
});
So using Parse I can just login to app with session_token generated with cloud code.
First question: can I do the same with QuickBlox - use for login some session_token generated in any cloud backend?
And the second question: when user logged in, session is expired in 2 hours and we have to renew session ?
When user has bad connection, before he receive useful information he has to renew session every 2 hours. Even Ukrainian API for Ukrainian usage (not for for best service in the world) use token generated with login function for any other requests. User should not renew session always - he get only useful information.
But if there is no approach in Quickblox without session renewing, then how to do this renew in right way ? I am very new to iOS development, but when I used Parse, I didi not get this problems (

App Badge not showing with Parse Push Notifications

I am using Parse as my backend for my iOS app to send push notifications. My problem is that the app icon never shows a badge after receiving push notifications (Besides for the badge, everything works fine).
I've checked the "badge" field in the Installation DB on Parse and it is increasing with every push, so I feel it might be a client-side issue
Here is my cloud code:
Parse.Push.send({
where: pushQuery,
data: {
aps: {
alert: "Your friend " + request.user.get("Name") + " just joined VoiceMe!",
sound: "default",
AlertType: "NewFriend"
},
badge: "Increment"
}
}, {
success: function() {
/*all is good*/
},
error: function(error) {
outcome = false;
errorOutput = error;
}
});
And the code in my app:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let currentInstallation = PFInstallation.currentInstallation()
if PFUser.currentUser() != nil {
currentInstallation.setObject(PFUser.currentUser()!, forKey: kParseInstallationUserKey)
}
currentInstallation.setDeviceTokenFromData(deviceToken)
currentInstallation.channels = ["global"]
currentInstallation.saveInBackgroundWithBlock { (resultBool, error) -> Void in
println("register device: --- \(resultBool) ---- error: \(error)")
}
}
Image of Installation DB on Parse:
See my answer here: https://stackoverflow.com/a/27615528/2353523 for reference
you've created your own dictionary. That's used for interactive notifications etc. Badge is outside of that dictionary that you've created which is the correct dictionary for sending pushes. That's why it doesn't increment in the payload you created under the dictionary of aps. You have to tell it to. Else, just delete the aps dict and pass your parameters through the data dict

How to change PFUser password in Swift?

I've tried updating the same way you would update a PFUser's email and even tried converting obj-c code (from other questions); neither worked. I also have no idea how to use Cloud Code (well...I installed it but I don't know how to pass information into Cloud Code or how to use JavaScript). Is there a way to update a users password without having to send the reset email?
You can not change a user's password that way for security reasons. You have two choices
Password Reset Email
Cloud Code Function to Reset the Password
As I understand that you do not know JavaScript, here is a cloud code function that you can use to reset the user's password, as well as a way to call the function using Swift.
Function (in JavaScript):
Parse.Cloud.define("changeUserPassword", function(request, response) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.User);
query.equalTo("username", request.params.username); // find all the women
query.first({
success: function(myUser) {
// Successfully retrieved the object.
myUser.set("password", request.params.newPassword);
myUser.save(null, {
success: function(myUser) {
// The user was saved successfully.
response.success("Successfully updated user.");
},
error: function(myUser, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
response.error("Could not save changes to user.");
}
});
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
});
Swift code to call the above function:
PFCloud.callFunctionInBackground("changeUserPassword", withParameters: ["username" : "MyCoolUsername", "newPassword" : passwordField.text]) {
(result: AnyObject?, error: NSError?) -> Void in
if (error == nil) {
// result is "Successfully updated user."
}
}
Good luck!
Yes, password can be changed without Cloud Code and e-mail. After changing "password" field for current user session is reset, but you can restore it by calling PFUser.logInWithUsername again.
let currentUser = PFUser.current()
currentUser!.password = "<new_password>"
currentUser!.saveInBackground() { (successed, error) in
if successed {
PFUser.logInWithUsername(inBackground: currentUser!.email!, password: currentUser!.password!) { (user, error) in
// Your code here...
}
}
}

Call Parse Cloud Function for current user

I'm trying to run some Cloud Functions in Parse, following along with a question I found here on Stackoverflow (Parse iOS SDK: Calling Cloud Functions From Xcode). Here are the functions I'd like to call within my app:
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);
});
});
I'd like to call the registerActivity function within a viewDidLoad within one of my view controllers. Here's what I have so far. I need some help completing the function:
override func viewDidLoad() {
super.viewDidLoad()
let currentUser = PFUser.currentUser()
PFCloud.callFunctionInBackground("registerActivity", withParameters: currentUser){
if (!error){
}
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
I get an error on the "PFCloud" line that states: PFUser is not identical to [NSOBject:Anyobject]. How can I call this registerActivity function for the current user?
Thanks!
Update
Updated function call in viewDidLoad to:
PFCloud.callFunctionInBackground("registerActivty", withParameters: nil, block: nil)
Which leads to an exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'
LAST UPDATE:
This to solved the problem:
PFCloud.callFunctionInBackground("registerActivity", withParameters: [:], target: nil, selector: "block:")
Thanks for the help!
You don't need to give currentUser if it is already logged in. In server side request.user will be already set to the logged in user that is making the request. In this case, you can call the cloud function with a parameter of empty dictionary.
On the other hand, the error you are getting is because PFUser object can't be passed to a cloud function. You can pass the user id as ["id": currentUser.objectId]. Then in the server, you can access it as request.params.id to get the user by id. This is in case you want to call the cloud function for any user (even it is not logged in).

Resources