Paginate posts to collection view with firebase & swift - swift

I'm trying to paginate posts that users are following to my collection view from my firebase database. Currently only 4 posts are being appended to the collection view and not loading anymore when I scroll down.
I've tried changing the number of posts loaded initially with no luck.
fileprivate func fetchFollowingUserIds() {
guard let uid = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("user-following").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
Database.fetchUserWithUID(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (err) in
print("Failed to fetch following user ids:", err)
}
}
var posts = [Post]()
fileprivate func fetchPosts() {
guard let uid = Auth.auth().currentUser?.uid else { return }
Database.fetchUserWithUID(uid: uid) { (user) in
self.fetchPostsWithUser(user: user)
}
}
var isFinishedPaging = false
fileprivate func fetchPostsWithUser(user: User) {
self.collectionView?.refreshControl?.endRefreshing()
let ref = Database.database().reference().child("posts").child(user.uid)
var query = ref.queryOrdered(byChild: "creationDate")
if posts.count > 0 {
let value = posts.last?.creationDate.timeIntervalSince1970
query = query.queryEnding(atValue: value)
}
query.queryLimited(toLast: 4).observeSingleEvent(of: .value, with: { (snapshot) in
guard var allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }
allObjects.reverse()
if allObjects.count < 4 {
self.isFinishedPaging = true
} else {
self.isFinishedPaging = false
}
if self.posts.count > 0 && allObjects.count > 0 {
allObjects.removeFirst()
}
allObjects.forEach({ (snapshot) in
guard let dictionary = snapshot.value as? [String: Any] else { return }
var post = Post(user: user, dictionary: dictionary)
post.id = snapshot.key
self.posts.append(post)
})
self.collectionView?.reloadData()
}) { (err) in
print(err)
}
}
I simply want it to load more posts when the user scrolls down.

There may be a query issue or a potential logic issue. Lets assume you want to present posts to the user, with the most recent at the top of the list and allow the user to scroll down to see earlier posts.
Let's address both with an example:
We don't have your structure but keeping it super simple, suppose your posts have the following structure with creation dates
post_0
creation_date: "20180101"
post_1
creation_date: "20180102"
post_2
creation_date: "20180103"
post_3
creation_date: "20180104"
post_4
creation_date: "20180105"
post_5
creation_date: "20180106"
post_6
creation_date: "20180107"
post_7
creation_date: "20180108"
Here's your initial query, order by creation date, which will load the last 4 posts from the 5th to the 8th
var query = ref.queryOrdered(byChild: "creationDate")
Then subsequent queries are ordered by creation date but the ending value is not the creation date but the time elapsed since 1970 of the creation date.
let value = posts.last?.creationDate.timeIntervalSince1970
var query = ref.queryOrdered(byChild: "creationDate").queryEnding(atValue: value)
I would guess you just want to load the next 4 earlier posts. So as this sit in the array, they look like this:
20180108
20180107
20180106
20180105
One way to do that is the get the creationDate of the last post from your dataSource (which will be the oldest post)
20180105
Then query by creationDate, endingAt the creation date of the last post, getting 5 total posts, then remove the last one
20180101
20180102
20180103
20180104
20180105
then reversed
20180105
20180104
20180103
20180102
20180101
and remove the first
20180104
20180103
20180102
20180101
something like this
let lastD = self.postsArray.last
self.postsArray = []
let postsRef = self.ref.child("posts")
let queryRef = postsRef.queryOrdered(byChild: "creation_date")
let queryEndingRef = queryRef.queryEnding(atValue: lastD)
let queryLimitedRef = queryEndingRef.queryLimited(toLast: 5)
queryLimitedRef.observeSingleEvent(of: .value, with: { snapshot in
guard var thisArray = snapshot.children.allObjects as? [DataSnapshot] else { return }
thisArray.reverse()
thisArray.removeFirst()
for post in thisArray {
let theDate = post.childSnapshot(forPath: "creation_date").value as! String
self.postsArray.append(theDate)
}
})

Related

Firebase query returns empty when data is there

