how to load Data from multiple child and show in same tableView - swift

Right now my tableview shows some data from a child in Firebase. Depending on which id is selected it only shows the data or comments regarding to this id.
I want to also show in the same tableView data from other id. How can I do that?
With this I get the data from a child with specific place ID.
func datenBankAbfrage () {
let placeIdFromSearch = ViewController.placeidUebertragen
ref = Database.database().reference().child("placeID/\(placeIdFromSearch)")
ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount > 0 {
self.table.removeAll()
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object = video.value as? [String: AnyObject]
let userName = Object?["userName"]
let userGroup = Object?["userGroup"]
let userComment = Object?["userComment"]
let userTime = Object?["userTime"]
let userLikes = Object?["userLikes"]
let commentId = Object?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video = importComment(userName: userName as! String, userGroup: userGroup as! String, userComment: userComment as! String, userTime: userTime as! Int, userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.insert(video, at: 0)
self.tableView.reloadData()
}
}
})
}
So I am searching somethings like
ref = Database.database().reference().child("placeID/\(placeIdFromSearch) & this child and that child ")

The only way to get a subset of the child nodes of a path is with a query, so if the nodes either share a common property (queryEqualToValue(...)) or if they are within a contiguous range (queryStartingAtValue/queryEndingAtValue).
Beyond that it is not possible to load a subset of child nodes with an OR like condition. This means that you will have to load each branch with a separate read operation, and then merge the data together in your application code. This is not nearly as slow as you may expect, since Firebase pipelines the requests together over the same connection.

Related

How to read all child data from firebase

How can i read all child data from Firebase.
let ShopRef = Database.database().reference(withPath: “ShoppingMallLst”).child(“ShoppingMall1”)
ShopRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for data in snapshot.children.allObjects as! [DataSnapshot] {
if let data = data.value as? [String: Any] {
let Description = data["Description"] as? String
let Floor = data[“Floor”] as? Int
….
}
}
}
})
But how can i read the data from child "ShopPath?"
child "ShopPath" has type [String: [String: String]]
you can try:
let ShopPath = data["ShopPath"] as? [String: [String: String]]
The key to firebase is to keep data in snapshots as long as you can. So in this case instead of casting items to dictionaries, which loose ordering or arrays that get more complex as the structure gets deeper, leverage DataSnapshots.
Here's the main function to read all shops in ShoppingMall1 - similar to the code in your question.
func readMallShops() {
let ref = self.ref.child("ShoppingMallList").child("ShoppingMall1")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allShopsSnap = snapshot.children.allObjects as! [DataSnapshot]
for shopSnap in allShopsSnap {
let shop = ShopClass(withSnap: shopSnap)
}
})
}
Then a class that holds data about each shop. Note that I pass each snapshot in to initialize the class
class ShopClass {
var name = ""
var height = ""
convenience init(withSnap: DataSnapshot) {
self.init()
let name = withSnap.childSnapshot(forPath: "ShopName").value as? String ?? "No Shop Name"
print("Shop: \(name)")
self.name = name
let shopPathSnap = withSnap.childSnapshot(forPath: "ShopPath")
let shopChildSnap = shopPathSnap.children.allObjects as! [DataSnapshot]
for childDataSnap in shopChildSnap { //iterate over the array in ShopPath
let height = childDataSnap.childSnapshot(forPath: "Height").value as! String
print(" height: \(height)")
self.height = height
}
}
}
And the output looks like this
Shop name: Test
height: 1,180
Shop name: Test 2
height: 2,000
I left off the other child nodes as if you can read height, you can read the rest. So this just assigns and prints out the shop name and height (as a string).
A suggestion as well. Arrays are not well suited for NoSql databases and their use is very situational (avoid if possible). If you're using an array, there's probably a better structure available.

Load data from firebase to a label?

