Parse backend: key won't increment? - ios

I'm trying to increment a number in my parse table under the column "votes". Here's my code:
func upVote() {
var reviewQuery: PFQuery = PFQuery(className: "reviews")
reviewQuery.whereKey("content", equalTo: reviewTextView.text)
reviewQuery.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
println(object)
let review:PFObject = object as! PFObject
review.incrementKey("votes", byAmount: 1)
}
}
}
}
When I print the object in the console I can see that it is the correct object that I'm looking for. It looks like this:
<reviews:ZqgSVL1Tsd:(null)> {
content = "njk\n";
reviewer = "<PFUser:6387CJtYI1>";
votes = 1;}
But when I look at my parse end, the number of votes has not changed. What am I doing wrong?

Save the object with
review.saveInBackground()
after incrementing the key.

After you modify an object, however small the modification, you must save it after. You are not saving your changes to the object review.
You have several options for saving, including save(), saveInBackground(), saveEventually(), and more. See the documentation for PFObject for more information:
https://www.parse.com/docs/ios/api/Classes/PFObject.html#//api/name/save
For example, you could save the object synchronously with
review.save() and you could save the object asynchronously with review.saveInBackground().

Related

Parse PFQuery bug with geoPoint constraint

I am doing a pretty straightforward PFQuery. I want to get any objects where the specific key I'm entering exists, and the object is within 2 km of user location. However, this query returns all objects within 2 km, but doesn't filter out anything to only return objects where that key exists. I checked and the key ("T(objectIDs[0])") contains the information I think it does. Additionally, I have tried this in a compound query where I am doing a different key in each query. This query works as expected and returns objects within 2 km AND only where the key exists. Have any of you seen behavior like this? Any insight is greatly appreciated, thanks!
let singleQuery = PFQuery(className: locationTagsClassNameConstant)
singleQuery.whereKeyExists("T\(objectIDs[0])")
singleQuery.whereKey(geoPointColumnNameConstant, nearGeoPoint: userGeo, withinKilometers: 2)
singleQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
var dataToExtract:Array<AnyObject> = []
if let objects = objects {
for activity in objects {
dataToExtract.append(activity)
}
complete(result: dataToExtract)
}
} else {
// Log details of the failure
print("Error++++: \(error!)")
}
})

Two constraints on one key in a Parse Query

I'm using Parse and my app is written in Swift. I have a golf app that allows a user to friend other users. The users can log their golf scores and then see their golf scores and their friend's scores in a leaderboard style tableViewController.
The problem I'm having is that Parse doesn't support two constraints on the same key in a query. I have a function that queries the users and their scores and stores them in an array of tuples (leadeboardData). I'm trying to querying the current user's PFRelation as well as the current user (variable "friendsRelation" and constant "friendQuery"). The class I query is the "GolfScorecard", which is where the scores are stored on Parse. I then called the "whereKey" method on the "golfer" key, which is where my user is stored under on Parse. I call "whereKey matchesQuery" for my friendsRelation query and then "whereKey equalTo" to try and get my current user. I then "includeKey" "golfer" so I can get the user info along with the score info. Any suggestions on how to go about this? I'm trying to do it all in one query but when the "whereKey" method is called on the same key ("golfer") the last one overrides the first one, which makes it only possible to get the friend info or the current user info but not both. I'm stumped how to include both. Thanks in advance.
Here is my function I call to make the query:
func loadLeaderboardData() {
leaderboardData.removeAll()
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
let friendQuery = friendsRelation?.query()
let query = PFQuery(className: "GolfScorecard")
query.whereKey("golfer", matchesQuery: friendQuery!)
query.whereKey("golfer", equalTo: PFUser.currentUser()!)
query.includeKey("golfer")
query.orderByAscending("score")
query.findObjectsInBackgroundWithBlock { (scoreCards: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object:PFObject in scoreCards! {
let golfer:PFObject = object["golfer"] as! PFObject
self.leaderboardData.append(object,golfer)
dispatch_async(dispatch_get_main_queue()) {
self.leaderboardTableView.reloadData()
}
}
} else {
print(error)
}
}
}
Thanks Paulw11 the orQueryWithSubQueries worked. The only issue it ended up presenting was my app was crashing with an error stating 'Key "username" has no data. Call fetchIfNeeded before getting its value.'...I don't think the Parse Query was coming back in time to load in the tableView that I was putting the data in. I ended up using the 'fetchIfNeededInBackgroundWithBlock' method inside of the cellForRowAtIndexPath call around the code where I was calling the 'username' data at. This allowed it to grab the data in the background if it didn't come back in time. This seemed to due the trick.
-Also I subclassed my queries, which is where the GolfScorecard & GolferProfile is coming from inside of my for-in loop.
-This is my new compound Query:
func loadLeaderboardData() {
leaderboardData.removeAll()
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
let friendQuery = friendsRelation?.query()
let friendScorecardQuery = PFQuery(className: "GolfScorecard")
friendScorecardQuery.whereKey("golfer", matchesQuery: friendQuery!)
let currentUserScorecardQuery = PFQuery(className: "GolfScorecard")
currentUserScorecardQuery.whereKey("golfer", equalTo: PFUser.currentUser()!)
let subQuery = PFQuery.orQueryWithSubqueries([friendScorecardQuery, currentUserScorecardQuery])
subQuery.orderByAscending("score")
subQuery.findObjectsInBackgroundWithBlock { (scoreCards: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object:PFObject in scoreCards! {
if let object = object as? GolfScorecard {
let golfer = object["golfer"] as! GolferProfile
self.leaderboardData.append(object,golfer)
dispatch_async(dispatch_get_main_queue()) {
self.leaderboardTableView.reloadData()
}
}
}
} else {
print(error)
}
}
}

