Call Parse Cloud Function for current user - ios

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).

Related

Accessing QBCOCustomObject retrieved from QBRequest inaccessible outside of QBRequest

I am trying to access whatever record was returned from my QBRequest and send it to a separate function (in this case, I'm trying to set whatever I got from the request, convert its contents to strings, and then display in cells). However, I am unable to do this by direct referencing outside of the QBRequest nor setting the object to outside variable.
var posts = [QBCOCustomObject]()
func queryForPost(city: String) -> [QBCOCustomObject] {
let requestParameters = NSMutableDictionary();
[requestParameters.setObject(city, forKey: "Location")];
QBRequest.objectsWithClassName("UserFeed", extendedRequest: requestParameters, successBlock: { (response: QBResponse, record, page) in
print(record![0].fields!.allValues[0])
print(record![1].fields!.allValues[0])
//the two above prints return the desired values
self.posts = record!
}, errorBlock: {(response: QBResponse) in
// Handle error here
NSLog("error QBRequest - objectsWithClassName")
})
print(self.posts.count, "POSTS.COUNT") //can't access posts or record - this returns 0, and THEN returns whatever print functions are inside the QBRequest.
print(self.posts, "POSTS")
return self.posts
}
"print(self.posts.count, "POSTS.COUNT")" returns 0, and THEN returns whatever print functions are inside the QBRequest. Seems as if the QBRequest is performing after going through the whole function. Very confused and not sure how to fix this...
Been struggling with this for a while now :( If anybody could provide some guidance I would very much appreciate it.
Thank you in advance!
Lance

Parse PFCloud.callInBackground completion block never called i.e no response received

I have defined a function in the Parse Cloud Code called "relatedWords". When I try call this function in my iOS app, the completion block/closure is never called i.e no response is received.
I've tested the function in the Parse API Console and it is working fine there, so I know it's not an issue with the cloud code.
Any ideas on what the issue is?
My swift code:
func fetchRelatedKeyWordsForWord(word: String)
{
PFCloud.callFunctionInBackground("relatedWords", withParameters: ["hashtag": word]) { (response, error) -> Void in
//This is never called
print(response)
print(error)
}
}
Snippet of the cloud code:
Parse.Cloud.define("relatedWords", function(request, response) {
Parse.Cloud.useMasterKey();
var hashtag = request.params.hashtag;
...
...
//Run a query
var query = new Parse.Query(parseClassName);
query.find({
success: function(results) {
if (results.length != 0) {
console.log("Found Objects! Returning Objects");
response.success(results);
return;
}
Edit:
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem.
you can use other server, other vise pay money on parse and solve the problem.

how to correctly make cloud code request with parse?

I am attempting to make all my user sessions with Parse exclusive, meaning if a user is already logged in on a certain device in a certain location, if another device logs in with the same credentials, I want the previous session(s) to be terminated, with a message of an alert view of course. Sort of like the old AOL Instant Messaging format. I figured the code for this action should be written in the login logic, so I wrote this within my login "succession" code :
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> Void in
if user != nil || error == nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSuccess", sender: self)
PFCloud.callFunctionInBackground("currentUser", withParameters: ["PFUser":"currentUser"])
//..... Get other currentUser session tokens and destroy them
}
} else {
Thats probably not the correct cloud code call, but you get the point. When the user logs in once again on another device, I want to grab the other sessions and terminate them. Does anyone know the correct way to go about making this request in swift?
I speak swift with a stutter, but I think I can answer adequately in almost-swift. The key idea is to start the success segue only after the cloud says it's okay. Here's what I think you want:
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> 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?
}
} else {
// good login and non-redundant, do the segue
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
}
} else {
// login failed for typical reasons, update the UI
}
}
Please don't take me too seriously on swift syntax. The idea is to nest the segue in the completion handlers to know that you need to do it before starting it. Also, please note that the explicit placement on the main_queue within the completion handler is unnecessary. The SDK runs those blocks on the main.
A simple check to determine if a user's session is redundant (not unique) looks like this...
Parse.Cloud.define("isLoginRedundant", function(request, response) {
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);
});
});

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...
}
}
}

Parse - Calling cloud code on app installation

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");
}
});
});

Resources