I have data in firebase that I would like to load into a UILabel with Swift.
My data structure looks like:
like-1bf89addclose
artists
-LP6zVO8iekRMMOWe7nm
artistGenre: pop
artistName: postmalone
id: 920930
And my swift code looks like:
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observe(FIRDataEventType.value, with: {(snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print(dataDict)
})
ref.child("artists").observeSingleEventOfType(.value, with: {(snapshot) in
let artist = snapshot.value!["artistName"] as! String
let genre = snapshot.value!["artistGenre"] as! String
self.artistlLabel.text = artist
self.genreLabel.text = genre
})
}
where's my mistake? I've tried searching online, but most examples only explain how to put input into tableviews, which has a different code I tried to understand and restructure but couldn't. I know there has to be something wrong with my ref but I can't figure it out.
I'm following a youtube tutorial and this works:
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("Users").child(userID).observeSingleEventOfType(.value, with: {(snapshot) in
let email = snapshot.value!["Email"] as! String
let password = snapshot.value!["Password"] as! String
self.emailLabel.text = email
self.passwordLabel.text = password
})
**issue with this code is I don't need that authentication part (no users have to log in on my app, they're just inputing info in).
You're missing the -LP6zVO8iekRMMOWe7nm level in your reference. Try this:
ref.child("artists/-LP6zVO8iekRMMOWe7nm").observeSingleEventOfType(.value, with: {(snapshot) in
let artist = snapshot.value!["artistName"] as! String
let genre = snapshot.value!["artistGenre"] as! String
print("\(artist) \(genre)")
self.artistlLabel.text = artist
self.genreLabel.text = genre
})
If you want to load all artists, you can load /artists and then loop over the results:
ref.child("artists/-LP6zVO8iekRMMOWe7nm").observeSingleEventOfType(.value, with: {(snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let artist = child.value!["artistName"] as! String
let genre = child.value!["artistGenre"] as! String
print("\(artist) \(genre)")
}
})

Swift Create Categories for Items (Classes + Attributes) at runtime

I have a problem I am not able to figure out myself or find any answer to:
I am creating an inventar app for my company and I want that the people are able to create categories for new items like in this example:
Create category at runtime (German Inventar Program)
My problem here is that I use firebase as the backend and - of course - upload all the attributes for the stored items whereas every item belongs to a category with predefined attributes. When I want to work with these items, I download the item from firebase and create an object with these specific attributes from a class I hard-coded at compile time.
I don't want to hard code the class at compile time and change all the functions and if-else blocks where i am checking for the actual category for an item. Now - when adding a new category - I have to change at least 50 places in code to make it work...
For better understanding, here is a function for one category which downloads all the items from firebase:
func downloadMiscsFromFirebase(handler: #escaping (_ miscs: [Miscellaneous]) -> ()) {
var miscs = [Miscellaneous]()
DataService.instance.hardwareRef.child(category).observeSingleEvent(of: .value) { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
let description = snap.childSnapshot(forPath: FirebaseIDs.Item.description).value as! String
let category = snap.childSnapshot(forPath: FirebaseIDs.Item.category).value as! String
let lfdNr = snap.childSnapshot(forPath: FirebaseIDs.Item.lfdNr).value as! Int
let fromTeam = snap.childSnapshot(forPath: FirebaseIDs.Item.fromTeam).value as! String
let hw = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.hw).value as! String
let sw = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.sw).value as! String
let other = snap.childSnapshot(forPath: FirebaseIDs.Miscellaneous.other).value as! String
let givenOut = snap.childSnapshot(forPath: FirebaseIDs.Item.givenOut).value as? Bool
let givenTo = snap.childSnapshot(forPath: FirebaseIDs.Item.givenTo).value as? String
let givenAt = snap.childSnapshot(forPath: FirebaseIDs.Item.givenAt).value as? String
let givenUntil = snap.childSnapshot(forPath: FirebaseIDs.Item.givenUntil).value as? String
let storageLocation = snap.childSnapshot(forPath: FirebaseIDs.Item.storageLocation).value as? String
let misc = Miscellaneous(UID: snap.key,
lfdNr: lfdNr,
description: description,
category: category,
fromTeam: fromTeam,
hw: hw,
sw: sw,
other: other,
givenOut: givenOut,
givenTo: givenTo,
givenAt: givenAt,
givenUntil: givenUntil,
storageLocation: storageLocation)
// Only add item if from the same team.
if fromTeam == self.fromTeam {
miscs.append(misc)
}
}
handler(miscs)
}
}
}
Thank you in advance!
I would create a general CategoryDefinition class/struct and a Category class/struct that looked something like
class CategoryDefinition {
var categoryName: String
var fields: [String: FieldType] //Dictionary with field definitions
}
class Category {
var values: [String: Any?] //Dictionary with field values
}
then two functions to convert from and to Firebase format.
To make it more flexible to work with this I would have a protocol with the properties and functions I mentioned. It would make sense to have a Factory class to generate Category instances from CategoryDefinition.
FieldType above is my made up enum since I assume you have pre-defined set of data types-

