Obtenga datos de Firebase uniéndose a las tablas en iOS

Estoy intentando get datos de dos tablas diferentes de Firebase. Aquí está la estructura de la tabla:

Post { 1{ pImages{ i1:true i2:true } } 2{ pImages{ i3:true } } } Images{ i1{ iUrl : .... pId : 1 } i2{ iUrl :... pId : 1 } i3{ iUrl:.... pId : 2 } } 

Necesito recuperar las imágenes correspondientes a la publicación con id = 1. La siguiente es mi implementación para recuperar imágenes:

  func retrieveImagesForPost(postId: String,completion: (result: AnyObject?, error: NSError?)->()){ var imgArray:[Image]=[] let postsRef = self.ref.child("post") let imagesRef = self.ref.child("image") let postImagesRef = postsRef.child(postId).child("pImages"); postImagesRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in for item in snapshot.children{ imagesRef.child(item.key).observeSingleEventOfType(.Value, withBlock: { (snap) in let image = Image(snapshot: snap) print(image) imgArray.append(image) }) } print(snapshot.key) print("called") completion(result:imgArray, error:nil) }) } 

Pero, el problema es que no puedo hacer que todas las imágenes en imgArray puedan enviarse al completion handler . A continuación se muestra el resultado de la llamada retrieveImagesForPost con la publicación id == 1.

 pImages called <TestProject.Image: 0x7f9551e82000> <TestProject.Image: 0x7f955466a150> 

Las imágenes se recuperan una completion handler se llama al completion handler . Probé los dispatch groups y el enfoque de semaphores como se describe en la siguiente publicación . Pero los resultados siguen siendo los mismos. ¿Cómo puedo hacer que el completion handler espere que todas las imágenes se obtengan de Firebase?

Mantenga un contador que aumente a medida que se carga cada image. Una vez que el contador scope la longitud de la list snapshot.children , habrá terminado y llamará al manejador de finalización.

 let postImagesRef = postsRef.child(postId).child("pImages"); postImagesRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in var counter = 0 for item in snapshot.children{ imagesRef.child(item.key).observeSingleEventOfType(.Value, withBlock: { (snap) in let image = Image(snapshot: snap) print(image) imgArray.append(image) counter = counter + 1 if (counter == snapshot.childrenCount) { completion(result:imgArray, error:nil) } }) } }) 

Probablemente debería agregar un poco de event handling errores en lo anterior, pero en general este enfoque se testing y se testing.

Otra respuesta para este problema es usar el DispatchGroup de GCD.

Primero necesita crear un grupo de despacho con DispatchGroup . En este caso, debe decirle manualmente al grupo cuando se inicie el trabajo con enter() y cuando termine con leave() . Luego, la notify(queue:execute:) del grupo de despacho notify(queue:execute:) ejecutará el manejador de finalización en la queue principal.

¡Ten cuidado! El número de inputs y salidas debe equilibrarse o la notificación del grupo de despacho nunca será invocada.

 let dispatchGroup = DispatchGroup() let postImagesRef = postsRef.child(postId).child("pImages"); postImagesRef.observeEventType(FIRDataEventType.value, withBlock: { (snapshot) in for item in snapshot.children{ dispatchGroup.enter() imagesRef.child(item.key).observeSingleEventOfType(.value, withBlock: { (snap) in let image = Image(snapshot: snap) print(image) imgArray.append(image) dispatchGroup.leave() }) } }) dispatchGroup.notify(queue: DispatchQueue.main, execute: { completion(result: imgArray) })