I have this RTDB I am trying to search my users in, from a path called users -> UID -> and then the user key/values. One of them being "username". I want to append these to an array to return in my table view but no matter what I do, I keep getting back nothing.
var userRef = Database.database().reference(withPath: "users")
func queryText(_ text: String, inField child: String) {
print(text)
userRef.queryOrdered(byChild: child)
.queryStarting(atValue: text)
.queryEnding(atValue: text+"\u{f8ff}")
.observeSingleEvent(of: .value) { [weak self] (snapshot) in
for case let item as DataSnapshot in snapshot.children {
//Don't show the current user in search results
if self?.currentUser?.uid == item.key {
continue
}
if var itemData = item.value as? [String:String] {
itemData["uid"] = item.key
self?.resultsArray.append(itemData)
print(self?.resultsArray)
}
}
self?.tableView.reloadData()
}
}
Edit: I have verified I am able to print out the snapshot, I am just not getting the usernames added to my resultsArray. Anyone have a clue why?
bio = " dfdf";
displayname = chattest4;
email = "test#test.com";
"first_name" = chattest4;
followers = 0;
following = 0;
"join_date" = "June 28, 2021";
languages = English;
"last_name" = test;
location = "United States";
profileImageURL = "hidjkfsf";
username = chattest4;
So I found out the issue. Some of the values in my database were not strings, but I had coded it to only look for Strings. Username was stored after these non-string values, so it never reached it. I just changed the array to [String:Any] and then it worked!
if var itemData = item.value as? [String:Any]

Filter items from Firebase branch (SWIFT)

Current user in App tap the Like button on post (audio record) from another user and it appears in likes branch in Firebase. There is another branch records, where all records from each users exist.
I've got a Firebase structure
Blue: current userID,
Green: another userID,
Red: recordID.
I need to display on my ViewController only liked records (red line), but with my code I've got all records from another user (green line). How to display only liked records (red line)?
fileprivate func fetchRecordsWithUser(user: User2){
let ref = Database.database().reference().child("records").child(user.uid)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else { return }
dictionaries.forEach({ (key, value) in
guard let dictionary = value as? [String: Any] else { return }
let record = Records(user: user, dictionary: dictionary)
self.records.append(record)
self.records.sort(by: { (p1, p2) -> Bool in
return p1.creationDate.compare(p2.creationDate) == .orderedDescending
})
self.tableView?.reloadData()
})
}) { (err) in
print ("Faild to fetch records:", err)
}
}
I am a little unclear on the question but let me give this a try
Lets's start with a structure like in the question, using simpler data.
likes
uid_0
uid_1
record_0: true
uid_2
record_1: true
uid_1
uid_0
record_3: true
I believe the object is the get the values record_0, record_1 and record_3 so the code to do that is:
func printRecordIds() {
var recordIdArray = [String]()
let likesRef = self.ref.child("likes")
likesRef.observeSingleEvent(of: .value, with: { snapshot in
let allLikes = snapshot.children.allObjects as! [DataSnapshot]
for likeSnap in allLikes {
let uid = likeSnap.key
print(uid)
let uidLikes = likeSnap.children.allObjects as! [DataSnapshot]
for childLike in uidLikes {
let childUid = childLike.key
print(" child uid: \(childUid)")
let childRecords = childLike.children.allObjects as! [DataSnapshot]
for child in childRecords {
let recordId = child.key
print(" recordId: \(recordId)")
recordIdArray.append(recordId)
}
}
for r in recordIdArray {
print(r)
}
}
})
and the output
uid_0
child uid: uid_1
recordId: record_0
child uid: uid_2
recordId: record_1
record_0
record_1
uid_1
child uid: uid_0
recordId: record_3
record_0
record_1
record_3
So the output shows the data we are reading in per node so you can see the flow and then the array, populated with the record id's.

Swift sort of array always in ascending order

i try to show a array list sorted by its Timestamp in an descending order (newest first --> highest ts first) therefore i created a downloading method and a sorting method:
func getBlogsByAuthor(){
self.allBlogs.removeAll()
for authorId in self.authorIds{
db.collection("Blogs").whereField("authorId", isEqualTo: authorId)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let ts = document.get("ts") as! Int
let title = document.get("title") as! String
let body = document.get("body") as! String
let authorId = document.get("authorId") as! String
let state = document.get("stateios") as? String
let imageUrl = document.get("imageUrl") as! String
let id = document.documentID as! String
let blogObject = BlogObject.init(title: title , body: body, imageUrl: imageUrl , authorId: authorId , state: state ?? "Null" , id: id, ts: ts )
self.allBlogs.append(blogObject)
}
self.sortDataset()
}
}
}
}
func sortDataset(){
self.allBlogs.sorted(by: { $0.ts! < $1.ts! })
self.rv.reloadData()
}
The problem is that the values are showing always the lowest ts first no matter if i change it from self.allBlogs.sorted(by: { $0.ts! < $1.ts! })
to self.allBlogs.sorted(by: { $0.ts! > $1.ts! })
You need
self.allBlogs.sort { $0.ts! < $1.ts! } // mutating sort in place
as sorted(by returns a result that you ignore it and don't re-assign it would be
self.allBlogs = self.allBlogs.sorted(by: { $0.ts! < $1.ts! })

Firebase Swift 3 Fetching multiple values from a group

I want to be able to list users in a tableview by fetching their UID Value from a key stored in my database. As of now, the code only fetches the first value in the database rather than all of the values. Here is the code for fetching the applicants.
func loadApplicants() {
let usersRef = ref.child("users")
usersRef.observe(.value, with: { (users) in
var resultArray = [UserClass]()
for user in users.children {
let user = UserClass(snapshot: user as! DataSnapshot)
if user.uid == self.job.userID {
let appRef = self.ref.child("jobs").child(self.job.postID).child("applicants")
appRef.queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { (snapshot) in
let sValue = snapshot.value
resultArray.append(user)
})
}
}
}) { (error) in
print(error.localizedDescription)
}
}
This is what my database looks like where the User's UIDs are stored.
jobs
"Job ID"
applicants:
-KtLJaQnFMnyI-MDWpys:"8R6ZAojX0FNO7aSd2mm5aQXQFpk1"
-KtLLBFU_aVS_xfSpw1k:"GGqjtYvwSwQw9hQCVpF4lHN0kMI3"
If I was to run the app, it fetches UID: "8R6ZAojX0FNO7aSd2mm5aQXQFpk1"
How can I implement a for loop or an if statement to ensure that all of the values are taken and appended into the table view
I know that I need a for loop before the fetchApplicants is called from AuthService because it is only fetching one UID but I can't work out where it would go.
Thanks.
P.S. This is what I have tried
func loadApplicants() {
let jobID = job.postID
let appRef = ref.child("jobs").child(jobID!).child("applicants")
appRef.queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let applicants = snapshot.value! as? [String:AnyObject] {
for (value) in applicants {
self.authService.fetchApplicants(applicantID: "\(value!)", completion: { (users) in
self.usersArray = users
self.tableView.reloadData()
})
}
}
})
}
but the output is:
(key: "-KtLLBFU_aVS_xfSpw1k", value: GGqjtYvwSwQw9hQCVpF4lHN0kMI3)
(key: "-KtLJaQnFMnyI-MDWpys", value: 8R6ZAojX0FNO7aSd2mm5aQXQFpk1)
Needed to use observe instead of observeSingleEvent
Answer:
appRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in

