Parse - saving related objects - ios

I'm using Parse for the back end in my project.
As you would imagine there are quite a few relations in the data model. A lot of the time I create a "parent" object and all of its "children" at the same moment and save them all to Parse.
Now, when doing this is it necessary to save the children individually? The same for files etc...
First example - Adding an avatar to a user object
UIImage *image = // image from camera
NSData *pngData = UIImagePNGRepresentation(image);
PFFile *imageFile = [PFFile fileWithData:pngData];
[[PFUser currentUser] setObject:imageFile forKey:"avatar"];
OK, so on the device I can reference the #"avatar" key on the user and get the avatar file. But how should this be saved to Parse?
If I do...
[[PFUser currentUser] saveInBackground];
Will this save the new file that was added? Or do I need to save the file first and wait for this to succeed before then adding it into the user object and then saving the user object?
Second example
Creating a tree of objects...
PFObject *family = [PFObject objectWithClassName:#"Family"];
[family setObject:#"Smith" forKey:#"familyName"];
PFObject *person1 = [PFObject objectWithClassName:#"Person"];
[person1 setObject:#"Bob" forKey:#"name"];
PFObject *person2 = [PFObject objectWithClassName:#"Person"];
[person2 setObject:#"Alice" forKey:#"name"];
PFObject *person3 = [PFObject objectWithClassName:#"Person"];
[person3 setObject:#"Chris" forKey:#"name"];
[family setObject:#[person1, person2, person3] forKey:#"members"];
How would I save this collection of objects?
Can I just do [family saveInBackground];?
Or do I have to go through a process of saving every Person object first and checking that it worked before saving the family object?

As long as the relationship between parent and child is Pointer, you don't have to save the child first. PFRelation works differently, but a save on the parent object will also save children related as pointers. This is true for Cloud Code, and I am pretty sure it holds true for the device as well.
Some details in this answer: https://www.parse.com/questions/cloud-code-efficient-hierarchy-saving

Yes that will do...in fact there is a wide range of methods for saving the contents
Saving an Object to Parse
– save
– save:
– saveInBackground
– saveInBackgroundWithBlock:
– saveInBackgroundWithTarget:selector:
– saveEventually
– saveEventually:
Saving Many Objects to Parse
+ saveAll:
+ saveAll:error:
+ saveAllInBackground:
+ saveAllInBackground:block:
+ saveAllInBackground:target:selector:
Refer Here for more

Related

Why is a PFRelation saved by saving the PFUser object?

so I am having trouble understanding how a PFRelation saves, I have this code:
PFUser *user = [PFUser currentUser];
PFRelation *relation = [user relationForKey:#"likes"];
[relation addObject:post];
[user saveInBackground];
Why is it that updating the user i.e. [user saveInBackground] updates the PFRelation field "likes" for that user? Does this use a single API call or does [relation addObject: post]; also require an API call?
Any help would be greatly appreciated.
Thanks
In your case 1 API request is used under circumstances.
Take a look at PFObject [straight from Parse.com]:
// Create the post
PFObject *myPost = [PFObject objectWithClassName:#"Post"];
myPost[#"title"] = #"I'm Hungry";
myPost[#"content"] = #"Where should we go for lunch?";
// Create the comment
PFObject *myComment = [PFObject objectWithClassName:#"Comment"];
myComment[#"content"] = #"Let's do Sushirrito.";
// Add a relation between the Post and Comment
myComment[#"parent"] = myPost;
Here you are setting attributes or properties to the PFObject but nothing ever happens until you save it, you can do anything to the object like change it, update it, doesn't matter, but backend wise, it won't update unless you tell it to which is where save comes in play :
[myComment saveInBackground];
In short, you can add relations, pointers and numerous parameters all day long, but nothing happens until you tell it to happen : [saveInBackground];
Because you made it a direct correlation to user it saves it to that user because you told it to. Because you specified a relation to the user, once you save the user properties the relation will also be saved. However, this doesn't create more API requests.

Multiple image in parse relationship

i'm trying to create relationship between multiple images to an object. I keep getting following error:
errorcode
All objects in a relation must have object ids
What am i doing wrong? i've tried to follow documentation and adjust it to my example.
relationship code.
homeObject = [PFObject objectWithClassName:#"Homes"];
homeObject[#"userId"] = [PFUser currentUser].objectId;
for (int i = 0; i < [self.assets count]; i++) {
PFObject *object = [PFObject objectWithClassName:#"homeImages"];
ALAsset *asset = [self.assets objectAtIndex:i];
ALAssetRepresentation *representation = asset.defaultRepresentation;
UIImage *fullResolutionImage =
[UIImage imageWithCGImage:representation.fullResolutionImage
scale:1.0f
orientation:(UIImageOrientation)representation.orientation];
NSData *imgData= UIImageJPEGRepresentation(fullResolutionImage,0.0);
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:imgData];
object[#"Image"] = imageFile;
[object saveInBackground];
PFRelation *relation = [homeObject relationforKey:#"imageId"];
[relation addObject:object];
}
[homeObject saveInBackground];
You need to save the file and wait for it to finish saving (in the background), then you can safely attach it to your homeImages object. Next you need to save that object and wait for it to finish saving (in the background), then you can finally add it to the relation and save the parent object.
Since there's no in-built promises support (there are some 3rd party ones you might want to look at), you'll have to use nesting.
Consider creating an array of PFFile objects and using saveAllInBackground:block:. In the block create the homeImages objects and put them in an array, calling save-all again. Lastly in that completion block add them all to the relation and call saveInBackground on your homeObject.

Parse iOS SDK: How to copy objectId while creating a PFObject

Scenario = I have a PFQueryTableViewController that will display messages that users send to each other. To create a relationship between the conversations, I saved a string to the object called "messageID". I want "messageID" to be an exact copy of the first message's "objectId" in order for the conversation to make chronological sense and I will 'orderByAscending' from the "createdAt" perimeter.
Question = How do I set (copy) the "objectId" from the first message?
The idea I am trying to do is this...
PFObject *message = [PFObject objectWithClassName:#"Message"];
[message setObject:[NSString stringWithFormat:#"%#", self.messageTextView.text] forKey:#"message"];
[message setObject:[PFUser currentUser][#"userID"] forKey:#"senderID"];
[message setObject:self.selectedFriendID forKey:#"receiverID"];
//RIGHT HERE
[message setObject:message.objectId forKey:#"messageID"];
[message saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
}
The idea is this way all of the messages once queried in the PFQueryTableViewController will all belong together and in order because they all have the same "messageID".
This gives me an error 'Can't use nil for keys or values on PFObject. Use NSNull for values.'
If anyone could point me in the right direction so I can accomplish this I will love you.
The error message tells you what is happening - you are trying to store a nil value in the messageID key, which isn't permitted.
The reason it is happening is the objectId is only assigned once the object is saved - a newly allocated object doesn't have a value yet.
So, you can either wait until the saveInBackgroundWithBlock has completed and retrieve the objectId then or you can use an alternative ID.
I would suggest you change your messageID column to a String and use a UUID. You can then create a new UUID before you save the first message in the conversation. You can use
NSString *messageID=[[NSUUID UUID] UUIDString];

How can I save an array using Parse?

I am struggling to use Parse to save an array of data - or at least I am finding I can't verify that my data is being saved.
I have a PFObject (journey) and I want to update it with my location (a string in the format: #"{lat, long}").
Eventually though I want to update and save my location every 10-30 seconds meaning I don't want to create a new object each time rather I want to add an array to the object and then update the array each time I have a new location.
Currently my code looks like this:
- (IBAction)startJourneyButtonPressed:(id)sender {
PFObject * journey = [PFObject objectWithClassName:#"Journey"];
journey[#"company"] = _companyNameTextField.text;
journey[#"destination"] = _destinationTextField.text;
[journey saveInBackground];
NSData * faceImageData = UIImagePNGRepresentation([_faceButton imageForState:UIControlStateNormal]);
PFFile * faceImageFile = [PFFile fileWithName:#"faceImage.png" data:faceImageData];
[faceImageFile saveInBackground];
NSData * carImageData = UIImagePNGRepresentation([_carButton2 imageForState:UIControlStateNormal]);
PFFile * carImageFile = [PFFile fileWithName:#"carImage.png" data:carImageData];
[carImageFile saveInBackground];
journey[#"faceImage"] = faceImageFile;
journey[#"carImage"] = carImageFile;
[journey saveInBackground];
NSMutableArray * locationArray = [NSMutableArray new];
[locationArray addObject:[self getCurrentLocationString]];
journey[#"location"] = locationArray;
[journey saveInBackground];
}
The strings are saving fine and so are the images but the dataBase doesn't seem to be getting uploaded with the array.
Here is a screenshot of my database:
I am expecting to see a "location" heading but obviously it isn't appearing.
Reading up on other forums it says that I have to have an object in JSON format for this to work but as I am only storing Strings and I am not receiving and JSON errors I don't think this is the issue.
Any help would be very appreciated
Probably it is saving your array, you should just manually in your database add column with name "location" (do not forget to set it as array type) to see the values.

Need to loop through an NSMutableArray that is stored inside an array in Parse.com database

I have an NSMutableArray object that contains NSString objects. The mutable array object is called _usersToAddToFriendsList. When I am done adding NSString objects to it, I run the following code for Parse.com:
[PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"username" equalTo:_userSubmittedUsername];
[query getObjectInBackgroundWithId:_objectId block:^(PFObject *object, NSError *error) {
object[#"friends"] = _usersToAddToFriendsList;
[object saveInBackground];
}];
The most important part is this statement: object[#"friends"] = _usersToAddToFriendsList;
This takes my NSMutable array and places it inside the "friends" column in my Parse.com database. When I created this column in Parse, I set it's "type" to "array".
All of this works perfectly and I can see that the contents of my mutable array have been placed in the "friends" column in the database.
Here's the problem though. Later on, when I query for the "friends" column I use the method call of findObjectsInBackgroundWithBlock and this places the object returned by the server into an NSArray object called "objects".
My problem is that this means that I now have an array called "objects" that contains my original mutable array with my NSStrings.
I need to loop through the string values of my original NSMutableArray, but I don't know how to get to them because my original mutable array is contained inside this new NSArray returned by the server.
I have tried playing around with various multi-dimensional array solutions that people provided in my previous stack overflow question: Need to loop through an array that is inside of another array
But it never works and it always crashes in xcode and says:
-[PFUser countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
This makes me think that this problem needs a solution specifically tailored to how Parse.com works.
I just want to be able access and loop through the string values that I originally stored in my NSMutableArray.
My problem is that this means that I now have an array called "objects" that contains my original mutable array with my NSStrings.
Not quite. It means you have an array of user objects, each of which contains an array of friends (strings).
Try something like
for (PFUser *user in objects) {
NSArray *friends = [user objectForKey:#"friends"];
for (NSString *friend in friends) {
NSLog(#"Friend is '%#'", friend);
}
}

Resources