Firebase Add New Key/Value to Pre-existing database without crashing xcode

` let query = ref?.child("Reviews").queryOrdered(byChild: "UserID").queryEqual(toValue: myUser.userId)
query?.observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let uid = dict["UserID"] as! String
let review = dict["Body"] as! String
let rating = dict["Rating"] as! String
let titleID = dict["TitleID"] as! String
let reviewID = dict["ReviewID"] as! String
let ratingID = dict["RatingID"] as! String
`
THE ERROR OCCURS AT THE ratingID call to the database. It unwraps nil.
I am trying to adapt a pre existing Firebase database with a new key/value.
I then try to display entries in my tableview and I get a crash with unwrap returning nil. I know why this is happening and it's because the previous data does not have the new key/value I want to include in the node going forward. I have tried many different things such as if let and guard let without much fortune. How do I add new key/Values and still have the tableview read entries that don't have the new value?
I include an image of the current node and I want to add a 'RatingsID' to the node. When I do, I get the unwrap nil error.
Database node prior to new key/value
Your code is super close, just need to protect the code in case the ratingID key doesn't exist.
So change
let ratingID = dict["RatingID"] as! String
to
let ratingID = dict["RatingID"] as! String ?? ""
So if the RatingID node does not exist, you'll set ratingID to an empty string (or whatever string you want)
You could also code it to only operate on the value of that key if the node exists (not nil)
if let someVal = dict["xxx"] {
//do something with someVal
} else {
print("xxx node wasn't found")
}
Here's a complete example: We are reading some messages from the messages node and some of them have a test_key node and some dont. For those that don't, default string is assigned to test
let postsRef = self.ref.child("messages")
postsRef.observe(.childAdded) { snapshot in
let dict = snapshot.value as! [String: Any]
let msg = dict["msg"] as! String
let test = dict["test_key"] ?? "default string"
print(msg, test)
}

reading data on firebase

This is my data structure.
This is how I load club data and its address.
func loadClubs() {
ref = Database.database().reference()
ref.child("club").observe(DataEventType.childAdded, with: { (clubSnapshot) in
if let clubDict = clubSnapshot.value as? [String : AnyObject] {
let name = clubDict["name"] as! String
let explanation = clubDict["explanation"] as! String
let courtNum = clubDict["courtNum"] as! Int
let membershipFee = clubDict["membershipFee"] as! Int
let visitorFee = clubDict["visitorFee"] as! Int
let hasCarParking = clubDict["hasCarParking"] as! Bool
let club2 = Club2(name: name, explanation: explanation, courtNum: courtNum, membershipFee: membershipFee, visitorFee: visitorFee, hasCarParking: hasCarParking)
self.club2Array.append(club2) // Add to clubArray
print(self.club2Array)
self.tableView.reloadData()
}
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key).observe(DataEventType.childAdded, with: { (addressSnapshot) in
if let addressDict = addressSnapshot.value as? [String: AnyObject] {
let clubAddress = ClubAddress(postCode: addressDict["postcode"] as! String, cityName: addressDict["city"] as! String, ward: addressDict["ward"] as! String, address1: addressDict["address1"] as! String, address2: addressDict["address2"] as! String)
self.addressArray.append(clubAddress)
print(self.addressArray)
}
})
})
}
basically, after retrieving each snapshot of club, I get club's key (-KsJB9TkoGNIkiZFGg7), then use that key to retrieve address.
However, print(self.addressArray) doesn't not print anything.
I add a debug breakpoint at if let addressDict = addressSnapshot.value as? [String: AnyObject] { , it does not stop the debug process.
Why is it not calling?
What do I miss here?
Ah! Your code is very close.
Keep in mind that .childAdded iterates over all of the child nodes and loads each one.
In your case, you don't want to iterate over all of the address nodes, you just want one, and don't want to leave an observer.
To do that, we load the specific node child data of the address node, by observeSingleEvent(of: .value. Here's a snippet of the important part.
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key)
.observeSingleEvent(of: .value, with: { (addressSnapshot) in
let dict = addressSnapshot.value as! [String: Any]
let address = dict["address1"] as! String
print(address)