Swift, Firebase retrieving data from unknown child - ios

I would like to retrieve data from Firebase database child. But I don’t know the child node name.
My database looks like this:
Users
UserID
2017 (I don't know this string. It's 2017, 2018... or something else?)
November (I don't know this string. It's November, January... or something else?)
Key: Value (I need this data)
Key: Value (I need this data)
Key: Value (I need this data)
I tried this:
override func viewDidLoad() {
super.viewDidLoad()
let userID = Firebase.Auth.auth().currentUser?.uid
databaseRef = Database.database().reference().child("Users").child(userID!)
databaseRef.observe(.value, with: { (snapshot) in
var newItems = [Post]()
for item in snapshot.children {
let newPost = Post(snapshot: item as! DataSnapshot)
newItems.append(newPost)
}
self.postArray = newItems
self.tableView.reloadData()
print(newItems)
}) { (error) in
print(error.localizedDescription)
}
}

In your current code you loop over the years already. If you also want to loop over the months, you'll need to add an extra for:
databaseRef = Database.database().reference().child("Users").child(userID!)
databaseRef.observe(.value, with: {(snapshot) in
for year in snapshot.children.allObjects as [DataSnapshot] {
for month in year.children.allObjects as [DataSnapshot] {
print(month.key)
}
}
}){
(error) in
print(error.localizedDescription)
}
This will print the months. You can get properties of the specific month with:
print(month.childSnapshot(forPath:"Key").value)

Related

Firebase child query coming up nil after deleting a key then adding it back

