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
iOS Healthkit How to Save Heart Rate (Bpm) Values? Swift
Swift iOS - Tag Collection View
How to Get the Correct Current Time in iOS
iOS with Parse. Pfuser.Currentuser() Not Getting Cached. Returns Nil After App Restart
App Groups Forsecurityapplicationgroupidentifier Returns Nil
How to Determine If a Business Is Open Given the Hours of Operation (Swift-Ios)
Why Sending Message from Watchkit Extension to iOS and Getting Back a Reply Is So Slow
Get Center Coordinates from Mapkit and Display in Uilabel
Cocoa Singleton and Shared Instances
Swiftui - Optional Timer, Reset and Recreate
Get Progress of Uinavigationcontroller Swipe Back
Load Large 3D Object .Scn File in Arscnview Aspect Fit in to the Screen Arkit Swift iOS
Do I Need to Wrap My Alamofire Calls Inside Dispatch_Async