How to retrieve data from different DatabaseReference at same time - ios

I have places in different cities in my real time database (firebase).
Following is my database structure.
But according to firebase tutorial, I tried following code to get data
cityA.queryOrdered(byChild: "completed").observe(.value, with: { snapShot in
var newItems: [AttractionPlace] = []
for child in snapShot.children {
if let snapShot = child as? DataSnapshot,
let attraction = AttractionPlace(snapShot: snapShot) {
newItems.append(attraction)
}
}
print(newItems.count)
})
I also would like to get data from cityB at the SAME TIME and no idea how to retrieve. I know I can repeat same action to cityB. But is there any better way?

I'm not sure how you want your data structured. Can you show me what an AttractionPlace is supposed to look like? The code below does what you want appending the name of each place to newItems, but without specifying from which city it is. Take notice that I did not use your AttractionPlace object.
let dataref = Database.database().reference()
dataref.child("attractions").observe(.value, with: { (snapshot) in
var newItems = [AnyObject]()
for city in snapshot.children.allObjects as! [DataSnapshot] {
let value = city.value as? NSDictionary ?? [:]
for child in value {
let attraction = child.value
newItems.append(attraction as AnyObject)
}
}
print("newItems: ",newItems)
print("newItems.count: ",newItems.count)
})
Result:
newItems: [Place 3, Place 4, Place 1, Place 2]
newItems.count: 4

Related

Access Multiple Nodes in Firebase Database

I am having a hard time pulling all my data from one of my nodes in my firebase database.
Here is how the node looks like in Firebase:
Considerations
-MEdUNZwVrsDW3dTrE6N
Company Description:
Company Image:
Company Name:
Decision Date:
Start Date:
Users
B2Z4DlZ8RucvEQhz2NSUkquqc5P2
Compensation:
PostNumber:
StoryNumber:
Under users there are going to be multiple people with different values for the compensation, post number, and storynumber. I have each user having a node called "user-considerations" that tags the unique id of the consideration each user is attached on and places it under their UID and tags a 1 next to it as the value. I am trying to access each specific user's info along with the other info in the node. Here is my code that I am using to call the information along with the struct I a using to capture the information:
STRUCT:
import UIKit
class ConsiderationInfo: NSObject {
var companyName: String?
var companyImage: String?
var companyDescription: String?
var decisionDate: String?
var startDate: String?
var compensation: String?
var postNumber: String?
var storyNumber: String?
}
CODE FOR OBSERVING INFO:
func updateConsiderationsArray() {
let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference().child("user-considerations").child(uid!)
ref.observe(.childAdded, with: { (snapshot) in
let considerationId = snapshot.key
let considerationReference = Database.database().reference().child("Considerations").child(considerationId)
considerationReference.observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let considerationInfo = ConsiderationInfo()
//self.setValuesForKeys(dictionary)
considerationInfo.companyName = dictionary["Company Name"] as? String
considerationInfo.companyImage = dictionary["Company Image"] as? String
considerationInfo.companyDescription = dictionary["Company Description"] as? String
considerationInfo.decisionDate = dictionary["Decision Date"] as? String
considerationInfo.startDate = dictionary["Start Date"] as? String
self.considerationsInfo.append(considerationInfo)
self.considerationName.append(considerationInfo.companyName!)
self.filteredConsiderations.append(considerationInfo.companyName!)
self.considerationCollectionView.reloadData()
}
}, withCancel: nil)
})
}
I am trying to access the information under the user specific node, i.e. the specific user's compensation post number and story number. I am unaware of how to access all of this to append the struct.
Here is the node with the user-considerations:
As it sits, I am really not seeing anything super wrong with the code but there are few things that could be changed to make it more streamlined.
I would first change the Consideration Info class to make it more self contained. Add a convenience initializer to handle a firebase snapshot directly with some error checking.
class ConsiderationInfo: NSObject {
var companyName = ""
convenience init(withSnapshot: DataSnapshot) {
self.init()
self.companyName = withSnapshot.childSnapshot(forPath: "Company Name").value as? String ?? "No Company Name"
}
}
I would also suggest removing the .childAdded and .observe events unless you specifically want to be notified of future changes. Use .value and .observeSingleEvent instead. Keeping in mind that .childAdded iterates over each node in your database one at a time - .value reads them in all at the same time. If there is a limited amount of data, .value works well.
func updateConsiderationsArray() {
let fbRef = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
let ref = fbRef.child("user_considerations").child(uid)
ref.observeSingleEvent(of: .value, with: { snapshot in
let allUidsSnap = snapshot.children.allObjects as! [DataSnapshot]
for uidSnap in allUidsSnap {
let considerationId = uidSnap.key
let considerationReference = fbRef.child("Considerations").child(considerationId)
considerationReference.observeSingleEvent(of: .value, with: { snapshot in
let considerationInfo = ConsiderationInfo(withSnapshot: snapshot)
self.considerationArray.append(considerationInfo)
// update your collectionView
})
}
})
}
What I am doing in the above is reading in the single node from user_considerations, which looks like this according to your quuestion
user_considerations
some_user_uid
a_user_uid
a_user_uid
and then mapping each child user to an array to maintain order
let allUidsSnap = snapshot.children.allObjects as! [DataSnapshot]
and then iterating over each, getting the uid (the key) of each node and getting that nodes data from the Considerations node.

