Home > Net >  Firebase swiftUI waiting for getData to retrieve image from Storage
Firebase swiftUI waiting for getData to retrieve image from Storage

Time:06-03

I have a collection that stores posts. Each post has some strings and references to images. I first call getDocuments and within that I call getData to get the corresponding image to a document from the stored reference. At the end I want to append an Object that contains the strings and the UiImages of this post. However, the Object I append contains the strings but the images are empty since it it does not wait on the firebase getData method. How can I make this work, I am fairly new to swift and firebase.

Here is some example code:

query.getDocuments { snapshot, error in
   if error == nil{
       if let snapshot = snapshot {
           for doc in snapshot.documents{
               
           }
               var imageUrls = [String]()
               imageUrls.append(contentsOf: [doc["imageOne"] as? String ?? "", doc["imageTwo"] as? String ?? "", doc["imageThree"] as? String ?? "", doc["imageFour"] as? String ?? "", doc["imageFive"] as? String ?? ""])
               
               var images = [UIImage]()
               for path in imageUrls{
                   
                   let storageRef = Storage.storage().reference()
                   let fileRef = storageRef.child(path)
                   
                   fileRef.getData(maxSize: 5 * 1024 * 1024) { data, error in
                       if error == nil && data != nil{
                           if let image = UIImage(data: data!){
                               images.append(image)
                           }
                       }
                       else{
                           //handle error
                       }
                   }
                   
               }
           
               //I want to wait here till the getData method is finished so I dont get the error
                // "index error" because images[] is empty
           
           loadedPost.append(LoadedPost(id: doc.documentID, Userid: doc["userID"] as? String ?? "", timestamp: doc["timestamp"] as? String ?? "", imageOne: images[0], imageTwo: images[1], imageThree: images[2], imageFour: images[3], imageFive: images[4]))
           }
       }     
   }

CodePudding user response:

The reason it's not being included is because you're appending the image asynchronously inside of the closure. It should look like this.

fileRef.getData(maxSize: 5 * 1024 * 1024) { data, error in          
   if error == nil && data != nil{
                   if let image = UIImage(data: data!){
                       images.append(image)
                       //APPEND ENTIRE THING HERE
                   }
               }
               else{
                   //handle error
               }
           }
       }

To make things a bit clearer, here's your. "Flow Of Data" as I call it.

getDocuments(..) -> Async (Everything below here is contained in this call.)
looping over docs (Nothing inside brackets?) -> Sync 
appending urls -> Sync
looping over urls -> Sync
getData(...) -> Async
appending to loadedPost -> Sync 

Looking at it like that if you have any sync stuff happening AFTER an Async call, then you will run it immediately. So your code could run like this. Why? Because Async calls (Mostly Closures) can happen at any time, and their callback is not guaranteed.

getDocuments(...)
loop over docs
append urls
loop over urls
append to loadedPost
//Some other stuff could happen elsewhere.
//Even more stuff might happen.
getData(..)

Returning

This is a way that you can return from an async call after it's finished. You can also pass values in your completion if you want data to be returned as well.

func someFunc(string: String, completion: @escaping () -> void {
     someClass.someAsyncCall(..) { data, error in 
         if error == nil {
              completion()
         }
     }
}

class someClass {
     func someOtherFunc() {
         //Calling our custom completion used above.
         yourObject.someFunc(someString) { _ in 
             //Function async was finished.
         }
     }
}

CodePudding user response:

Try this out:

query.getDocuments { snapshot, error in
           if error == nil{
               if let snapshot = snapshot {
                   for doc in snapshot.documents{
                       
                   }
                       var imageUrls = [String]()
                       imageUrls.append(contentsOf: [doc["imageOne"] as? String ?? "", doc["imageTwo"] as? String ?? "", doc["imageThree"] as? String ?? "", doc["imageFour"] as? String ?? "", doc["imageFive"] as? String ?? ""])
                       
                       var images = [UIImage]()
                       let group = DispatchGroup()
                       for path in imageUrls{
                           group.enter()
                           let storageRef = Storage.storage().reference()
                           let fileRef = storageRef.child(path)
                           
                           fileRef.getData(maxSize: 5 * 1024 * 1024) { data, error in
                               if error == nil && data != nil{
                                   if let image = UIImage(data: data!){
                                       images.append(image)
                                       group.leave()
                                   } else {
                                       group.leave()
                                   }
                               }
                               else{
                                   group.leave()()
                                   //handle error
                               }
                           }
                           
                       }
                       group.notify(queue: .main) {
                           loadedPost.append(LoadedPost(id: doc.documentID, Userid: doc["userID"] as? String ?? "", timestamp: doc["timestamp"] as? String ?? "", imageOne: images[0], imageTwo: images[1], imageThree: images[2], imageFour: images[3], imageFive: images[4]))
                       }
                   }
               }
           }
  • Related