PFRelation addObject does not add relation - ios

I'm trying to add an object to a relation in Parse, although the code gets exectued without any errors the relation does not appear in the backend, therefore the object was not saved.
PFObject *newContact = [PFObject objectWithClassName:#"Contact"];
[newContact saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
PFQuery *query = [PFQuery queryWithClassName:#"Trip"];
PFObject *trip = [query getObjectWithId:self.parseID];
PFRelation *rel = [trip relationForKey:#"contacts"];
[rel addObject:newContact];
contact.parseID = newContact.objectId;
}];
I've also checked if the PFObject trip is correct and I get back the desired object with the corresponding id. Also the key contacts is double-checked and correct.

The problem is that you never save the relation. You create the PFRelation within the block, add an object to it, and do nothing else with it... You never save it.
Instead, try fetching the trip object and creating the PFRelation outside of the save block, ex:
PFQuery *query = [PFQuery queryWithClassName:#"Trip"];
PFObject *trip = [query getObjectWithId:self.parseID];
PFObject *newContact = [PFObject objectWithClassName:#"Contact"];
PFRelation *rel = [trip relationForKey:#"contacts"];
[rel addObject:newContact];
[newContact saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
contact.parseID = newContact.objectId;
}];

Related

Why is my NSMutable not being populated correctly when inserting objects from another NSArray?

I have a PFQuery that gets the current participants of a particular event:
PFQuery *getcurrentparticipants = [PFQuery queryWithClassName:#"Event"];
[getcurrentparticipants selectKeys:#[#"Participants"]];
[getcurrentparticipants whereKey:#"objectId" equalTo:ObjectID];
[getcurrentparticipants findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *newParticipantsArray = [[NSMutableArray alloc]init];
if([objects[0] valueForKey:#"Participants"] == nil){ // If object retrieved in objects is null. If there are 0 participants
[newParticipantsArray addObject:PFUser.currentUser.username];
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}else{ // STEP 5
for(int i=0;i<objects.count;i++) {
[newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]];
}
[newParticipantsArray addObject:PFUser.currentUser.username];
NSLog(#"Part to upload %#", newParticipantsArray);
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
This is how the method works:
Create a PFQuery object
Query the Participants Class for an specific ObjectId
If no error, then we create a NSMutable array
If no participants are in Parse then we insert the current user as participant.
Else, insert all participants in the mutable array and add currentuser at the end of the array.
Then upload it again to Parse
My problem is in step 5:
When I perform the tasks in the else, the column in Parse looks like this :
[["Participant 1"],"Participant 2"]
But I would like to have it like this:
["Participant 1","Participant 2"]
What I have tried:
I tried things like putting the arrays like this. [newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]]; and similar combinations, of course without luck.
It’s hard to say for sure since I can’t see the structure of your data but are you sure the value held in
[[objects objectAtIndex:i] valueForKey: #“Participants”]
Is a single user and not itself an array of users? The plural key “participants” seems to suggest it’s an array of users which would also explain the result you’re getting.
If in fact the value returned for the "Participants" key is an array, you can add the objects in it to your mutable array by doing the following:
NSArray* participants = [[objects objectAtIndex:i] valueForKey:#"Participants"]
[newParticipantsArray addObjectsInArray:participants];
This uses the addObjectsInArray: method of NSMutableArray to add the objects from the old array into the new one.

Parse - Using Local Datastore with Relations

My situation includes having PFObjects that are connected with relations and I want to add to the relation using the local datastore.
So, I have a screen that creates a main object and saves it to the data store and server properly like so:
// Save User
PFObject *parseUser = [PFObject objectWithClassName:#"Users"];
parseUser[#"name"] = name;
parseUser[#"uuid"] = userUuid;
[parseUser saveEventually];
// Save List
PFObject *parseList = [PFObject objectWithClassName:#"Lists"];
parseList[#"name"] = newList.listName;
parseList[#"uuid"] = newList.listUuid;
[parseList saveEventually];
[parseUser pinInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// Create relation
PFRelation *relation = [parseUser relationForKey:#"listAccess"];
[relation addObject:parseList];
[parseUser saveEventually];
}
}];
[parseList pinInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// Create relation
PFRelation *relation = [parseList relationForKey:#"sharedWith"];
[relation addObject:parseUser];
[parseList saveEventually];
}
}];
So this part works. This took me a bit to figure out as well, but it works. Creates the User and List. Sets the relations of both then saves. Easy.
On to the problem area:
// Save List
PFObject *parseList = [PFObject objectWithClassName:#"Lists"];
parseList[#"name"] = newList.listName;
parseList[#"uuid"] = newList.listUuid;
[parseList saveEventually];
PFQuery *query = [PFQuery queryWithClassName:#"Users"];
[query fromLocalDatastore];
[query whereKey:#"uuid" equalTo:[User instance].userUuid];
[[query findObjectsInBackground] continueWithBlock:^id(BFTask *task) {
if (task.error) {
NSLog(#"Error: %#", task.error);
return task;
}
PFObject *user = [task.result objectAtIndex:0];
// Create relation
PFRelation *relation = [user relationForKey:#"listAccess"];
[relation addObject:parseList];
[user saveEventually];
PFQuery *relationQuery = [relation query];
[[relationQuery findObjectsInBackground] continueWithBlock:^id(BFTask *task) {
NSLog(#"resutls: %#", task.result);
return task;
}];
[parseList pinInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// Create relation
PFRelation *relation = [parseList relationForKey:#"sharedWith"];
[relation addObject:user];
[parseList saveEventually];
}
}];
return task;
}];
So what I am thinking I am doing here is grabbing the user from the local data store and adding another relation to it. But in reality, this actually is only saving the parseList to user relation. User doesn't get changed in the datastore or the server.
Other things I've tried:
Querying just the server
Pinning the User object with a name
EDIT: includeKey (This doesn't work as the include isn't a PFObject.)
Thanks for the help in advance.
Edit:
Looking through the query and relation grabs I get some weird data information.
So, first off, the relation is empty when searching through it like so:
estimatedData
0 - name - Bill Nye
1 - listAcces - no summary
value - PFRelation
knownObjects - 0 objects
2 - uuid - long string that is uuid
So this matches the server stuff, all besides the relation. But, when I query the relation and log it, I get this:
resutls: (
" {\n name = Okkkkk;\n sharedWith = \" Users>\";\n uuid = \"D8174D80-4F08-4B25-9B72-F8E00CF19DDB\";\n}"
)
So querying it, it is there, but it doesn't exist to the relation?

How to run a loop of parse queries?

I am making an app that registers users and allows them to add friends etc. So I have a LoginViewController where I retrieve the array of user's friends' objectIds when the login is successful. This function is called.
- (void) getFriendList
{
NSString * objectID = [PFUser currentUser].objectId;
NSLog(#"%#", objectID);
PFQuery *query = [PFUser query];
[query getObjectInBackgroundWithId:objectID block:^(PFObject *username, NSError *error) {
sharedClass.sharedInstance->retrievedFriends = username[#"friendsIds"];
friendListLoaded = YES;
[self getFriendsUsernames];
}];
Here i get an array object that contains object ids of all the friends this user has. As you can see when getFriendList is completed, it calls another function called GetFriendsUsernames. This function is supposed to retrieve profile pictures and usernames of those friends so I can populate the Friend List view later.
-(void) getFriendsUsernames
{
NSMutableArray * objectIDs = [[NSMutableArray alloc] initWithArray: sharedClass.sharedInstance->retrievedFriends];
PFQuery *query = [PFUser query];
int friendsCount = [objectIDs count];
for(int i = 0; i<=friendsCount;i++)
{
[query getObjectInBackgroundWithId:objectIDs[i] block:^(PFObject *username, NSError *error) {
[sharedClass.sharedInstance->friendsUsernames addObject:username[#"username"]];
[sharedClass.sharedInstance->friendsProfilePictures addObject:username[#"ProfilePicture"]];
}];
NSLog(#"%#", sharedClass.sharedInstance->friendsUsernames );
}
}
But this seems to be unsuccessful because nothing is logged on the console where it should log username of retrieved friend whenever one query gets finished. Am I doing this the right way? My array count is right so loop runs to the number of friends a user has. It also prints the username of first object in objectIds array if i put the logging command in the loop.
Your NSLog runs immediately after your query objects are queued for execution. Therefore, even if the code is running correctly (and I suspect it might not be), you'll never get the correct results you're after logged to the console.
If you have your Parse classes designed in a certain way, you could collapse this into 1 query by using the include method on PFQuery. This assumes that you've created an array of Pointers on your User object, named "friends". If you actually store the objectId (i.e., the string value) of each friend, this code won't work.
I'm not using Swift yet, so here's how I'd write this query in Objective-C:
- (void)getFriendList {
PFUser *currentUser = [PFUser currentUser];
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:currentUser.objectId];
[query includeKey:#"friends.username"];
[query includeKey:#"friends.ProfilePicture"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *user, NSError *error) {
if (error != nil) {
// Process the error
} else }
// You've got your user and the data you wanted
}
}];
}
Found it. Not the prettiest way out there but well it does what i want. gets me arrays of usernames and profile pictures of every objectID contained in array of friends.
NSMutableArray * objectIDs = [[NSMutableArray alloc] initWithArray: sharedClass.sharedInstance->retrievedFriends];
PFQuery *query = [PFUser query];
PFFile * imageFile;
//UIImage *profilePictureData;
int friendsCount = [objectIDs count];
for(int i = 0; i<friendsCount;i++)
{
PFObject * username = [query getObjectWithId:objectIDs[i]];
[sharedClass.sharedInstance->friendsUsernames addObject:username[#"username"]];
[sharedClass.sharedInstance->friendsEmailAdresses addObject:username[#"email"]];
//NSLog(#"%#",username[#"ProfilePicture"]);
imageFile = [username objectForKey:#"ProfilePicture"];
NSData *imageData = [imageFile getData];
UIImage *imageFromData = [UIImage imageWithData:imageData];
[sharedClass.sharedInstance->friendsProfilePictures addObject:imageFromData];
NSLog(#"%#", sharedClass.sharedInstance->friendsUsernames );
}

Query Custom User Class Parse

I'm trying to query three columns that I created in my user class with parse. I successfully saved them to the user class. But I'm having difficulty query these three columns onto a label. I really don't know where to start with the query, I want it to be user specific of course, so user A gets his name, bio, gender, ect. And name is the user's full name, not their username.
This is my code to save the objects:
- (IBAction)save:(id)sender {
PFUser *profile = [PFUser currentUser];
[profile setObject:_name.text forKey:#"Name"];
[profile setObject:_bio.text forKey:#"Bio"];
NSString *var = [[NSUserDefaults standardUserDefaults] objectForKey:#"MyKey"];
[profile setObject:var forKey:#"Gender"];
[profile saveInBackground];
}
For the query I really don't know, this is where I need help. So if you have any suggestions I would appreciate it. Thank you!
Try this one
PFQuery * query = [PFQuery queryWithClassName:#"ClassName"];
[query whereKey:#"Name" equalTo:#"XYZ"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// objects : PFObjects
// you can filter out the data you want and print into lable
}];

Parse Set Object To UILabel

So this is my first time working with parse I have simple application which creates a user and allows them to sign in. I'm currently working on something that will allow them to fill in details about themselves using PFObjects, I don't have a problem with that. My issue is I need to get user specific data print out on an UILabel.
Here's my code creating a PFObject this works fine:
- (IBAction)saveProfile:(id)sender {
PFObject *profile = [PFObject objectWithClassName:#"Profile"];
[profile setObject: self.name.text forKey:#"name"];
[profile setObject:[PFUser currentUser] forKey:#"author"];
[profile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
NSLog(#"No Error");
}else NSLog(#"Yeah you got an error bro");
}];
}
Here's what I'm having an issue with my PFQuery:
PFQuery *query = [PFQuery queryWithClassName:#"Profile"];
[query whereKey:#"name" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSLog(#"Success");
self.nameLabel.text = [NSString stringWithFormat:#"%#", query];
}
else {
NSLog(#"Fail");
}
}];
}
So basicly I want the user to enter their name have it save, and have that specific user's name print out on a label. This is as far as I got so, if you have any suggestions I'm all ears. Thanks!
Updated:
PFQuery *query = [PFQuery queryWithClassName:#"Profile"];
[query whereKey:#"author" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSLog(#"Success");
self.nameLabel.text = object[#"name"];
}
else {
NSLog(#"Fails");
}
}];
}
Looks like you're setting the PFObject "name" key to self.name.text, but in your PFQuery you're querying the class and asking for values where "name" is equal to [PFUser currentUser]. You're setting the value one way then using a completely different value in an attempt to query the object; so getFirstObjectInBackgroundWithBlock isn't returning an object since there's no Profile object where "name" equals [PFUser currentUser].
I think you're confusing your "name" and "author" properties...
Edit (in response to your comment):
OK, so in saveProfile: you're creating a PFObject where you're setting "name" to the name string and "author" to the user's PFUser object. When you're using whereKey: to perform a query on this class in an effort to retrieve the object using getFirstObjectInBackgroundWithBlock:, the result returned to you will be the full first PFObject where the object associated with the key is the one specified in the whereKey: criteria. So you don't have to specify which key of the PFObject you want to read before performing getFirstObjectInBackgroundWithBlock:. The query returns the whole object -- name, author, etc.
So in order to access the returned PFObject's "name" within the query block, change:
self.nameLabel.text = [NSString stringWithFormat:#"%#", query];
to (dispatch_aync added to force the label change onto the main thread):
dispatch_async(dispatch_get_main_queue(),^{
self.nameLabel.text = object[#"name"];
});
This line
[query whereKey:#"name" equalTo:[PFUser currentUser]];
only works if the "name" column is a pointer or relation to the User class. If it is the username you're after, you need to use
[query whereKey:#"name" equalTo:[PFUser currentUser][#"username"]];
But why are you querying for the object you just saved?
Your last, updated example should work for your need.
You could fire up a query like the one below
PFQuery *query = [PFQuery queryWithClassName:#"Profile"];
NSString *nameStr = [NSString stringWithFormat:#"%#",[[PFUser currentUser]objectForKey:#"name"]];
[query whereKey:#"name" containsString:nameStr];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error)
{
if (!error)
{
/*object contains all columns and you need only key to obtain value*/
nameLabel = [NSString stringWithFormat:#"%#",object[#"job"];
.
.
.
}
else
{
NSLog(#"Error: %#", [error localizedDescription]);
}
}];
Now you will have object of current user along with its all details. Also you could do a thing, i.e., At time of user filling up profile details save it in a dictionary as below :
NSDictionary *signupDetail = [NSDictionary dictionaryWithObjectsAndKeys:self.userRegisterTextField.text, #"username",
self.nameTextField.text, #"Name",
[ResponseDict objectForKey:#"sessionToken"] ,#"sessionToken",
[ResponseDict objectForKey:#"objectId"], #"objectId",
nil];
//ResponseDict is dictionary you get in response for successful signup.
Then you could store it using [NSUserDefaults standardUserDefaults] so you will have all info of user at one place and call it wherever needed.

Resources