I run the code below and using print statements the Database is saying the children are null
But in the Database there is definitely something there:
The only thing I can think of is I let a user delete a message (by deleting the -LoyeLv... key) but if they want to add it back they can. I keep a copy of the deleted key and just send it back to the database so that the message in still in sync. The only problem with that is even when I don't do it (as in my example) and I delete the message, come back and make a completely new one with a completely new key, it's still coming up as null?
How is this possible even though there are children there? Btw this is the first time this has ever happened to me.
Database.database().reference()
.child("favorite")
.child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
.queryOrderedByKey()
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
print(snapshot.value) // prints null
if snapshot.hasChildren() {
print("yay")
} else {
print("nay") // prints nay
}
// doesn't go past here
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
Update I just tried this below and it works fine but the above still doesn't work:
Database.database().reference()
.child("favorite")
.child(uid) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
print(snapshot.value) // prints the json data
if snapshot.hasChildren() {
print("yay") // prints nay
} else {
print("nay")
}
I woder does deleting the key at that ref than adding the same key than deleting it again than adding a brand new key somehow throws the db off?
Update Again Instead of using .queryOrderedByKey() I changed it to use .queryOrdered(byChild: "timeStamp") and it works fine:
Database.database().reference()
.child("favorite")
.child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
.queryOrdered(byChild: "timeStamp")
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
When deleting a key then adding the same exact key back to the db then querying by queryOrderedByKey() must cause some sort of internal problem that doesn't just mess up the key, it messes up the entire ref. I think what happens is after I delete the key, whatever internal tracking mechanism wipes it from the system. When I add it back it's no longer the same key but instead just a regular String with no internal meaning and that's why is says null. I'm asking it to query something with no meaning. This is a wild guess.
But I would like to know why the same problem occurs with brand new keys that have never been deleted and added back at that same ref?
Here's the code that #FrankvanPuffelen requested in the comments:
This is the vc that sends/deletes the data.
1- sendDataToFirebase()
2- deleteDataFromFirebase()
3- sendDataToFirebase()
Do it in that order
var someOtherId = "12345" // this id has nothing to do with firebase and might be something like "x778-248-000"
var snapKey: String?
var originalTimeStamp: Double?
func sendDataToFirebase() {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let fbKey = Database.database().reference().child("favorite").childByAutoId().key else { return }
let timeStamp = Date().timeIntervalSince1970
var favoriteDict = [String: Any]()
favoriteDict.updateValue(uid, forKey: "uid")
favoriteDict.updateValue(originalTimeStamp ?? timeStamp, forKey: "timeStamp")
var whichKeyToUse: String?
if let snapKey = self.snapKey {
whichKeyToUse = snapKey
} else {
whichKeyToUse = fbKey
}
var carDict = [String: Any]()
carDict.updateValue(originalTimeStamp ?? timeStamp, forKey: whichKeyToUse!)
let favoriteRef = "favorite/\(uid)/\(whichKeyToUse!)"
let carRef = "carType/\(someOtherId)/\(uid)"
var multiLocationDict = [String: Any]()
multiLocationDict.updateValue(favoriteDict, forKey: favoriteRef)
multiLocationDict.updateValue(carDict, forKey: carRef)
Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { (error, ref) in
if self.snapKey == nil {
self.snapKey = fbKey
}
if self.originalTimeStamp == nil {
self.originalTimeStamp = timeStamp
}
// if no error this 100% saves it the way it's supposed to
})
}
func deleteDataFromFirebase() {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let snapKey = self.snapKey else { return }
let favoriteRef = "favorite/\(uid)/\(snapKey)"
let carRef = "carType/\(someOtherId)/\(uid)"
var multiLocationDict = [String: Any]()
multiLocationDict.updateValue(NSNull(), forKey: favoriteRef)
multiLocationDict.updateValue(NSNull(), forKey: carRef)
Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { [weak self](error, ref) in
// if no error this 100% deletes what it's supposed to
})
}
2. This is the vc that reads the data, this is an entirely different vc and where the problem lies
func firstCheckIfCurrentUserExistsAtFavoriteRef() {
guard let uid = Auth.auth().currentUser?.uid else { return }
let favoriteRef = Database.database().reference()
.child("favorite")
.child(uid)
favoriteRef.observeSingleEvent(of: .value) { [weak self](snapshot) in
if !snapshot.exists() {
return
}
print(snapshot.key) // prints the key
print(snapshot.value) // *** the value prints fine here but in handlePagination it prints null ***
if snapshot.hasChildren() {
print("yay") // prints yay
} else {
print("nay")
}
self?.handlePagination(with: uid)
}
}
var startKey: String?
func handlePagination(with currentUserId: String) {
if startKey == nil {
Database.database().reference()
.child("favorite")
.child(currentUserId)
.queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
print(snapshot.key) // prints the uid
print(snapshot.value) // *** prints null but in firstCheckIfCurrentUserExistsAtFavoriteRef() it prints fine ***
if snapshot.hasChildren() {
print("yay")
} else {
print("nay") // prints nay
}
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
// ...
})
} else {
// it never makes it this far but it's the same code from above
Database.database().reference()
.child("favorite")
.child(currentUserId)
.queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
.queryEnding(atValue: startKey!)
.queryLimited(toLast: 11)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
}
I call firstCheckIfCurrentUserExistsAtFavoriteRef() in viewDidLoad
I have seen this issue before with persistence turned on - the issue is the data is partially sync'd but not fully.
For a quick test, try turning persistence off and re-running the query.
Database.database().isPersistenceEnabled = false
That's obviously not a long term solution but just for testing.
If you want to use persistence, you also need to ensure the data is kept fresh and always in sync. Here's the code
let postsRef = Database.database().reference(withPath: "posts")
postsRef.keepSynced(true)
The Firebase Realtime Database client automatically downloads the data
at these locations and keeps it in sync even if the reference has no
active listeners. You can turn synchronization back off with the
following line of code
postsRef.keepSynced(false)

Fetch first key from firebase database with swift 4 using ChildAdded

I'm trying to fetch the first key from my firebase database but for some reason nothing is being printed out. How can I get the first key from my firebase database using .childAdded
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
print(first)
This in incredibly easy if you literally are asking how to only ever get the first child of a node. Here's how to only get the first child of a /users node
func getFirstChild() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .childAdded, with: { snapshot in
print(snapshot)
})
}
or
print(snapshot.key)
if you just want the key.
Or, if you want to use a query to do the same thing
func getFirstChildAgain() {
let usersRef = self.ref.child("users")
let query = usersRef.queryOrderedByKey().queryLimited(toFirst: 1)
query.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
The child_added event is typically used when retrieving a list of items from the database. Unlike value which returns the entire contents of the location, child_added is triggered once for each existing child and then again every time a new child is added to the specified path. The event callback is passed a snapshot containing the new child's data. For ordering purposes, it is also passed a second argument containing the key of the previous child.
From: Read and Write Data on iOS
Per your requirements, this is possible in .value and childAdded.
var child_array = [String:String]
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as? String ?? "Empty String"
let key = snapshot.key
child_array[key] = value;
}) { (error) in
print(error.localizedDescription)
}
then:
if let first = child_array.first?.key {
print(first) // First Key
}
Big NOTE: child_added randomly collects this data, you should never use it to sort your data