Print All Children Columns from Firebase - iOS Swift 4

I have 2 records in my users table
This code below
let fcmTokenRef = Database.database().reference().root.child("users").child(id!).child("fcmToken")
fcmTokenRef.observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
})
will print out the token of a child
How do I adjust my code to print all the tokens for all my children?
You can try
let fcmTokenRef = Database.database().reference().root.child("users").observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
let str = $0["fcmToken"] as! String
print(str)
}
})
You’re requesting a onetime read, hence you’re reading the data once. You need to use .childAdded
Try this:
let fcmTokenRef = Database.database().reference().child(“users”)
fcmTokenRef.observe(.childAdded, with: { (snapshot) in
print(">>",snapshot)
guard let data = snapshot as? NSDictionary else {return}
var each_token = data[“fcmToken”] as? String
print(“all tokens: \(each_token!)”)
})
#puf says something very important:
differences between child added and value firebase
The child_added event fires for each matching child under the node that you query. If there are no matching children, it will not fire.

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 ensure that the data is not retrieved and appended as a whole each time a new entry is added?

func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.

How to retrieve objects from firebase by key value

I'm new to firebase and I have such structure of my firebase project
I want to get all objects, that "Interested" value is equal to "men"
I wrote such code, to get all object sorted by interes value:
let thisUserRef = URL_BASE.childByAppendingPath("profile")
thisUserRef.queryOrderedByChild("Interest")
.observeEventType(.Value, withBlock: { snapshot in
if let UserInterest = snapshot.value!["Interest"] as? String {
print (snapshot.key)
}
}
But I receive nil.
you need to loop through all the key-value profiles
if let allProfiles = snapshot.value as? [String:AnyObject] {
for (_,profile) in allProfiles {
print(profile);
let userInterest = profile["Interest"]
}
}
Here _ is the key that is in the format KYXA-random string and profile will be the element for that key.
Edit:
There is querying for child values as per the docs.
Try thisUserRef.queryOrderedByChild("Interest").equalTo("men") and then using the inner loop that i specified in the answer
This is a basic query in Firebase. (Updated for Swift 3, Firebase 4)
let profileRef = self.ref.child("profile")
profileRef.queryOrdered(byChild: "Interest").queryEqual(toValue: "men")
profileRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let dict = child as! [String: Any]
let name = dict["Name"] as! String
print(name)
}
})
The legacy documentation from Firebase really outlines how to work with queries: find it here
Legacy Firebase Queries
The new documentation is pretty thin.
Oh, just to point out the variable; thisUserNode should probably be profileRef as that's what you are actually query'ing.

Resources