Search code examples
swiftfirebaseuipickerview

Read from Firebase to UIpickerView


I have some trouble doing the following:

  1. Read from Firebase database.
  2. Add the information to my UIpickerView as "options"
  3. Show "options" in the app.

What I want to achieve is to read from "Time report" and display the "user names" as "options" in the UIpickerView (in the "dropdown menu").

(The plan is to later be able to display the information in each of the "options" in the same view. Something like choosing a "User" in a app and having the users "stats" displayed.)

Any help would be great!

This is my code: Well for now, I dont really have code as everything i'v tried seems not to work. Last working code was:

    func fetchUser()
{
    ref = Database.database().reference() //Set the reference//

    databaseHandle = ref?.child("Time report").observe(.childAdded, with: { (snapshot) in

        print (snapshot)
    })
}

My database looks like: (Login is the first node. Meaning its the node closest to the root - if that makes any sens.) enter image description here enter image description here

My Results Well, for now - I only get Fred, Kalle and Ivar printed, with all their information...

I've tried:

  • To change "childAdded" to Value. No go..
  • Make it work with only one node. Meaning just having "Ivar" and a couple of random values. Printed in console - but nothing in the pickerview.

Used this video to aid in create a pickerView.


Solution

  • Here's how it's done. I am assuming you know how to populate a dataSource for your pickerView in general. If you need help with that, it would be best addressed as a separate question.

    Assume we are using a structure like the one in the original question:

      time_report
        adam
          gym
            timestamp_0
              From : "from_0",
              To : "to_0"
            timestamp_1
              From : "from_1",
              To : "to_1"
          home : {
            timestamp_0
              From : "from_0",
              To : "to_0"
          work : {
            timestamp_0
              From : "from_0",
              To : "to_0"
    

    Notes:

    1) best practice is to not use spaces in keys so I am using time_report
    2) It's not clear if under each option (gym, home, work) there will be
          multiple nodes so I included two under gym for demonstration purposes
    3) I am using placeholders for timestamps from_0, to_0 etc but you get the idea.
    4) This will handle any situation for options; they could be gym, home, work
           or A, B, C or whatever the key names are.
    5) Nothing is hard coded so as timestamps change, the code continues to work
    

    Now to read the options (gym, home, work or whatever they may be) for the adam node here's the code.

    let timeReportRef = self.ref.child("time_report")
    let adamRef = timeReportRef.child("adam")
    adamRef.observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let snap = child as! DataSnapshot
            let nodeKey = snap.key //will be gym, home, work
            print(nodeKey)
            //populate your dataSource Array here with the nodeKeys
            //the following code could be used to populate a structure with additional
            //  info so when the user taps 'gym', we have already loaded the gym data
            //   and can populate another view controller with the details
            for childTimes in snap.children { //the child nodes under gym, home, work
                let timeSnap = childTimes as! DataSnapshot
                let timeKey = timeSnap.key
                let dict = timeSnap.value as! [String: Any]
                let from = dict["From"] as! String
                let to = dict["To"] as! String
    
                print("  timestamp: \(timeKey)")
                print("     from: \(from)")
                print("       to: \(to)")
            }
        }
    })
    

    and the output

    gym
      timestamp: timestamp_0
         from: from_0
           to: to_0
      timestamp: timestamp_1
         from: from_1
           to: to_1
    home
      timestamp: timestamp_0
         from: from_0
           to: to_0
    work
      timestamp: timestamp_0
         from: from_0
           to: to_0
    

    The important bit here is that if you just want to add gym, home, work to your pickerView, you can omit the For loop. However. That loop demonstrates how to load additional data (timestamps) under gym, home and work.

    There's a couple of directions to go from here. If you want to load the firebase data from Firebase when the user taps gym for example, you would get what the user tapped (gym) and you are in the adam section so it would be

    let user = userSection //assume we are in the 'adam' section
    let option = optionTapped //assume the user tapped gym
    
    let userOptionRef = self.ref.child(user).child(option)
    userOptionRef.observeSingleEvent(.value.... etc etc to read in that data.
    

    Another option would be to read it all in at one time, as in the code I provided)\, and populate a class with that info. Then store the class in a dataSource array to be used in the picker.

    class userStuff {
       option = ""
       data: DataSnapshot?
    }
    

    populate that class or stuct within the closure I presented and then store it in an array. Then in your pickerView, as it's being updated, get the userStuff class from the array for the row and read the option text. If the user then taps on row 0, 'gym' read the array, row 0, get the dataSnapshot and iterate over it to populate a sub view with the timestamp info.

    Edit

    Original question was changed and in response to a comment here:

    let timeReportRef = self.ref.child("Time report")
    timeReportRef.observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let snap = child as! DataSnapshot
            print(snap.key)
        }
    })
    

    output will be

    Fred
    Ivar
    Kalle