2016-07-10 2 views
3

J'essaie d'extraire des données de deux tables Firebase différentes. Voici la structure de la table:Récupère des données depuis Firebase en joignant des tables dans iOS

Post { 
    1{ 
     pImages{ 
      i1:true 
      i2:true 
     } 
    } 
    2{ 
     pImages{ 
      i3:true 

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

je dois récupérer des images correspondant à l'emploi avec id = 1. Ce qui suit est ma mise en œuvre pour récupérer des images:

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) 
     }) 
    } 

Mais, le problème est que je suis pas en mesure d'obtenir toutes les images dans imgArray pour pouvoir envoyer à completion handler. Voici la sortie de l'appel retrieveImagesForPost avec l'ID de poste == 1.

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

Les images sont récupérées après la completion handler est appelée. J'ai essayé le dispatch groups et l'approche semaphores comme décrit dans le suivant post. Mais les résultats sont toujours les mêmes. Comment puis-je faire completion handler pour attendre que toutes les images soient récupérées à partir de Firebase?

Répondre

4

Conservez un compteur que vous augmentez au fur et à mesure que chaque image est chargée. Une fois que le compteur atteint la longueur de la liste snapshot.children, vous avez terminé et appelez votre gestionnaire de complétion.

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) 
      } 
     }) 
    } 
}) 

Vous devriez probablement ajouter un traitement d'erreur dans ce qui précède, mais en général cette approche est essayée et testée.

+0

comment votre fonction changement je aurais besoin de se joindre 3 noeuds (relations) dans Firebase – Learn2Code

+0

si (contre == snapshot.childrenCount) renvoie une erreur !? – Learn2Code

1

Une autre réponse à ce problème consiste à utiliser les codes DispatchGroup de GCD. Pour commencer, vous devez créer un groupe de répartition avec DispatchGroup. Dans ce cas, vous devez indiquer manuellement au groupe lorsque le travail est démarré avec enter() et lorsqu'il est terminé avec leave(). Ensuite, le notify(queue:execute:) du groupe d'expédition exécutera le gestionnaire d'achèvement dans la file d'attente principale.

Soyez prudent! Le nombre d'entrées et de feuilles doit être équilibré ou la notification du groupe d'expédition ne sera jamais appelée.

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) 
}) 
+0

C'est beaucoup mieux que d'utiliser un compteur, comme dans la réponse acceptée. Bien que, pour qu'il fonctionne correctement, le 'dispatchGroup' doit être instancié dans le premier bloc d'observation, et notifié après le' for'. Entrer et sortir du groupe est correct. –