I had firebase database structure like this:
Currently I'm fetching the top key which is categories, But I want it to make dynamic when network call when selecting picker based on its categories and display on tableview, Is there any way to do that in swift3? But somehow I figure out by adding it in array.
my question is how do I convert array to multidimensional array?
func getCategories() {
DataService.ds.REF_CATEGORIES.observe(FIRDataEventType.value, with: { snapshot in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for (_, item) in snapshot.enumerated().reversed() {
let key = item.key.capitalized
self.categories.append(key)
print("key --> \(key)")
if let catDict = item.value as? JSONDictionary {
let count = catDict.count
for i in catDict {
if let finalCat = i.value as? JSONDictionary {
let finalKey = i.key
let category = Pizza(categoriesKey: finalKey , categoriesData: finalCat)
self.multiCat.append([category])
print("multiCat --> \(self.multiCat)")
print("i count --> \(count)")
}
}
}
}
}
})
}
Console Log: multiCat --> [[Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,Pizza,]]
What I want is: [[Pizza,Pizza,Pizza], [Pizza,Pizza,Pizza], [Pizza,Pizza,Pizza,]]
Related
I am trying to iterate over the following dictionary:
Dictionary in Firebase
This is my code:
Global.sharedInstance.db.collection("usuarios").getDocuments { (snapshot, error) in
if error != nil {
print("error de lectura usuarios...")
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let txtIdentificador = data["identificador"] as? String ?? ""
let txtBio = data["bio"] as? String ?? ""
let txtNombre = data["nombre_usuario"] as? String ?? ""
let txtFotoPerfil = data["foto_perfil"] as? String ?? ""
var arrFotos = data["fotos"] as? [String: [String:String]]
}
}
}
}
I am able to retrieve the first few lines, like the id, the biography, name, etc.
But when I try to access the array of dictionary I have no idea.
This is the main idea:
I have a set of users, which I iterate over with the first loop 'for document in documents...", then each user has a set of photos. I want to iterate over the 3 photos, and in each iteration I want to retrieve the fields, so I can create a object called Image and associate the user with the 'hasUpload(Image)'.
I would like to know how to iterate over X photos an in each iteration retrieve the fields.
Something like this:
var arrFotos = data["fotos"] as? [String: [String:String]]
for foto in arrFotos {
for (key,value) in foto {
}
}
I get the error: For-in loop requires '[String : [String : String]]?' to conform to 'Sequence'; did you mean to unwrap optional?
A similar StackOverflow case can be found here and this is how they resolved it:
You can either do this, where x is the index and token is the element:
for (x, token) in transcriptCandidate.enumerated() {
}
Or this if you don't need the index:
for token in transcriptCandidate {
}
How dow I map the objects stored in a list in my firebase db to their corresponding objects in swift?
Right now the following isn't working:
for child in snapshot.children{
let tempMed = child as Med
}
You cannot directly get custom object from FIRDataSnapshot what you do is create one init with your custom class or struct and use that to create object from FIRDataSnapshot.
Ex
struct Med {
let title: String
let desc: String
init?(snapshot: FIRDataSnapshot) {
guard let dic = snapshot.value as? [String:Any],
let title = dic["title"] as? String,
let desc = dic["description"] as? String else {
return nil
}
self.title = title
self.desc = desc
}
}
Now get array of Med this way.
let meds = snapshot.children.flatMap { Med(snapshot: $0 as! FIRDataSnapshot) }
I added refreshing capabilities for my tableView when the user pulls down on it. This works most of the time but occasionally I'll get a fatal error, Index out of range. The callback thats called to refresh the table does the following:
1) Empty's out data structures holding data to be displayed
2) Fetch data from Firebase database
3) Parse data into individual parts and insert into data structures
4) Refresh the table
However, in the cellForRowAt indexPath function of my tableView I'm getting the fatal error on the first line of the function:
let eventsOnDay = eventsForDate[allDates[indexPath.section]]!
Here's my code for the refresh callback:
var allDates = [DateStruct]() // Holds all unique dates of events
var eventsForDate = [DateStruct : [PetEvent]]() // Holds all events for each day
/// Read events from db, split into individual dates
///
func readEventsFromDb() {
// 1. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 2. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
// 3. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.eventsForDate[eventDate] != nil) {
self.eventsForDate[eventDate]!.append(event)
} else {
self.eventsForDate[eventDate] = [event]
}
}
}
}
self.allDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 4. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
I'm having a hard time figuring out why this is working most of the time but occasionally fails. Does anyone have an idea?
If your goal is to avoid the crash you can safely check for your eventOnDelay item by doing something like this:
if allDates.count > indexPath.section {
guard let eventsOnDay = eventsForDate[allDates[indexPath.section]] else {
//handle error here...
return
}
//Do whatever you need with eventsOnDay here
} else {
//Check your numberOfSections method, as it's not set up correctly
}
You also definitely need to make sure you're calling tableView.reloadData() and endRefreshing on the main thread as your Firebase callback likely comes back on a bg thread.
check wether the array(containing data to display) is not empty in your tableview's delegate and datasource method.
i.e. in cellForRow method
check if(yourArray.count > 0){
// Do your code
}
else{
// Dont
}
You can use different approach to update the table view content. I think there is delay getting data from firebase you are using callback dbRef.observeSingleEvent. you are removing the previous data at the beginning of the function call. you can try following two approaches to fix the issue
1. Remove empty data in dbRef.observeSingleEvent callback
var allDates = [DateStruct]() // Holds all unique dates of events
var eventsForDate = [DateStruct : [PetEvent]]() // Holds all events for each day
// Read events from db, split into individual dates
func readEventsFromDb() {
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 1. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
// 2. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
// 3. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.eventsForDate[eventDate] != nil) {
self.eventsForDate[eventDate]!.append(event)
} else {
self.eventsForDate[eventDate] = [event]
}
}
}
}
self.allDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 4. Check if this is on UI/Main thread. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
2. Use temporary arrays to hold the data and then update the main data with temporary array
//Read events from db, split into individual dates
func readEventsFromDb() {
let dbRef = FIRDatabase.database().reference().child("pets").child(currentPet).child("events")
// 1. Fetch data from db
dbRef.observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
var tempAllDates = [DateStruct]() // Holds all unique dates of events
var tempEventsForDate = [DateStruct : [PetEvent]]()
// 2. Split data into individual components
for child in snapshots{
if let data = child.value as? [String: Any] {
if let c = data["comment"] as? String, let p = data["user"] as? String, let t = data["type"] as? Int, let d = data["date"] as? UInt64 {
let event = PetEvent(comment: c, person: p, type: t, time: self.timeFromEpoch(time: Double(d)))
let eventDate = self.dateFromEpoch(time: Double(d))
if (self.tempEventsForDate[eventDate] != nil) {
self.tempEventsForDate[eventDate]!.append(event)
} else {
self. tempEventsForDate[eventDate] = [event]
}
}
}
}
self. tempAllDates = Array(self.eventsForDate.keys).sorted {d1,d2 in
d2 < d1
}
// 3. Empty out data structures
eventsForDate.removeAll()
allDates.removeAll()
// 4. Fill data with temporary array. check the size
if tempEventsForDate.count > 0{
eventsForDate = tempEventsForDate
}
if tempAllDates.count > 0{
allDates = tempAllDates
}
// 5. Check if this is on UI/Main thread. Reload table
self.feedTable.reloadData()
self.refreshControl.endRefreshing()
}
})
}
Hope this approaches will help to solve the issue :)
I am trying to print array from the firebase. Actually if we tap a medication in a list(tableviewcontroller), it will show its specfic dosages. I got stucked to retrieve the dosages list. Here is my code to get data from firebase. Any help is appreciated. Thanks in advance. My firebase structure looks like this.. firebase img
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observeEventType(.Value, withBlock: { snapshot in
for item in snapshot.children{
FIRDatabase.database().reference().child("medication").child("options").observeEventType(.Value, withBlock: {snapshot in
print(snapshot.value)
})
}
})
You should take a look on firebase documentation https://firebase.google.com/docs/database/ios/read-and-write
but if I'm understanding your idea, you probably has a model class for your medications. So, to retrieve your data you should do like this for Swift 3.0:
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observe(.value, with: { (snapshot) in
for item in snapshot.children{
// here you have the objects that contains your medications
let value = item.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let dossage = value?["dossage"] as? String ?? ""
let type = value?["type"] as? String ?? ""
let options = value?["options"] as? [String] ?? ""
let medication = Medication(name: name, dossage: dossage, type: type, options: options)
// now you populate your medications array
yourArrayOfMedications.append(medication)
}
yourTableView.reloadData()
})
}
Now that you have your array with all your medications, you just need to populate your tableView with this medications. When someone press an item on table you can just call prepareForSegue: and send your yourArrayOfMedications[indexPath.row].options to the next view
The solution is same as above but with a small change.
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observe(.value, with: { (snapshot) in
for item in snapshot.children{
// here you have the objects that contains your medications
let value = item.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let dossage = value?["dossage"] as? String ?? ""
let type = value?["type"] as? String ?? ""
let options = value?["options"] as? [String : String] ?? [:]
print(options["first"]) // -> this will print 100 as per your image
// Similarly you can add do whatever you want with this data
let medication = Medication(name: name, dossage: dossage, type: type, options: options)
// now you populate your medications array
yourArrayOfMedications.append(medication)
}
yourTableView.reloadData()
})
}
Hi I am new to Swift and Firebase here and I am trying to append the objects I get into the array I have created. But when I return the array, it returns nil even when I print the array it shows the values are stored inside.I tried to assign it to another array but it still returns nil. Please help me as I am currently a newbie to Swift and Firebase and need some guidance. Thanks in advance.
func retrieveData() -> Array<AnyObject>{
var ref = Firebase(url: "my_firebase_url")
ref.queryOrderedByChild("datetime").observeEventType(.Value, withBlock: {
snapshot in
if let i = snapshot.value as? NSDictionary {
for item in i {
if let value = item.value as? NSDictionary {
self.adArray.append(value)
println(self.adArray[0])
println(self.adArray.count)
self.codeNum = value["code"] as! String
self.datetime = value["datetime"] as! String
self.nameEvent = value["name"] as! String
println("code is \(self.codeNum) and name is \(self.nameEvent) at \(self.datetime)")
}
}
}
})
println(adArray.count)
return adArray }