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];
Related
So, I am able to successfully perform a query of my parse database. This query matches the logged in user with all objects that have the logged in user in the key user. The object I'm querying has keys "email" "firstName" "lastName" "friendUserName" and "phoneNumber"
I would like to be able to store all the phone numbers into a mutable array named phoneNumbers. The code I am trying to use to do this is.
int i=0;
for (PFObject *object in objects) {
NSLog(#"%#", objects[i]);
phonenumber = object[#"phoneNumber"];
NSLog(#"%#", phonenumber);
[phoneNumbers[i] addObject:phonenumber];
NSLog(#"%#", phoneNumbers[i]);
i=i+1;
}
When I run this code the output for the code NSLog(#"%#", phoneNumbers[i]);
comes out a null both times. All other outputs are as expected.
The following should work for you:
NSMutableArray *phoneNumbers = [NSMutableArray new];
for (PFObject *object in objects) {
NSLog(#"%#", object);
// Assuming your phone numbers are stored as strings - use a different data type if necessary
NSString *phoneNumber = object[#"phoneNumber"];
NSLog(#"%#", phoneNumber);
[phoneNumbers addObject:phoneNumber];
}
NSLog(#"%#", phoneNumbers);
If you're just adding items to the end of a mutable array, you don't need to worry about the index of the items.
The place you were going wrong was when trying to add the object to your array. You were doing this:
[phoneNumbers[i] addObject:phonenumber];
Which is actually saying "fetch the object at index i in the phoneNumbers array, and tell it to add the phone number object". However, presumably your array is empty or not initialised, which means addObject will be sent to a nil object and so not have any effect. Instead, you simply need to tell the phoneNumbers array itself to add the 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.
// Posts a message to the backend database
- (void)postMessage:(NSString *)message
{
// return right away if message is nil or all whitespace
if (!message || ![[message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) {
return;
}
// Create new Message object and set relationships
PFObject *postedMessage = [PFObject objectWithClassName:#"Message"];
postedMessage[#"text"] = message;
PFRelation *tagRelation = [postedMessage relationForKey:#"tags"];
NSMutableArray *tags = [self generateTagsFromMessage:message];
NSLog(#"Number of Tags Found: %lu", (unsigned long)[tags count]);
[PFObject saveAllInBackground:tags block:^(BOOL succeeded, NSError *error) {
for (PFObject *tag in tags) {
// [tagRelation addObject:tag];
NSLog(#"%#", tag[#"text"]);
[tag setObject:postedMessage forKey:#"message"];
}
}];
[postedMessage saveInBackground]
}
I am using Parse as the back end for my application, but am having inexplicable difficulty creating a one-to-many relationship.
The idea is to make it so that users can post messages to the database with a tag on them, and the database stores Messages as one class, and Tags as another. The Message class has a "tags" field intended to store all tags in the message, and the Tag class has a "message" field intended to store the parent message of the tag. I thought I followed the tutorial of how to create a One-to-many relationship in Parse very well, but no matter what I cannot see the relationship on the Data Browser in Parse.
I have tried getting the relation to work by both simply setting the postedMessage as the newTag's message field, and also by creating a PFRelation * for the tags field of the message and adding tags to that. None of it works.
Does anybody know what my problem might be?
Thank you!
Edit
Specifically, the problem is that no relation is formed between the objects. By inspecting the data browser I can see that both the "tags" relational field of Message and "message" relational field of Tag remain blank after running this code.
An issue with your above code is that you're creating two new objects at once and triggering background saves on both at once.
For this to work, you need to save one side of the relationship first, then create the new objects, relate them and save them (in the block).
In your case you have a single message from multiple tags, so create the message and save it, then in the save block create the tags, add them to the message and re-save the message (it'll walk the tree looking for changes and save the new tags).
// Create new Message object
PFObject *postedMessage = [PFObject objectWithClassName:#"Message"];
postedMessage[#"text"] = message;
[postedMessage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// message saved, now create tags with link to message
PFRelation *tagRelation = [postedMessage relationForKey:#"tags"];
NSMutableArray *tags = [self generateTagsFromMessage:message];
NSLog(#"Number of Tags Found: %lu", (unsigned long)[tags count]);
for (PFObject *tag in tags) {
NSLog(#"%#", tag[#"text"]);
[tag setObject:postedMessage forKey:#"message"];
}
// now save the tags
[PFObject saveAllInBackground:tags block:^(BOOL succeeded, NSError *error) {
// now add the relationships
for (PFObject *tag in tags) {
[tagRelation addObject:tag];
}
// and save
[postedMessage saveInBackground];
}];
}];
Your code is almost correct, but you missed a few details. See my working version of your code re-written below. But first, read through the examples from Parse.
Take a look at this example from the Parse iOS documentation where they are setting the pointer relationship between two objects; a One-to-One relationship.
Neither object must have an objectId before creating the relationship.
// 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;
// This will save both myPost and myComment
[myComment saveInBackground];
What you are looking for is either a One-to-Many relationship array of pointers, which is outlined here and copied below for easy reference.
One object must have an objectId before creating the relationship
// Create Post
PFObject *newPost = [PFObject objectWithClassName:#"Post"];
// Set text content
[newPost setObject:[textView text] forKey:#"textContent"];
// Create relationship
[newPost setObject:[PFUser currentUser] forKey:#"author"];
// Save the new post
[newPost saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Dismiss the NewPostViewController and show the BlogTableViewController
[self dismissModalViewControllerAnimated:YES];
}
}];
OR you are looking for a One-to-Many relationship using the PFRelation object because; "you don't need to download all the Objects in a relation at once. This allows PFRelation to scale to many more objects than the NSArray of PFObject approach" . If this approach is what you are looking for, your re-written code below will work.
One object must have on objectId before creating the relationship
// Create new Message object and set relationships
PFObject *postedMessage = [PFObject objectWithClassName:#"TestClass"];
postedMessage[#"text"] = #"Hello World";
//example tags into array
PFObject *tag1 = [PFObject objectWithClassName:#"Tag"];
PFObject *tag2 = [PFObject objectWithClassName:#"Tag"];
PFObject *tag3 = [PFObject objectWithClassName:#"Tag"];
PFObject *tag4 = [PFObject objectWithClassName:#"Tag"];
NSArray *tags = [NSArray arrayWithObjects:tag1, tag2, tag3, tag4, nil];
//iterate the tags, add pointer for each
for (PFObject *tag in tags) {
//create a new tag-to-message pointer
[tag setObject:postedMessage forKey:#"message"];
}
//save all the tags and save the postedMessage
[PFObject saveAllInBackground:tags block:^(BOOL succeeded, NSError *error) {
if (!error) {
NSLog(#"created new postedMessage w/o relations :%#", postedMessage);
NSLog(#"saved tags with pointers :%#", tags);
PFRelation *tagRelation = [postedMessage relationForKey:#"tags"];
//iterate the tags, add relation for each
for (PFObject *tag in tags) {
//create a new postedMessage-to-tag relationship
[tagRelation addObject:tag];
}
//update the postedMessage
[postedMessage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
NSLog(#"updated postedMessage with relations :%#", postedMessage);
}
}];
}
}];
Edit: Adding the code I wrote; which works as you would expect. Once you get your relationships working there is one thing you will encounter using the Browser and that is an "object not found" error when you click on an object relationship from an object that is not directly related. A better way to explain that is to refer you to my example below.
I created a Message-to-Notification-to-User relationship -- think of it in any direction you prefer. When I access this relationship data using the Browser in CoreData and I click on the Users notifications relations column, I am directed to the users notifications. However; when I click on any of the message relations column, I received the, "object not found" error message. This is expected behavior and logically it makes sense. The User object doesn't care what messages its notifications contain, much like how the messages don't care who the user is. When the User object calls delete on a notification, auto-cleanup removes the messages without the User object needing to worry about them. When an object has no reference pointers and auto-cleanup is called, the object will be removed.
- (void)addNotificationWithData:(NSDictionary*)userInfo withCompletionHandler:(void(^)(BOOL isFinished))completionHandler {
PFObject *notification = [PFObject objectWithClassName:#"Notification"];
PFObject *msg = [PFObject objectWithClassName:#"Message"];
[msg saveEventually:^(BOOL succeeded, NSError *error) {
if (!error) {
PFRelation *notification_to_message = [notification relationForKey:#"messages"];
[notification_to_message addObject:msg];
[notification saveEventually:^(BOOL succeeded, NSError *error) {
if (!error) {
PFRelation *user_to_notification = [[PFUser currentUser]relationForKey:#"notifications"];
[user_to_notification addObject:notification];
[[PFUser currentUser]saveEventually:^(BOOL succeeded, NSError *error) {
if (!error) {
//add the notication to the notification(s)
[notifications addObject:notification];
NSLog(#"the current notifications are :%#", notifications);
//call the completion handler
if (completionHandler) {
completionHandler(YES);
}
}
}];
}
}];
}
}];
}
You've commented out the addObject call to add the object to the relation. You'll need to uncomment that line in order to actually add the object to the relation.
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
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);
}
}