Getting all data in a Firebase column

I created JSON database tree and I can read specific values with these codes. I can see on table view "Albert Einstein"
ref.child("Personel").child("Name").observeSingleEvent(of: .value, with: { (snapshot) in
if let item = snapshot.value as? String{
self.myList.append(item)
self.LessonsTableView.reloadData()
}
})
But, I want to see which categories under Personal column? Like this,
Is there any way to get or learn which columns are under "Personal"
Table view output must be -> Age, Name, Photo
You can iterate over the snapshot, get it's children and then those childrens keys
Say you have a users node with a user
users
user_0
fav_game: "WoW"
name: "Leroy"
then to get the keys of name: and fav_game:
let userRef = self.ref.child("users").child("user_0")
userRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
print(key)
}
})
prints
fav_game
name
An important note is
for child in snapshot.children
because that will keep the data (keys in this case) in order. If the snapshot is dumped into a dictionary it looses order.
If you loop through snapshot.value you should be able to get the keys from the database.
In JavaScript it would be something like:
ref.child("Personel").child("Name").observeSingleEvent(of: .value, with: { (snapshot) in
if let item = snapshot.value as? String{
self.myList.append(item)
self.LessonsTableView.reloadData()
}
var vals = snapshot.val();
for(var property in vals) {
console.log(property); // property has those values in it
}
})
This is one way:
ref.child("Personel").observeSingleEvent(of: .value, with: { (snapshot) in
self.myList.append(snapshot.childSnapshot(forPath: "Age").value)
self.myList.append(snapshot.childSnapshot(forPath: "Name").value)
self.myList.append(snapshot.childSnapshot(forPath: "Photo").value)
self.LessonsTableView.reloadData()
})

How to query a child from another child. Firebase(SWIFT)

im developing a delivery app. So I have productos and popular products in firebase this way:
Products
PopularProducts(ID of the product as key and true as value)
How I can query only the products who are popular using those childs?
You need to get array of productosPopulares and then get each object.
let referenceToProductosPopulares = FIRDatabase.database().reference(withPath: "productosPopulares")
referenceToProductosPopulares.observeSingleEvent(of: .value, with: { snapshot in
if let value = snapshot.value as? NSDictionary {
let productosPopIds = value.allKeys as! [String]
// get every object
for id in productosPopIds {
let refToProd = FIRDatabase.database().reference(withPath: "productos").child(id)
refToProd.observeSingleEvent(of: .value, with: { snapshot in
// your value
})
}
} else {
print("productosPopulares")
}
})
Not perfect code. Should make more functions. But this is idea.
Hope it helps
You can query ordered by the esPopular child and only return the children with it equal to true.
let ref = Database.database().reference(withPath: "productos")
let query = ref.queryOrdered(byChild: "esPopular").queryEqual(toValue: true)
query.observe(.childAdded, with: { (snapshot) in
print(snapshot)
}) { (error) in
print(error)
}

Firebase ,Display post with The next path "self.dataBaseRef.child("Posts").child(uid).childByAutoId()"

I have to display post(s) with firebase, the post is on the path:
self.dataBaseRef.child("Posts").child(uid).childByAutoId()
Where
"uid" = FIRAuth.auth()!.currentUser!.uid
dataBaseRef = FIRDatabaseReference! {
return FIRDatabase.database().reference()
}
The publication of the post on Firebase works with success, but I can't get to this path because I can not declare the uid because is specific to each user. How?
Try like this:
let dataBaseRef = FIRDatabase.database().reference(withPath: "Posts")
dataBaseRef.observe(.value, with: { snapshot in
for item in snapshot.children {
let childSnap = item as! FIRDataSnapshot // do something you wants to
// childSnap contains item in Posts. So in cycle you will catch
// every post child node. From here you can get uid's
// and make a new observer
}
})
Hope it helps

Resources