mercredi 6 janvier 2016

parse.com 'retweet' pattern is too verbose

So, I have been tasked with implementing a 'retweet'-like functionality in an app (iOS, Swift), using Parse.

This has been asked before here, but that's a) pretty high-level and b) I get the task at hand - I am not necessarily asking for help on the architectural decisions, though if it seems that I am obviously missing something, I'm happy to accept feedback.

My app has CAUSES which are each created by a USER. There is also a FOLLOW table with a TO and a FROM user. So to start, I simply query the CAUSES table, with the constraint that the USER who posted should match the objectId of a TO user (where the current user is the FROM user) in the FOLLOW table. More succinctly:

    let getFollowedUsersQuery = PFQuery(className: Constants.kParseClassFollowers)
    getFollowedUsersQuery.whereKey(Constants.kParseFieldFromUser, equalTo: PFUser.currentUser()!)

    let causesQuery = PFQuery(className: Constants.kParseClassCauses)
    causesQuery.whereKey(Constants.kParseFieldFromUser, matchesKey: Constants.kParseFieldToUser, inQuery: getFollowedUsersQuery)
    causesQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
        if let causes = objects {
            for cause in causes {
              // populate the tableview cells, etc.
            }
        }
    })

Now I have all the causes from users that i follow... that's all pretty standard.

Here's where it gets tricky.
Each CAUSE also has a Relation called SUPPORTERS. Now I need to architect a way to get all the CAUSES from people that I do not follow, but which have in their list of supporters a user that I follow.

I have yet to find an elegant solution, though I am approaching a 'brute force' one, and it is so cumbersome and verbose that the better half of my programmer's brain is screaming at me like Susan Powter...

Here's a sample:

        let retweetQuery = PFQuery(className: Constants.kParseClassCauses)
        retweetQuery.orderByDescending(Constants.kParseFieldCreatedAt)
        retweetQuery.whereKey(Constants.kParseFieldFromUser, notEqualTo: PFUser.currentUser()!)
        retweetQuery.whereKey(Constants.kParseFieldFromUser, doesNotMatchKey: Constants.kParseFieldToUser, inQuery: getFollowedUsersQuery)
        retweetQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
            if let causes = objects {
                for cause in causes {
                    let supporterRelations = cause.relationForKey(Constants.kParseClassSupporters)
                    let supporterQuery = supporterRelations.query()
                    supporterQuery.findObjectsInBackgroundWithBlock { (supporters, error) in
                        if(error == nil && supporters?.count > 0) {
                            for supporter in supporters! {
                                let user:PFUser = supporter as! PFUser
                                getFollowedUsersQuery.whereKey(Constants.kParseFieldToUser, equalTo: user)
                                getFollowedUsersQuery.whereKey(Constants.kParseFieldFromUser, equalTo: PFUser.currentUser()!)
                                getFollowedUsersQuery.findObjectsInBackgroundWithBlock({ (results, error) -> Void in
                                    if(error == nil && results?.count > 0) {
                                        for result in results! {
                                            // do stuff
                                        }
                                    }
                                })
                            }
                        }
                    }
                }
            }
        })

Now, this is pure madness, and incredibly wasteful (especially considering how Parse calculates the free tier - I feel this could really contribute heavily to my API limit if pushed to production).

Having already done two queries, I redo one entirely, then perform another query for each cause on the SUPPORTER Relations, then do another query on each user in that Relation to see if I follow them... and once I have that information, I need to loop through that User's supported causes (because of the asynchronous returning of the Parse queries, I don't feel that I can just reach back into the parent loops at all) ... which I haven't implemented yet, cause I'm about to throw in the towel - there has to be a better way!

I hope that I'm missing a strategy here...

Aucun commentaire:

Enregistrer un commentaire