Getting value of children with same name - Firebase Swift3

I'm using the following query to get the snapshot below.
dbRef.child("users").queryOrdered(byChild: "username").queryStarting(atValue: text).queryEnding(atValue: text+"\u{f8ff}").observe(.value, with: { snapshot in })
But I'm having trouble figuring out how to iterate over the values of the children "username" (i.e. bill, billy, billykins) because they all have the same key ("username"). I'm trying to extract all the names and put them in an array of names.
Snap (users) {
fDrzmwRUt2guh3O8pc792ipyEqA3 = {
username = bill;
};
qyQkIOxSpXgQoUoh0QNvPlkQ1Mp1 = {
username = billy;
};
edSk54xSpXgQoYoh0QNvOlkQ1Mp3 = {
username = billykins;
};
}
This is my function which gets the snapshot and tries to iterate over it. However, the loop crashes:
func findUsers(text: String) {
print("search triggered")
dbRef.child("users").queryOrdered(byChild: "username").queryStarting(atValue: text).queryEnding(atValue: text+"\u{f8ff}").observe(.value, with: { snapshot in
print(snapshot)
for i in snapshot.children{
let value = snapshot.value as! Dictionary<String,String>
let username = value["username"]
print(username)
self.userList.append(username!)
print(self.userList)
}
}) { (error) in
print("searchUsers \(error.localizedDescription)")
}
}
Here's a fraction of the log. Too long to post:
0x1002cg360 <+272>: adr x9, #268929 ; "value type is not bridged to Objective-C"
0x1002cg364 <+276>: nop
0x1002cg368 <+280>: str x9, [x8, #8]
-> 0x1002cg36c <+284>: brk #0x1
0x1002cg370 <+288>: .long 0xffffffe8 ; unknown opcode
Here's the solution I was looking for. I needed to loop through the big snapshot to extract the child snapshots into an array, and then loop through the child snapshots by value to get the usernames:
func findUsers(text: String) {
print("search triggered")
dbRef.child("users").queryOrdered(byChild: "username").queryStarting(atValue: text).queryEnding(atValue: text+"\u{f8ff}").observe(.value, with: { snapshot in
print(snapshot)
if ( snapshot.value is NSNull ) {
print("not found")
} else {
var blaArray = [FIRDataSnapshot]()
for child in snapshot.children {
let snap = child as! FIRDataSnapshot //downcast
blaArray.append(snap)
}
for child in blaArray {
let dict = child.value as! NSDictionary
let username = dict["username"]!
print(username)
}
}) { (error) in
print("searchUsers \(error.localizedDescription)")
}
}
Adapted from this post: Get data out of array of Firebase snapshots