PFQuery where array of pointers contains a certain PFObject

Objects in my class Deal have an attribute relatedContacts which is an array of pointers to Contact objects. I'm running the following query to determine whether the current Contact object is the target of a pointer in any Deal, prior to deleting the Contact.
let relatedContactObjects:NSArray = [self.contactObject] as NSArray
let relatedContactQuery:PFQuery = PFQuery(className: "Deal")
relatedContactQuery.fromLocalDatastore()
relatedContactQuery.fromPinWithName("Deals")
relatedContactQuery.whereKey("user", equalTo: PFUser.currentUser()!)
relatedContactQuery.whereKey("relatedContacts", containsAllObjectsInArray: relatedContactObjects as [AnyObject])
However this returns Parse Error 102: "Value type not supported for $all queries."
The Parse documentation says that containsAllObjectsInArray takes an NSArray, but Xcode shows a warning that NSArray is not implicity convertible to [AnyObject].
Any ideas how I can make this query work?
Edit: I looked at the contents of relatedContacts and it seems that each instance contains an array of dictionaries, example: [{"__type":"Pointer","className":"Contact","objectId":"BoLym053hX"},{"__type":"Pointer","className":"Contact","objectId":"AgpnxAFUBn"},{"__type":"Pointer","className":"Contact","objectId":"ob20tThdfp"}]
As suggested, I've also looked at the containedIn query constraint, but that is used to identify objects that are contained in a given array. I am trying to identify arrays that contain a given object.
Parse.com overloads equalTo: by allowing it to mean either: (a) a singular property equals the operand, or (b) an array property contains the operand. So you're objective is easily stated as follows:
relatedContactQuery.fromPinWithName("Deals")
relatedContactQuery.whereKey("user", equalTo: PFUser.currentUser()!)
relatedContactQuery.whereKey("relatedContacts", equalTo:self.contactObject)
Prior to the accepted answer, I also tried using loops to go through the arrays and identify whether they contained the current object, then incremented a count.
var dealsPointingToContactCount:Int = 0
func countDealsRelatedToContact() {
let dealsWithRelatedContactQuery:PFQuery = PFQuery(className: "Deal")
dealsWithRelatedContactQuery.fromLocalDatastore()
dealsWithRelatedContactQuery.fromPinWithName("Deals")
dealsWithRelatedContactQuery.whereKey("user", equalTo:PFUser.currentUser()!)
dealsWithRelatedContactQuery.whereKeyExists("relatedContacts")
dealsWithRelatedContactQuery.findObjectsInBackgroundWithBlock{(objects, error) -> Void in
if (error == nil) {
var dealsWithPointersToContacts:NSArray = objects! as NSArray
for deal in dealsWithPointersToContacts {
var dealContactsArray:NSArray = deal["relatedContacts"] as! [PFObject]
for contact in dealContactsArray {
if contact as! PFObject == self.object {
self.dealsPointingToContactCount++
println("Deals pointing to current contact: \(self.dealsPointingToContactCount)")
}
}
}
}
}
}

