Show Posts from the Users You Are Following - Swift

Show posts from the users you are following - swift

Assuming that you are saving your followers list as an dictionary in other parent node like this :-

user_followed_by :{
userID2 : {
userID1 : true,
userID5 : true,
userID6 : true,
userID12 : true,
userID99 : true,
}
}

users :{
userID2 :{
post :{
postAutoID1 : {...},
postAutoID2 : {...},
...
}
}
}
postsToShowToUser :{

userID1 : {
postAutoID1 : true, //These are the post's autoID's of all the users whom
// your current user is following
postAutoID5 : true,
postAutoID3 : true,
},

}

/* // If you choose to declare a separate section of the post Details in your Database.
posts_Of_The_User :{

userID1 : {

postAutoID1 : {...//DETAILS},
postAutoID2 : {...//DETAILS},
postAutoID3 : {...//DETAILS},
....
},

} */

The idea is that whenever a user whom your current user is following makes a post. That post's autoID gets appended in the postsToShowToUser/userID.

That is, if userID2 will make a post then a call will be made to append that post's autoID in all users postsToShowToUser/userID who are following the userID2.

PS:- I strongly suggest that you move your post details out of post section. Make it a separate parent node consisting of multiple postAutoID's as key and there post data as value. It might come in handy later on, also would avoid nesting data, which would help you navigate through your Database.

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

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.

How to retrieve posts from following users at once?

There are some ways that you can achieve that. You will need to get the uid per users and compare it with the uid from the posts. I would recommend you to take a look at the Community post Firestore: Queries and Security (Swift). This post provide some ideas on how to achieve that.

Besides that, the official documentation Querying and filtering data provides examples of queries with Swift.

These other two below posts should provide you some additional insights on querying with Swift, per ids.

  • Get document id From Firestore Swift
  • Query Firebase Firestore documents by the ID

I could find these below two repositories, that have Firestore post applications - not in Swift - that might help you get ideas as well, on how to perform queries and the logic to retrieve posts per user.

  • CloudFirestore-Android
  • photo-blog-app

Let me know if the information helped you!

SwiftUI / Firestore Displaying posts from users you follow / favorite

do a for loop on favorite trainers and query on workouts with their ids.

var workouts : [Workout] = []

for trainer in favTrainer {
fetchTrainerWorkouts(withId : trainer.Uid){ workouts in
self.workouts += workouts
}
}

fetchTrainerWorkouts:

func fetchTrainerWorkouts(withId trainerId: String , compilation : @escaping : ([Workout])->Void){
WORKOUT_COLLECTION.whereField("TrainerUid" , isEqualTo:trainerId).addSnapshotListener { snapshot, _ in
guard let documents = snapshot?.documents else { return }
let workouts = documents.compactMap({ try? $0.data(as: Workout.self) })
compilation(workouts)
}
}

then in each snapshot, add workouts to your workouts array.

Firestore - Retrieve posts for accounts a user follows

I think that this can be done easier. Of course I do not have details of whole system, however looking at this particular problem I would add to every userPosts document new field followedBy. The field would be an array where all following the post user ids (uid) will be stored.

Now checking, if particular posts is followed by particular user can be done by checking if this followedBy array contains this user id. Then it will be possible to use two Firestore features: collectionGroup and arrayContains filter

Then getting list of posts followed by particular user will be extremely simple like this:

db.collectionGroup("userPosts")
.whereField("followedBy", arrayContains: uid)
.getDocuments() { (snapshot, error) in
// ...

No looping and less document got from the Firestore. This should be more effective when it comes to usage cost as well.

Show only posts from current user - firebase swift

Try this:-

Swift 3

func startObersvingDB() {
FIRDatabase.database().reference().child("feed-items").queryOrdered(byChild: "byUsername").queryEqual(toValue: "MyUser").observe(.value, with: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()

for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)

}

self.updates = newUpdates
self.tableView.reloadData()

}) { (err) in
print(err!.localisedDescription)
}
}

Swift 2

func startObersvingDB() {
FIRDatabase.database().reference().child("feed-items").queryOrderedbyChild("byUsername").queryEqualtoValue("MyUser").observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()

for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)

}

