0

Firebase Structure

enter image description here

Hi, I am try to work out how to query jobBrand & jobName in my Firebase Database (Structure Attached) and store it in a tableView. I am going to store more information under each Job Brand so I would like to keep the structure this way if possible?

So far, I can get tableView to read the 2 fields if they are one level up, so the structure would be:

tbaShootApp -> Jobs -> Job Brand > data.

I cannot work out to query down another level and store it in the tableView. Is this possible?

I am using a dictionary to store the job information:

class Job: NSObject {
var id: String?
var jobBrand: String?
var jobName : String?
var director : String?
var jobInfo : jobInfo?
init(dictionary: [String: AnyObject]) {
    self.id = dictionary["id"] as? String
    self.jobBrand = dictionary["jobBrand"] as? String
    self.jobName = dictionary["jobName"] as? String
    self.director = dictionary["Director"] as? String
}

}

Here is the code to query the data - I have the function 'fetchJobs' in my superViewDidLoad.

func fetchJobs() {

    Database.database().reference().child("Jobs").observe(.childAdded) { (snapshot) in

        if let dictionary = snapshot.value as? [String: AnyObject] {

                let job = Job(dictionary: dictionary)
                job.id = snapshot.key
                self.jobs.append(job)

                //this will crash because of background thread, use dispatch_async to fix
                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })
            }
        }
    }

JSON

{
  "Jobs" : {
    "Candycrush" : {
      "cameraInfo" : {
        "cameraBody" : "A",
        "cameraBrand" : "Arri",
        "cameraType" : "Alexa"
      },
      "jobInfo" : {
        "jobBrand" : "CandyCrush",
        "jobName" : "Winter"
      }
    },
    "Honda" : {
      "cameraInfo" : {
        "cameraBody" : "A",
        "cameraBrand" : "Arri",
        "cameraType" : "Alexa XT"
      },
      "jobBrand" : "Honda",
      "jobName" : "Comet"
    },
    "WWF" : {
      "cameraInfo" : {
        "cameraBody" : "A",
        "cameraBrand" : "Red",
        "cameraType" : "Epic"
      },
      "jobInfo" : {
        "jobBrand" : "WWF",
        "jobName" : "Eye"
      }
    }
  }
}
spoax
  • 471
  • 9
  • 29
  • I think I need to change my dictionary set up so it looks down the correct child, but that is a total guess? Also if that is right I don't know what it should be? – spoax Dec 22 '17 at 12:01
  • To read the lower level: `Database.database().reference().child("Jobs/Job Brand").observe(.childAdded)` – Frank van Puffelen Dec 22 '17 at 14:44
  • Thanks Frank van Puffelen but “Job Brand” will be called something by the user and each “Job Brand” created will have a different name. So I won’t know what that is called each time. Is there a way to bypass that level or only look for every “jobInfo” folder that exists? – spoax Dec 22 '17 at 15:25
  • 1
    Is `jobInfo` always the same name? Or is that also dynamic? (hint: providing a snippet of actual JSON would make this kind of confusion much less likely) – Frank van Puffelen Dec 22 '17 at 15:33
  • Hi Frank van Puffelen, I have attached a JSON to the post to help understand my database. jobInfo is always the same name. The level above which jobName will be dynamic. – spoax Dec 22 '17 at 16:06
  • OK, then it's a matter of querying `Database.database().reference().child("Jobs").queryOrdered(byChild: "jobInfo/jobBrand").queryEqual(toValue: "WWF").observe(.childAdded)` – Frank van Puffelen Dec 22 '17 at 16:29

2 Answers2

1

There's a long way to go but maybe this will help:

 .child("Jobs").observe(.childAdded)

It will let you know each time a new Jobs is added (Candycrush, Honda etc).

(Note - apart from anything else, you very likely also want to observe removals on that list, also.)

If you are making a table (or whatever ... some sort of list, paging thing, collection of tabs, or the like):

almost certainly each row of the table will, on it's own, want to observe that job.

So the first row, would observe for changes in Jobs/Candycrush

So the second row, would observe for changes in Jobs/Honda

And so on. Each table row (or screen, panel, bubble, tab, or whatever it is) would, on it's own, observe that thing.

Incidentally, almost certainly the "first level" header there (where you have Honda, Candycrush etc) would be an id string. (Just use a UUID, or let Firebase do it.) And then a field "Title" would be Honda etc. It would be very unusual to use the actual title as the sort of ID there. (Note that, apart from anything else, you then can't change/edit the title!).

Fattie
  • 27,874
  • 70
  • 431
  • 719
0

UPDATE

Since you have added a lot more information since I posted my answer. It is now obvious that you want to query deeper. Additionally, since you posted the actual JSON it is now obvious that the JobBrand node is dynamic. You can query one level below an unknown child. You can read about that here.

I'm going to change .observe(.childAdded) to .observeSingleEvent(of: .value) because you are querying so deep I doubt you want to observe that node, but I could be wrong. Changing this will pull the data in once and only once. If you want to update that value you will have to query again.

You want a query like this:

Database.database().reference().child("Jobs").queryOrdered(b‌​yChild: "jobInfo/jobBrand").queryEqual(toValue: "JobBrandYouWant").observeSingleEvent(of: .value, with: { snapshot in
    if let dictionary = snapshot.value as? [String: AnyObject] {
        let job = Job(dictionary: dictionary)
        job.id = snapshot.key
        self.jobs.append(job)
    }
})

ORIGINAL ANSWER

You current query is going to return all of the data under jobs. Which includes the nodes Job Brand -> jobinfo -> etc.

If you are trying to get less data then you need to know either the JobBrand, jobinfo or both. However, I think you want all of the jobs and your query just isn't working.

You current query fails because your dictionary contains everything under "Jobs" not just one job. You want to loop over the data snapshot and get each job before you call init.

Database.database().reference().child("Jobs").observe(.childAdded, with: { snapshot in 
    for child in snapshot.children { // over over the snapshot and cast each "job" to it's own dictionary
        let child = child as? DataSnapshot
        if let key = child?.key {
            if let dictionary = child?.value as? [String: AnyObject] {
                let job = Job(dictionary: dictionary)
                job.id = key
                self.jobs.append(job)
            }
        }
    }
})

The documentation provides this example for looping over a snapshot

_commentsRef.observe(.value) { snapshot in
  for child in snapshot.children {
    ...
  }
}
DoesData
  • 6,594
  • 3
  • 39
  • 62
  • Thank you for getting back to me. I think at this level of the app I only want to find out the jobBrand and jobName. Then when you go into each job there will be a lot more information, attachments etc. Is it worth setting up a separate dictionary just for this tableview? – spoax Dec 22 '17 at 14:18
  • If it helps, each job Brand at the top level will have a jobInfo node underneath it, so is there a way I can query the jobInfo node if I don't know the job Brand node? – spoax Dec 22 '17 at 14:27
  • Yes you can do that. Look at this question https://stackoverflow.com/questions/40713137/querying-below-autoids-in-firebase – DoesData Dec 22 '17 at 14:47
  • Great! Thanks @DoesData – spoax Dec 23 '17 at 10:30