How to store retrieved data to the another class in Parse

I've a small problem. I am working on an App using Parse backend. So my problem is:
i've retrieved all the objectIds of users in _User class like this:
var userIds = [String]()
var userQuery = PFUser.query()
userQuery?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects {
self.userIds.removeAll(keepCapacity: true)
for object in users {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
self.userIds.append(user.objectId!)
}
}
}
}
println(userIds)
})
Now i want to save all the userIds which are stored in the array "userIds" to a column in a class named "Something". so the code will be like:
var something:PFObject = PFObject(className: "Something")
something["users"] = // have problem here
something.saveInBackground()
if i put userIds here, it gives an error.. because userIds is an array but column "users" is String.. but also i want to save all the userIds seperately in the column "users" like it is saved in objectId column of _User class..
I'm new here and I can't comment in the reply where you asked for help. Anyway, what do you want now is save only new users, as you said:
I just want to save the users which are not there
Well, there are some suggestions:
You can retrieve your User class and check for new users manually in the app and then send the new ones to your Class.
You can create a new column in your User class like "saved" where contains a boolean indicating if you already saved this user to your custom class or not. Remember also, of always do
user["saved"] = false
while creating a new user and then simply do:
let search = PFQuery(className: "_User")
search.whereKey("saved", equalTo: false)
to get these non saved users.
I strongly recommend the second option.
Ok, the case that you describe should look something like this:
var userIds = [String]()
var userQuery = PFUser.query()
userQuery?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects as? [PFUser] {
var somethingObjects = [PFObject]()
self.userIds.removeAll(keepCapacity: true)
for user in users {
if user.objectId != PFUser.currentUser()?.objectId {
self.userIds.append(user.objectId!) // I don't know if this is necessary anymore for your usecase
var something:PFObject = PFObject(className: "Something")
something["users"] = user.objectId
somethingObjects.append(something)
}
}
PFObject.saveAllInBackground(somethingObjects)
}
})
In this case, you create a number of something objects that are saved, but you might want to rename the "users" column to "user", since you only save one objectId per row.
I haven't worked with parse in a while, so there might be a way to optimize the number of API calls you have to make.

ios swift parse: get data out of parse and use them

I find only in the docs how the query can look like to select data.
As far as I see, there is only one way to collect 1 or many results:
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
for object in objects {
NSLog("%#", object.objectId)
}
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
What I cant figure out (as I am a beginner!) is how to access the object data. Lets say I have the fields "name", how can I get it? What is the right syntax? Especially if I have more than 1 result?
For just one result I would do:
var name = object["name"] as String
myArray.append(name)
Even that can't be right? To use "var xx = xx" within the loop?
And what do I do when I have more than one result?
Other thought:
Declaring the var name: String! before I do the query and then:
name = object["name"] as String
self.myArray.append(name)
Returns the error: Immutable vaue of type [String] only has mutating members named 'append'
What would be the correct way to "work" with the data the query returns?
Another question: as those querys are async, they finished later and the method is "done" much more earlier, this way my array with names is empty when the view is shown and I receive the data at a later stage. What is the best practice here to have all data available before the view is delivered to the device?
Thanks so much!!
You can use objectForKey on your object. So instead of using var name = object["name"] as String you can use:
for object in objects {
var name = object.valueForKey("name") as String
}
But the other parts are absolutely fine. You can create an array somewhere in you code and then add the objects to it. to do that, you can loop through your objects and than add the objects to your array. Like that:
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
for object in objects {
var name = object["name"] as String
myArray.append(name)
}
}
Because you can reuse the var name because every loop-element will be filled into the variable name and will erase the last value. But the array will get the new value appended. For example:
First loop. The value at the first index of your objects gets loaded into the object. For example with the value "John".
variable name's value is now the value of the object["name"] of the current loop. So name has the value John
Now you add the value to your array.
The second loop starts and the second element gets loaded inside object which now has the string Michael.
The Variable name's new value is now the value of object. So name's value is now Michael
and so on.

Resources