Strange issue with Parse.com with key not being included - objective-c

I'm having an issue very similar to this one
Basically I'm using Parse.com to load some objects, which have PFUser pointers, and then I'm also using includeKey to include those PFUsers, here's the code...
PFQuery *query = [PFQuery queryWithClassName:#"GameVillageObject"];
[query whereKey:#"region" equalTo:[NSNumber numberWithInt:region]];
[query includeKey:#"pfUser"];
query.limit = 100;
[sharedInstance requestSentWithDesc:#"Get all village objects in region"];
[query findObjectsInBackgroundWithBlock:^(NSArray *PUObjects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d village objects from server.", PUObjects.count);
if(PUObjects.count > 0)
{
villageObjects = PUObjects;
for (int i=0; i<[villageObjects count]; i++)
{
PFObject *villageItem = [villageObjects objectAtIndex:i];
PFUser *user = [villageItem objectForKey:#"pfUser"];
NSString *userName = [NSString stringWithFormat:#"%#",[user objectForKey:#"username"]];
NSLog(#"User name is: %#.", userName);
}
[self setupVillageList];
[sharedInstance centerImage:marketItemsContainer xChoice:YES yChoice:NO];
}
} else {
// Log details of the failure
NSLog(#"Getting village objects Error: %# %#", error, [error userInfo]);
}
}];
Now for some reason, everynow and again, maybe 1 time out of 10, the game is crashing, with this error
Key "username" has no data. Call fetchIfNeeded before getting its
value.
Although I can't tell if it's crashing when I try to use "username" above, or a little later when I try to use "username" but either way, I don't get why most of the time it has no problem including those extra objects and then a few times it doesn't. Any ideas?

It sounds as though the associated user is not being loaded, but may be cached in most instances (and thus available). I can think of two ways to resolve this.
1) Call fetchIfNeeded as suggested by the error message:
PFQuery *query = [PFQuery queryWithClassName:#"GameVillageObject"];
[query whereKey:#"region" equalTo:[NSNumber numberWithInt:region]];
[query includeKey:#"pfUser"];
...
PFObject *villageItem = [villageObjects objectAtIndex:i];
PFUser *user = [villageItem objectForKey:#"pfUser"];
[user fetchIfNeeded];
NSString *userName = [NSString stringWithFormat:#"%#",[user objectForKey:#"username"]];
NSLog(#"User name is: %#.", userName);
2) Specify pfUser.username in the includeKey: call
[query whereKey:#"region" equalTo:[NSNumber numberWithInt:region]];
[query includeKey:#"pfUser.username"];
query.limit = 100;
...
This should tell Parse to load username when the query is executed.

I had this same problem.
Here is what I did:
call fetchIfNeeded on the pointer.
ex.
[someObject.user fetchIfNeeded]
this will get the information about the user that you need.
If you need a PFFile from the user object you can do the following.
//if you run this code in cellForRowAtIndexPath and want to update the profile image for the user you can do the following.
User *user = (User *)someObject.user;
[user.profileImage getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:data];
cell.userProfileImage.image = image;
}else{
NSLog(#"Print error!!! %#", error.localizedDescription);
}
}];

Related

What is causing this logic error?

Here is my code and here is the output... I do not understand why my if statement above the log is allowing this to happen...This if statement that is nested inside of the for loop should not allow output whenever the fullname is the same the description...
Firebase *firebase = [[Firebase alloc] initWithUrl:[NSString stringWithFormat:#"%#/Recent", FIREBASE]];
FQuery *query = [[firebase queryOrderedByChild:#"groupId"] queryEqualToValue:groupId];
[query observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
//Is group
if ([snapshot.value[#"type"] isEqual: #"group"]){
self.title = snapshot.value[#"description"];
}
//Is individual
else{
NSString *senderId = snapshot.value[#"userId"];
PFQuery *query = [PFQuery queryWithClassName:PF_USER_CLASS_NAME];
[query whereKey:#"objectId" equalTo:senderId];
query.limit = 1;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// Do something with the found objects
for (PFObject *object in objects) {
NSString *userName = [[PFUser currentUser]fullname];
if (object[#"fullname"] != userName){
self.title = object[#"fullname"];
NSLog(#"You're talking to: %#", object[#"fullname"]);
NSLog(#"Logged in user: %#", userName);
}
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
}];
Heres the log as well
2016-03-12 18:48:09.844 Gibr[34128:1436940] You're talking to: Testerten
2016-03-12 18:48:09.844 Gibr[34128:1436940] Logged in user: Travis Tubbs
2016-03-12 18:48:09.845 Gibr[34128:1436940] You're talking to: Travis Tubbs
2016-03-12 18:48:09.845 Gibr[34128:1436940] Logged in user: Travis Tubbs
The problem that you are using != to compare two strings. But that's not what it's for; it's for comparing objects. The two string variables are not the same object; they are two different objects, two different variables.
If you want to know whether two string variables have the same value as strings, use isEqualToString:.
if (![object[#"fullname"] isEqualToString: userName])

Parse: Query to change labels text

I can currently query a parse class, but can't figure out how to change a labels text if the returned values match the query. I am relatively new to objective C and Parse so my knowledge on the subject is little. My query looks like this (with the text of what i'm trying to achieve underneath).
PFQuery *FTQ0 = [PFQuery queryWithClassName:#"Class1"];
[FTQ0 whereKey:#"Location" equalTo:#"The Shop"];
//Label.text = query (object?)
Thanks in advance.
Here is a solution. You have to be careful though, because there might be many objects that have key Location and equal to The Shop. This is why parse is returning an array of objects. In this case, I pick the first object in the array and display it.
PFQuery *query = [PFQuery queryWithClassName:#"Class1"];
[query whereKey:#"Location" equalTo:#"The Shop"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
Label.text = [NSString stringWithFormat:#"%#", [[objects firstObject] objectForKey:#"WHATEVER YOU WANT TO DISPLAY EX. NAME, LOCATION..."]]
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
For more information please visit https://parse.com/docs/ios/guide
Something like this :
PFQuery *query = [PFQuery queryWithClassName:#"Class1"];
[query whereKey:#"Location" equalTo:#"The Shop"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error && object) {
// Do your stuff
Label.text = [[object objectForKey:#"YOUR KEY"] stringValue];
} else {
// Error or null object
}
}];
In this example the query return only the first object.
Thanks so much for your quick and helpful response! This was an issue that was troubling me allot! The code provided works!
Thanks!

How to use the value of a pfobject outside of the query

I'm a first time developer and I'm starting out in objective C and parse. I understand how to post data to a pfclass, I understand how to retrieve the data in a pfquery, but what I am unclear on is how to take that data from a query and use it elsewhere in the app.
The example I am using is related to a survey app I am creating.
When a user selects a survey answer
The app posts the category, the question, and the answer that was chosen by the user to my first pfclass.
NSString *quizString = [NSString stringWithFormat:#"%i",CategoryLoaded];
NSString *questionString = [NSString stringWithFormat:#"%i",QuestionSelected];
NSString *answerString = [NSString stringWithFormat:#"b"];
PFObject *newAnswerVote = [PFObject objectWithClassName:#"QuizData"];
newAnswerVote[#"quiz"] = quizString;
newAnswerVote[#"question"] = questionString;
newAnswerVote[#"answer"] = answerString;
[newAnswerVote saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded == YES){
}
else{
}
}
Then I go to my second class, and query the exact result this user has selected and add an additional increment to it's tally. This way I have a record of how many times this answer has been chosen by all users.
PFQuery *query = [PFQuery queryWithClassName:#"AnswerStorage"];
[query whereKey:#"Quiz" equalTo: [NSString stringWithFormat:#"%i",CategoryLoaded]];
[query whereKey:#"Question" equalTo: [NSString stringWithFormat:#"%i",QuestionSelected]];
[query whereKey:#"Answer" equalTo: [NSString stringWithFormat:#"b"]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *Total, NSError *error){
[Total incrementKey:#"Total"];
[Total saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded == YES){
}
else{
};
Here's where I am stuck. What I need is for the app to determine the total votes for each of 4 possible answers in this specific question and survey. Then I want the app to determine which answer has the most votes, and then have two separate outcomes based on whether the user picked the most voted answer or not.
Now, I'm not looking for any one to spell all those functions out in code for me, but if you could help me understand how I take the result of a PFQuery and turn it into an integer value that the app can understand once the query is complete I think I can get the rest of it figured out.
Thanks for any help!
Edit -----------
Got it all solved. thank you!
If I understand you right, you had to do the following:
After the value of Total has been incremented, you had to find the answer with the highest Total.
You could use something like:
PFQuery *query = [PFQuery queryWithClassName:#"AnswerStorage"];
[query whereKey:#"Quiz" equalTo: [NSString stringWithFormat:#"%i",CategoryLoaded]];
[query whereKey:#"Question" equalTo: [NSString stringWithFormat:#"%i",QuestionSelected]];
[query whereKey:#"Answer" equalTo: [NSString stringWithFormat:#"b"]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *total, NSError *error){
[total incrementKey:#"Total"];
NSUInteger myVote = total[#"Total"];
[total saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded == YES){
PFQuery *queryForHighestVote = [PFQuery queryWithClassName:#"AnswerStorage"];
[queryForHighestVote orderByDescending:#"Total"];
[queryForHighestVote getFirstObjectInBackgroundWithBlock:^(PFObject *firstAnswerStorageObject, NSError *error){
if (nil != error) {
// query failed
} else {
NSUInteger highestVote = firstAnswerStorageObject[#"Total"];
if (myVote == highestVote) {
} else {
}
}
}
else {
// Increment not saved
}
PS: I changed Total to total, since variables usually start with LC characters, while classes start with UC characters.

PFQuery Array Issue

I have the following code which is loading a list of users from Parse into an array named users:
PFUser *currentUser = [PFUser currentUser];
NSString *currentUserUni = currentUser[#"university"];
//Parse Query
//-------------------------------------------
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"university" equalTo:currentUserUni];
[query setLimit:50];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
[users removeAllObjects];
[users addObjectsFromArray:objects];
}];
But if I try to call users outside of that closing bracket at the end, it is nil. Does anyone know
1)what would be causing this?
and
2)How can I get the values into an array that can be accessed outside of that closing bracket?
1) findObjectsInBackgroundWithBlock retrieves objects in the background, meaning, it happens asynchronously - the method runs while other processes are running instead of sequentially. So say you have your current code:
Note I would, supply an error check before messing with your users array.
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"university" equalTo:currentUserUni];
[query setLimit:50];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
[users removeAllObjects];
[users addObjectsFromArray:objects];
} else {
// show your alert message
}
}];
... some code that comes after the block ...
The "... some code that comes after the block ..." portion of your 'class' or 'method' will be running while your searching for the objects in the background...
2) What needs to be done to solve your issue is add the __block keyword to your users array.
Heres a reference to an answer in stack overflow that further explains the __block keyword.
__block keyword
takeing jsetting32 answer and changing it
i think you should take removeAllObjects out of the loop
[users removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"university" equalTo:currentUserUni];
[query setLimit:50];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
[users addObjectsFromArray:objects];
} else {
// show your alert message
}
}];
... some code that comes after the block ...

How to delete image from Parse?

I want to delete / overwrite a photo to Parse for a given user. Will you help me? Because I wrote the code that I always return error 102
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"User" equalTo:[PFUser currentUser]];
[query includeKey:#"Foto"];
[query whereKeyExists:#"Dev.png"]; //Change this condition to fetch the row.
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object1, NSError *error)
{
if (!error) {
NSLog(#"Successfully retrieved: %#", object1);
//This might work as I searched for this deleting image but there is no method to do so.
//So a way would be is to get that object and then setting nil value in it's place.
//Else if u want to delete the entire row then u could simply type, [object deleteInBackground]
object1[#"Foto"] = [NSNull null];
}
else
{
NSLog(#"Error: %#", [error localizedDescription]);
}
}];
This is my table:
Change this line:
[query whereKey:#"User" equalTo:[PFUser currentUser]];
With below line:
[query whereKey:#"username" equalTo:[PFUser currentUser]];