Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent Parse from saving PFObject children's?

I'm facing a very common issue with Parse and iOS.

I have a class POST with the following structure:

  • text(String)
  • Image (PFFile)
  • LikesUsers (Array of String)
  • LikesCount (Int)
  • From (Pointer to User who posted it)

and if the user (already logged in) likes a post. I'm just incrementing the likes and the add Objectid of the user to the array

For example : User-2 likes User-1's Post.

PostObject.incrementKey("Likes")
PostObject.addObject((PFUser.currentUser()?.objectId)!, forKey: "LikesUsers")
PostObject.saveEventually()

The issue is here. I can't save a the PostObject as long as it has a pointer to another user (than the logged in) I'm getting the error :

User cannot be saved unless they have been authenticated via logIn or signUp

So how to prevent from saving the ParseObject Children ("From")

I don't want to use a CloudCode, I want to keep it simple and to Use SaveEventually for a better user experience.

like image 707
Stranger B. Avatar asked Mar 10 '16 15:03

Stranger B.


2 Answers

From the parse.com forums:

When you save an object with a pointer, Parse looks at the entirety of the object and saves the fields that are "dirty" (changed since last save). It also inspects all the objects being pointed to by pointers. If your object has a pointer to PFUser and the instance of the PFUser is "dirty", then Parse will save the object and then attempt to save the instance of the PFUser as well. My hypothesis is that somewhere in your code you are setting a value on a user and then not saving the user. This, I suspect, is the source of your error. Go through your code and look for anywhere you set a PFUser instance and make sure there's a corresponding save. If you can't find it at first glance, be sure to check all blocks and ensure that you are not asynchronously dirtying a user and subsequently trying to save it.

Are you trying to make any changes to the user that the post was made by?

like image 70
Jacob Avatar answered Sep 28 '22 22:09

Jacob


Short answer:

Try to set from field as:

let author = PostObject["from"] as! PFObject
PostObject["from"] = PFObject(withoutDataWithClassName: "_User", 
    objectId: author.objectId)

This is not tested.

But I suggest you to change the architecture

I was facing the same problem and finally decided to move likes field to User class instead and make it Relation<Post> type. Parse documentation says that Relation type is better for keeping many objects. Since LikesUsers is an array of objects, the performance may drop significantly if a post will get many likes: the app will download all the users liked this post. See this for more info: https://parse.com/docs/ios/guide#objects-relational-data

Here is how my class structure looks like (simplified):

User:

  • postLikes (Relation)

Post:

  • author (PFObject)
  • likesCount (Int)

I also moved like/dislike logic to CloudCode function:

/// The user liked or disliked the post.
Parse.Cloud.define("likeDislikePost", function(request, response) {
    var userProfileId = request.params.userProfileId
    var postId = request.params.postId
    // Shows whether the user liked or disliked the post. Bool value
    var like = request.params.like
    var query = new Parse.Query("_User");
    query.equalTo("objectId", userProfileId);
    query.first({
      success: function(userProfile) {
            if (userProfile) {
              var postQuery = new Parse.Query("Post");
              postQuery.equalTo("objectId", postId);
              postQuery.first({
                  success: function(post) {
                        if (post) {
                            var relation = userProfile.relation("postLikes");
                            if (like) {
                              relation.add(post);
                              post.increment("likesCount");
                            }
                            else {
                              relation.remove(post)
                              post.increment("likesCount", -1);
                            }
                            post.save();
                            userProfile.save();

                            console.log("The user with user profile id " + userProfileId + " " + (like ? "liked" : "disliked") + " the post with id " + postId);
                            response.success();
                        }
                        else {
                            response.error("Unable to find a post with such ID");
                        }         
                  },
                  error: function() {
                    response.error("Unable to like the post");
                  }
              });
            }
            else {
                response.error("Unable to find a user profile with such ID");
            }         
      },
      error: function() {
        response.error("Unable to like the post");
      }
  });
});

Works just fine.

like image 22
Andrey Gordeev Avatar answered Sep 29 '22 00:09

Andrey Gordeev