Search code examples
iosswiftdownloadaddsubview

How can I check when all my images have downloaded?


I am downloading a couple of images and cache them. Then I am recalling the cached images and add them as a subView to my scrollView (which is horizontal using a page controller). I want to do both of that when view appears without any other actions required. Unfortunately, it already adds the subview before the images were cached, because it goes through the code while the images are getting downloaded. It follows that nothing is added as a subView.

It works when I download and cache the images when the view appears and I add them as a subView when I press a button, but I would like for that to work automatically.

I am not sure if my code helps for a better understanding but here it is.

This is my code to download the images

   func downloadImage(url: URL) {
        let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
            
            var downloadedImage:UIImage?
  
            if let data = data {
                downloadedImage = UIImage(data: data)
            }
            // if download actaully got an image
            if downloadedImage != nil {
                self.sum += 1 

                self.cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString) // cache the image
      
                // add the url as value to the dictionary
                self.imageURLS[self.sum] = url.absoluteString
            }
        }
        dataTask.resume()
    }

And this is what I use to add the subviews

func appendImages(){
        // sort the values of the dictionary by the greatest
        let sortedImageURLs = Array(imageURLS.keys).sorted(by: >)
        var stringURLs = [String]() // empty string array to append the URL's
        
        // for loop which goes through all integers in "creation Date" array
        for keys in sortedImageURLs {
            let url: String? = imageURLS[keys] // url which is connected to the key
            stringURLs.append(url!) // append the url to the url array
        }
        // -> the url string array starts with the latest loaded url
        
        var originSubview = 0 // counter of the origin of the subviews
        for urls in stringURLs {
            // 1.
            frame.origin.x = scrollView.frame.size.width * CGFloat(originSubview)
            frame.size = scrollView.frame.size
            // 2.
            let imageView = UIImageView(frame: frame)
            
            // get the image which is cahed under the url
            let image = cache.object(forKey: urls as NSString)
            
            // set the image of image view as the cached image
            imageView.image = image
            // and add that image view as a subview to the scrollView
            scrollView.addSubview(imageView)
            
            // increase counter variable by one
            //-> next images origin is at the end of the image before
            originSubview += 1
        }
        
        // 3.
        scrollView.contentSize = CGSize(width: ((scrollView.frame.size.width) * CGFloat(specialsCounter)), height: (scrollView.frame.size.height))
        scrollView.delegate = self
        pageControl.numberOfPages = specialsCounter
    }

Solution

  • You can use a DispatchGroup to notify when all your downloads have completed. Basically you enter() before you start a download and leave when the download has finished. notify will trigger when all entered tasks has left

    Here is an example on how to do it. I made a completion block for your downloadImage function.

    let dispatchGroup = DispatchGroup()
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        for url in imageUrls {
            self.dispatchGroup.enter()
            downloadImage(url: url) {
                self.dispatchGroup.leave()
            }
        }
        
        self.dispatchGroup.notify(queue: .main) {
            //All images has been downloaded here
        }
        
    }
    

    downloadImage function with completion:

    func downloadImage(url: URL, completion: () -> ()) {
          let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
              
              var downloadedImage:UIImage?
    
              if let data = data {
                  downloadedImage = UIImage(data: data)
              }
              // if download actaully got an image
              if downloadedImage != nil {
                  self.sum += 1
    
                  self.cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString) // cache the image
        
                  // add the url as value to the dictionary
                  self.imageURLS[self.sum] = url.absoluteString
                  completion()
              } else {
                  completion()
              }
          }
          dataTask.resume()
      }