Search code examples
swiftfirebasefirebase-realtime-databasesocial-networking

Firebase - Making a homefeed of posts of people you follow (swift)


whats the most scalable solution to this?

- posts
    - userID
    - userID
          - randomID
          - randomID
- followers
    - personB userid
          - personA userid
- following 
    - personA userid
          - personB userid

I have a Database structure like this. so if person A starts following person B he will be put under person B´s ID under "followers". At the same time person B will be put under persons A´s id under "following".

If a person makes a post on the App, it will be posted under "posts" and has a child with the userID of the person who posted (and under there, childs with random ids of different posts they have).

I have tried

let queryRef = self.ref.child("posts/\(userID)")

 queryRef?.observe(.childAdded, with: { (snapshot) in

    // new post from a friend 

})`

I'd have to use this method for every single person the user follows though which would be really slow if there is alot of people who has posted stuff!

Do you guys have any smart scalable solutions to this problem?


Solution

  • This is really not scalable. Ideally, you'd want to fan out the data. For example, you'd create additional node, let's call it feed. Every time the current logged user follows another user you'd add that users' posts ids (if any) to the feed of the current user like this:

    -feed 
       -userID
          -postID
    

    Now, in the example you gave above when Person B follows Person A, if Person A has existing posts, get the id of those posts and store them in the feed under person B id with the timestamp of the post (if you have any and you want to sort out the posts on the feed):

    let followRef = Database.database().reference().child("followers")
    guard currentUserUid = Auth.auth().currentUser?.uid else { return }
    
    followRef.child(currentUserUid).observeSingleEvent(of: .value, with: { snapshot in
        let followersArraySnapshot = snapshot.children.allObjects as! [DataSnapshot]
        followersArraySnapshot.forEach({ (child) in
    
            // child.key is the userUid of each user they follow (depends on your model)
            let refFeed = Database.database().reference().child("feed")
            refFeed.child(child.key).child("get the post ID").setValue(["timestamp" : timestamp])
         })
    })
    

    Then in your FeedViewController or wherever you need to show the feed really, you'd have to observe the feed for the current logged user (which should return the id of each post), then observe each post with that id, cache/store them in an array and then display them to the user.

    This whole thing should look something like this:

    var posts = [Post]() // post array (based on your post model)
    
    func observeFeedPosts(withUserId id: String, completion: @escaping (Post) -> Void) {
    let feedRef = Database.database().reference().child("feed")
    let postRef = Database.database().reference().child("posts")
    
    feedRef.child(id).queryOrdered(byChild: "timestamp").observe(.childAdded, with: { snapshot in
        let postId = snapshot.key
         self.postRef.child(postId).observeSingleEvent(of: .value, with: { snapshot in
        if let dictionary = snapshot.value as? [String : Any] {
    
            // snapshot.value should return your post, just transform it based on your own model 
            let post = Post.transformDataToImagePost(dictionary: dictionary, key: snapshot.key)
            self.posts.append(post)
    
          }
       })
    }
    

    That way you don't need to store the userID under posts, but the id of the post itself. At some point, you'd want to observe all the posts the current logged user made, so I suggest to fan out the data in a similar way - create myPosts node (or call it whatever you want) and store the information like so:

     -myPosts
         -userId
            -postId:true
    

    Then all you have to do is observe this myPosts node, get the postId for the current logged user and observe the posts node to get the actual post value. That's it. It flattens the data. This would be my recommendation.