0

In the below model that downloads the images from Firebase Storage it completes the for loop, but the print statement in the completion block shows "[]". However, moments later the downloads arrive & show in the array. How do I wait for the images to fully download & enter into the array before executing the completion & reload a table later?

View Model: Downloads Image


    //Firebase Cloud Storage Reference:
    let storage = Storage.storage()
    
    public var imageArray: [UIImage] = []

    public func fetchProductImages(completion: @escaping (Result<UIImage, Error>) -> Void) {
        
        //Clear Image Array:
        imageArray.removeAll()
        
        for imageRef in products! {
            
            //Access to Image inside a Collection:
            let storageRef = self.storage.reference(withPath: imageRef.image!)
            
            //Download in Memory with a Maximum Size of 1MB (1 * 1024 * 1024 Bytes):
            storageRef.getData(maxSize: 1 * 1024 * 1024) { [self] data, error in
                
                if let error = error {
                    //Error:
                    print (error)
                    
                } else {
                    
                    //Image Returned Successfully:
                    let image = UIImage(data: data!)

                    //Add Images to the Array:
                    imageArray.append(image!)
                    
                }
            }
        }
        
        print (imageArray)
        
        completion (true)
        
    }


Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Captain725
  • 73
  • 1
  • 7
  • Does this answer your question? [Handling asynchronous Firestore data reading in tableView - iOS](https://stackoverflow.com/questions/55504778/handling-asynchronous-firestore-data-reading-in-tableview-ios) – burnsi Feb 28 '22 at 12:22
  • @burnsi That links to great info - however, a completion handler is NOT required, it's just one option. OP: The important part is Firebase Is Asynchronous and firebase data is only valid within the closure following the function. See [this](https://stackoverflow.com/questions/62199268/swift-firebase-storage-code-block-not-executing/62201584#62201584) and maybe [this](https://stackoverflow.com/questions/43027817/how-to-perform-an-action-only-after-data-are-downloaded-from-firebase/43029121#43029121). Those are not Storage answers but the concepts are the same. – Jay Mar 03 '22 at 20:13

1 Answers1

2

Since loading the image data is an asynchronous operation, any code that needs to run after the images have loaded will need to be inside the completion handler, be called from there, or be otherwise synchronized.

A simple way is to count the number of images that have completely loaded, and call once it matches the length of the array:

imageArray.removeAll()

for imageRef in products! {
    
    let storageRef = self.storage.reference(withPath: imageRef.image!)
    
    storageRef.getData(maxSize: 1 * 1024 * 1024) { [self] data, error in
        
        if let error = error {
            print (error)                
        } else {
            
            //Image Returned Successfully:
            let image = UIImage(data: data!)

            //Add Images to the Array:
            imageArray.append(image!)

            // 
            if (imageArray.count == products!.count) {    
                completion (true)                
            }           
        }
    }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • That was definitely it! - However, how do I order the images in a particular way? – Captain725 Mar 01 '22 at 10:40
  • I have a view model as you can see in the link below. How can I save the correct image into the proper store product so the images appear in the rightt order? https://stackoverflow.com/q/71136478/13488513 @Frank van Puffelen – Captain725 Mar 01 '22 at 16:42