Firebase Data Retrieval - Edit Annotation

Firebase Data Retrieval - Edit Annotation

To read data, you want to use the observe attribute from your firebase reference. This returns a snapshot consisting of your data.

Since your Skatepark class already has a snapshot constructor, all you'd have to do is pass the snapshot to it and create your object.

reference.child("yourNode").observeSingleEvent(of: .value, with: { (snapshot) in

/* Two things can happen here. Snapshot can either consist of one element or multiple.
To handle this, you want to iterate through the snapshot, create your Skatepark
object and populate it into your global skatepark array.
*/

if snapshot.value is NSNull
{
print("Error Getting Snapshot")
}
else
{
for (child in snapshot.children)
{
let snap = child as! FIRDataSnapshot
let skateObject = Skatepark(child) // this calls the constructor which take snapshot as parameter
globalSkateParkArray.append(skateObject)
}
}

})

To update your values, this is all you need.

Personal Recommendations

  1. Consider making coordinate, name, subtitle into var instead of let.
  2. Consider creating a Skatepark object first out of the data before sending it to the database. This constructor should do the trick

    init(coordinate: CLLocationCoordinate2D, name: String, subtitle: String, type: SkateboardType, editable: Bool)
    {
    self.coordinate = coordinate
    self.name = name
    self.subtitle = subtitle
    self.type = type
    self.editable = editable
    }
  3. I'd create a dictionary variable inside Skatepark that creates a dictionary from the object. This way I won't have ["lat": locationManager.location?.coordinate.latitude, "lng": locationManager.location?.coordinate.longitude, "name": skateTitleText, "subtitle": skateStyleText, "type": (selected - 1), "editable": true] floating all around my codebase.

So have this in your class

var dictionary: [String:Any]
{
return
[
"lat": coordinate.latitude,
"lng": coordinate.longitude,
"name": title,
"subtitle": subtitle,
"type": type,
"editable": editable
]
}

Every time you'd want a dictionary out of your object, simply use object.dictionary

Retrieve data from Firebase before adding to a map

The second observer in your code is using .childAdded, which means that closure gets called for each individual child node under clinicas. This makes it hard to know when you're done with clinics, so I recommend first switching that observer over to .value, with something like:

clinicas.observe(DataEventType.value) { (snapshots) in
for child in snapshots.children {
let snapshot = child as! DataSnapshot

let dados = snapshot.value as? NSDictionary
//print("Dados na leitura \(dados)")
let clinica = Clinica()
clinica.identificador = snapshot.key
clinica.nome = dados?["nome"] as! String
clinica.endereco = dados?["endereco"] as! String
clinica.cidade = dados?["cidade"] as! String
clinica.cep = dados?["cep"] as! String
clinica.estado = dados?["estado"] as! String
clinica.latitude = dados?["latitude"] as! String
clinica.longitude = dados?["longitude"] as! String
clinica.urlImagem = dados?["urlImagem"] as! String
clinica.idImagem = dados?["idImagem"] as! String
self.clinicasR.append(clinica)
}
// At this point we're done with all clinics
}

As you'll have notice this code now uses a loop to iterate over snapshots.children. So we're processing all child nodes of clinicas in one closure, which makes it much easier to know when we're done.


Now with the above change we have two closures that get called individually, and you have some code that you want to run when both of them are done. There are a few ways to synchronize this code.

The first way is what you've already tried: putting the clinicas.observe block into the closure of usuarios.child(idUsuarioLogado).observe(. Now that the clinics observer uses .value, you'll find that this is much easier to get working.

But I'd like to show an alternative below, where we use two simple flags to determine if both closures are done. For this you will have to put the code that runs after the data is loaded into a separate function, something like this:

func addAnnotationsToMap() }
for oneObject in self.todasAnotacoes {
...
}
}

Now in each of the two closures, we're going to set a flag when they're done. And then at the end of each of the closures, we'll detect whether both flags are set, and then call the new function if we have all data we need.

So at the start of viewDidLoad we define our two flags:

let isUserLoaded = false
let isClinicsLoaded = false

And then at the end of loading the user:

usuarios.child(idUsuarioLogado).observe(DataEventType.value) { (snapshot) in
let dados = snapshot.value as? NSDictionary

let emailUsuario = dados?["email"] as! String
let nomeUsuario = dados?["nome"] as! String
let perfilUsuario = dados?["perfil"] as! String
let idUsuario = snapshot.key

let usuario = Usuario(email: emailUsuario, nome: nomeUsuario, uid: idUsuario, perfil: perfilUsuario)

isUserLoaded = true
if (isUserLoaded && isClinicsLoaded) {
addAnnotationsToMap();
}
}

And similar code (setting the isClinicsLoaded flag) to the other closure.

With these in place, you'll start loading the user data and clinics when the code runs, and then whichever one of them completes last, will call the new addAnnotationsToMap function.


Another alternative is to use a DispatchGroup, which is an Apple-specific construct that can also make this much simpler. Read more about it in this answer (and the links from there): Wait until swift for loop with asynchronous network requests finishes executing

Firebase data retrieve and passing it from fragment to new activity not working

In your adapter you dont need to add valueeventlistner , all you need is to pass data to descriptionactivity as shown below

 holder.viewmore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, JobDescription.class);
intent.putExtra("JOBTITLE", jobs.getJobtitle);
intent.putExtra("COMPANY", jobs.getcompany);

context.startActivity(intent);
}

});


Related Topics



Leave a reply



Submit