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.


    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?


    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.


    I created some project from scratch for you. I think you miss some thing when you are creating your PFClass. I don't know. Whatever look my codes,

    This is the ViewController and it contains like Button Action ;

        import UIKit
    
        class ViewController: UIViewController {
    
        private let test = Test() // PFObject SubClass
        private let post = Post() // PFObject SubClass
    
        @IBAction func likeButton() {
    
            self.test.fromUser = PFUser.currentUser() // Current User
            self.test.likedUsers.append(Post().user  // In there your post should be a Subclass of PFObject too like your this Test class and it should be contains Owner property. So you can grab the Post owner PFUser or User Object ID.)
            self.test.saveEventually() {
                (let success, error) in
                if error == nil {
                    // Success
                } else {
                    print(error?.localizedDescription)
                }
            }
        }
    }
    

    In your AppDelegate init your ParseSDK and Register your new Parse subclasses.

       func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        Parse.setApplicationId("YOUR_API_KEY", clientKey: "YOUR_CLIENT_KEY")
        Test.registerSubclass()
        Post.registerSubclass()
    
        return true
    }
    

    Test Subclass ;

    import Foundation
    import Parse
    
    class Test: PFObject, PFSubclassing {
    
        // Your Properties
        @NSManaged var likedUsers: [PFUser]
        @NSManaged var fromUser: PFUser
    
        static func parseClassName() -> String {
            return "Test" // Your Class Name
        }
    }
    

    Post Subclass ;

        class Post: PFObject, PFSubclassing {
    
        // Your Properties
        @NSManaged var owner: PFUser
        @NSManaged var likeCount: NSNumber
    
        static func parseClassName() -> String {
            return "Post" // Your Class Name
        }
       }
    

    In my case it works fine. If you get an error please inform me.

    Have a nice coding.

    链接地址: http://www.djcxy.com/p/90572.html

    上一篇: Asp.Net核心1.0(aka Asp.Net 5)网站刷新与任何文件更改?

    下一篇: 如何防止Parse保存PFObject儿童?