Home > Net >  Nil while caching images
Nil while caching images

Time:02-27

I've been able to solve the issue of caching images to improve scroll performance in my app. However nil is found when it tries to add it to cache. Also how can I add a placeholder image for images that failed to load or aren't available ?

let imageCache = NSCache<NSString, UIImage>()

    extension UIImageView {
    
        func downloadImage(from imgURL: String) -> URLSessionDataTask? {
            guard let url = URL(string: imgURL) else { return nil }
    
            // set initial image to nil so it doesn't use the image from a reused cell
            image = nil
    
            // check if the image is already in the cache
            if let imageToCache = imageCache.object(forKey: imgURL as NSString) {
                self.image = imageToCache
                return nil
            }
    
            // download the image asynchronously
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
                if let err = error {
                    print(err)
                    return
                }
    
                DispatchQueue.main.async {
                    // create UIImage
                    let imageToCache = UIImage(data: data!)
                    // add image to cache
                    imageCache.setObject(imageToCache!, forKey: imgURL as NSString)
                    self.image = imageToCache
                }
            }
            task.resume()
            return task
        }
    }

CodePudding user response:

A couple of observations:

  1. Just supply placeholder as parameter to function and use it instead of nil to initialize the image.

  2. Do that after checking the cache (because there’s no point in using the placeholder if you found desired image in the cache).

  3. Avoid use of ! forced unwrapping operator.

  4. Check that UIImage(data:) found an image in the guard statement (and on the session queue, not the main thread).

Thus:

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {
    func downloadImage(from imgURL: String, placeholder: UIImage? = nil) -> URLSessionDataTask? {
        guard let url = URL(string: imgURL) else { return nil }
        
        // check if the image is already in the cache
        if let imageToCache = imageCache.object(forKey: imgURL as NSString) {
            image = imageToCache
            return nil
        }
        
        // set initial image to placeholder so it doesn't use the image from a reused cell
        image = placeholder
        
        // download the image asynchronously
        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            guard 
                let data = data, 
                error == nil,
                let imageToCache = UIImage(data: data)
            else {
                print(error ?? URLError(.badServerResponse))
                return
            }
            
            imageCache.setObject(imageToCache, forKey: imgURL as NSString)

            DispatchQueue.main.async {
                self.image = imageToCache
            }
        }
        task.resume()
        return task
    }
}
  • Related