self.updates = newUpdates
self.tableView.reloadData()

}) { (error: NSError) in
print(error.description)
}
}

How do I fetch posts of users that the current signed in user is following

It is better to break your question down into smaller pieces, cause the question you've asked is a whole feature of your and its implementation.

I don't see why you separate the following collection from users' collection and put them in the top level of the data model, because basically in the there, you've created another layer to add a userFollowing sub-collection to the user document. You can relocate the userFollowing to the next level of user documents. Still, again, it depends on the fact that whether you want to store the whole data of the user (the people whom user follows) or fill their documents with the uid of their own.

But looking at the current state of your data model something similar to the code below can help you on this:

const arrayOfPeopleWhichUserFollows = await db.collection('following').doc(userId).collection('userFollowing').get()
.then(querySnapshot => {
return querySnapshot.docs.map((doc) => {
return doc.data().uid;
});
});

// you have to measure the size of arrayOfPeopleWhichUserFollows to check whether it exceeds the limitation of 10
// for using in the "where in" query from firestore
// then breaks the array into a smaller piece, as small as 10 items per array and the repeat the below part for all the
// fragmented arrays and append all the results into a single variable

const firstPosts = await db.collection('posts')
.where('uid', 'in', fragmentedArrayOfPeopleWhichUserFollows)
.orderBy('timestamp', 'desc').limit(10);

const posts = await firstPosts.get()
.then(querySnapshot => {
const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
return querySnapshot.docs.map((post) => {
return post.data();
});
const nextPosts = db.collection('posts')
.where('uid', 'in', fragmentedArrayOfPeopleWhichUserFollows)
.orderBy('timestamp', 'desc')
.startAfter(lastVisible)
.limit(10)
});

consider reading these links:

how to write queries in firestore

how to paginate your data while querying from firebase

how to Manage indexes in Cloud Firestore

Showing post data from logged in user only swift/xcode/firebase

I believe the question is how to get the posts for a certain user. The structure looks good but no need to have a child node 'author' in each post so instead of this:

posts
post_id_0
author
author data
text: "Hello, World"
uid: "uid_0"

do this

posts
post_id_0
text: "Hello, World"
uid: "uid_0"
name: "usmaan"
photoURL:"https://firebasestorage..."

So now just query for this users posts (this is for Firestore, scroll down fo the RTDB solution)...

func getThisUsersPosts() {
let uid = "uid_0" //this users uid
self.db.collection("posts]").whereField("uid", isEqualTo: uid).getDocuments { (snapshot, error) in
if let err = error {
print(err.localizedDescription)
return
}

if let doc = snapshot?.documents {
for d in doc {
let text = d.get("text") as? String ?? "No Post Text"
print(text)
}
} else {
print("no posts found")
}
}
}

self.db points to my Firestore.

EDIT:

OP is using the Real Time Database so here's the code for that

func getThisUsersPosts() {
let uid = "uid_0"
let ref = self.ref.child("posts") //self.ref points to MY firebase.
let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: uid)
query.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
let text = postSnap.childSnapshot(forPath: "text").value as? String ?? "No Text"
print(text)
}
})
}

EDIT 2:

OP wants to keep their same structure.

To query for data that's two levels deep we use what's called Deep Query and will look something like this:

func performDeepQuery() {
let uid = "uid_0"
let ref = self.ref.child("posts")
let query = ref.queryOrdered(byChild: "author/uid").queryEqual(toValue: uid)
query.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
//populate your tableView datasource here
let post = PostClass()
post.postId = postSnap.key
post.name = postStap.childSnapshot("name").value as? String ?? "No Post Name"
post.text = postStap.childSnapshot("text").value as? String ?? "No Post Text"
self.postArray.append(post)
}
self.myTableView.reloadData()
})
}

which will perform a deep query on this structure

posts
post_0
author
uid: "uid_0"
name: "post 0 name"
text: "post 0 text"

The PostClass could be this

class PostClass {
var postId = ""
var name = ""
var text = ""
}

More Info:

To get the current users uid (which is covered in the getting started guide as well) and assuming you are authenticated (otherwise it will be nil)

guard let user = Auth.auth().currentUser else { return }
let uid = user.uid


Related Topics



Leave a reply



Submit