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.
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)
}
})
This is how my Firebase database looks like:
I want to get the names "Attack on Nibeiwa", "Fort Capuzzo" and so on instead of the rest of the things. My current code gives my the complete JSON:
let ref = Database.database().reference().child("Battle Details")
ref.observeSingleEvent(of: .value, with: { (snap : DataSnapshot) in
print("\(String(describing: snap.value))")
}) { (err: Error) in
print("\(err.localizedDescription)")
}
To retrieve the names try the following:
let ref = Database.database().reference().child("Battle Details")
ref.observeSingleEvent(of: .value, with: { (snap : DataSnapshot) in
for child in snap.children {
let key = (child as AnyObject).key as String
}
}) { (err: Error) in
print("\(err.localizedDescription)")
}
Here the snapshot is at Battle Details then you iterate inside the direct children which are the names in this case and retrieve the names using child.key
I have two arrays of dictionaries:
Dict 1 =
[{"id":"100", "name":"Matt", "phone":"0404040404", "address":"TBC"}
,{"id":"110", "name":"Sean", "phone":"0404040404", "address":"TBC"}
, {"id":"120", "name":"Luke", "phone":"0404040404", "address":"TBC"}]
Dict 2 =
[{"id":"100", "address":"1 Main Street"}
,{"id":"110", "address":"2 Main Road"}
, {"id":"120", "address":"3 Main Street"}]
I want to compare the key:value pair, id , of each dictionary in Dict 2 against Dict 1, and if the id matches, update the corresponding address in Dict 1 from the value in Dict2.
So the desired output should be:
Dict 1 =
[{"id":"100", "name":"Matt", "phone":"0404040404", "address":"1 Main Street"}
,{"id":"110", "name":"Sean", "phone":"0404040404", "address":"2 Main Road"}
, {"id":"120", "name":"Luke", "phone":"0404040404", "address":"3 Main Street"}]
EDIT
As requested, here is more information regarding how I am parsing the data. I am getting Dict1 and Dict2 as response to HTTP URL call btw. And also, I use dictionaries of the type [Dictionary] while parsing.
let Task1 = URLSession.shared.dataTask(with: URL!) { (Data, response, error) in
if error != nil {
print(error)
} else {
if let DataContent = Data {
do {
let JSONresponse1 = try JSONSerialization.jsonObject(with: DataContent, options: JSONSerialization.ReadingOptions.mutableContainers)
print(JSONresponse1)
for item in JSONresponse1 as! [Dictionary<String, Any>] {
//Parse here
}
}
catch { }
DispatchQueue.main.async(execute: {
self.getAddressTask()
})
}
}
}
Task1.resume()
}
JSONResponse1 is Dict 1
Then inside the getAddressTask() func called above, I do the HTTP URL call to get Dict 2
let AddressTask = URLSession.shared.dataTask(with: URL2!) { (Data, response, error) in
if error != nil {
print(error)
} else {
if let DataContent = Data {
do {
let JSONresponse2 = try JSONSerialization.jsonObject(with: timeRestrictionsDataContent, options: JSONSerialization.ReadingOptions.mutableContainers)
print(JSONresponse2)
for item in JSONresponse2 as! [Dictionary<String, Any>] {
//Parse here
}
catch { }
self.compileDictionaries()
}
}
}
AddressTask.resume()
JSONResponse2 is Dict2
Inside compileDictionaries() i would like to get the desired output as shown above.
You should struct your data using Codable protocol and create a mutating method to update your contact. If you need an array of your contacts once you have them updated all you need is to encode your contacts using JSONEncoder:
struct Contact: Codable, CustomStringConvertible {
let id: String
var address: String?
var name: String?
var phone: String?
mutating func update(with contact: Contact) {
address = contact.address ?? address
name = contact.name ?? name
phone = contact.phone ?? phone
}
var description: String {
return "ID: \(id)\nName: \(name ?? "")\nPhone: \(phone ?? "")\nAddress: \(address ?? "")\n"
}
}
Playground testing:
let json1 = """
[{"id":"100", "name":"Matt", "phone":"0404040404", "address":"TBC"},
{"id":"110", "name":"Sean", "phone":"0404040404", "address":"TBC"},
{"id":"120", "name":"Luke", "phone":"0404040404", "address":"TBC"}]
"""
let json2 = """
[{"id":"100", "address":"1 Main Street"},
{"id":"110", "address":"2 Main Road"},
{"id":"120", "address":"3 Main Street"}]
"""
var contacts: [Contact] = []
var updates: [Contact] = []
do {
contacts = try JSONDecoder().decode([Contact].self, from: Data(json1.utf8))
updates = try JSONDecoder().decode([Contact].self, from: Data(json2.utf8))
for contact in updates {
if let index = contacts.index(where: {$0.id == contact.id}) {
contacts[index].update(with: contact)
} else {
contacts.append(contact)
}
}
let updatedJSON = try JSONEncoder().encode(contacts)
print(String(data: updatedJSON, encoding: .utf8) ?? "")
} catch {
print(error)
}
This will print:
[{"id":"100","phone":"0404040404","name":"Matt","address":"1 Main
Street"},{"id":"110","phone":"0404040404","name":"Sean","address":"2
Main
Road"},{"id":"120","phone":"0404040404","name":"Luke","address":"3
Main Street"}]
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