How to cache images using URLSession in Swift

后端 未结 3 1425
鱼传尺愫
鱼传尺愫 2020-12-11 04:04

I would like to enhance the code below to cache images and only download them if they haven\'t been cached previously. I can\'t seem to find any good examples of how to use

相关标签:
3条回答
  • 2020-12-11 04:56

    Updated for Swift 4

    import UIKit
    
    let imageCache = NSCache<AnyObject, AnyObject>()
    
    class ImageLoader: UIImageView {
    
        var imageURL: URL?
    
        let activityIndicator = UIActivityIndicatorView()
    
        func loadImageWithUrl(_ url: URL) {
    
            // setup activityIndicator...
            activityIndicator.color = .darkGray
    
            addSubview(activityIndicator)
            activityIndicator.translatesAutoresizingMaskIntoConstraints = false
            activityIndicator.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
            activityIndicator.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    
            imageURL = url
    
            image = nil
            activityIndicator.startAnimating()
    
            // retrieves image if already available in cache
            if let imageFromCache = imageCache.object(forKey: url as AnyObject) as? UIImage {
    
                self.image = imageFromCache
                activityIndicator.stopAnimating()
                return
            }
    
            // image does not available in cache.. so retrieving it from url...
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
    
                if error != nil {
                    print(error as Any)
                    DispatchQueue.main.async(execute: {
                        self.activityIndicator.stopAnimating()
                    })
                    return
                }
    
                DispatchQueue.main.async(execute: {
    
                    if let unwrappedData = data, let imageToCache = UIImage(data: unwrappedData) {
    
                        if self.imageURL == url {
                            self.image = imageToCache
                        }
    
                        imageCache.setObject(imageToCache, forKey: url as AnyObject)
                    }
                    self.activityIndicator.stopAnimating()
                })
            }).resume()
        }
    }
    

    Usage:

    // assign ImageLoader class to your imageView class
    let yourImageView: ImageLoader = {
    
        let iv = ImageLoader()
        iv.frame = CGRect(x: 10, y: 100, width: 300, height: 300)
        iv.backgroundColor = UIColor(red: 0.94, green: 0.94, blue: 0.96, alpha: 1.0)
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        return iv
    }()
    
    
    // unwrapped url safely...
       if let strUrl = "https://picsum.photos/300/300".addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
          let imgUrl = URL(string: strUrl) {
    
          yourImageView.loadImageWithUrl(imgUrl) // call this line for getting image to yourImageView
    }
    
    0 讨论(0)
  • 2020-12-11 04:59
    let imageCache = NSCache<AnyObject, AnyObject>()
    extension UIImageView {
    
        func loadImageFromUrl(urlString: String)  {
            if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
                self.image = imageFromCache
                return
            }
    
            Alamofire.request(urlString, method: .get).response { (responseData) in
                if let data = responseData.data {
                   DispatchQueue.main.async {
                    if let imageToCache = UIImage(data: data){
                        imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
                        self.image = imageToCache
                    }
                }
            }
        }
    
     }
    }
    
    0 讨论(0)
  • 2020-12-11 05:04

    One potential solution to this would be to utilize NSCache to take care of caching. Essentially what you would do is check if you already have the image locally to load from rather than fetching every time before you actually make a request.

    Here's one of my implementations, however - it's a subclass rather than an extension:

    class CustomImageView: UIImageView {
    
        // MARK: - Constants
    
        let imageCache = NSCache<NSString, AnyObject>()
    
        // MARK: - Properties
    
        var imageURLString: String?
    
        func downloadImageFrom(urlString: String, imageMode: UIViewContentMode) {
            guard let url = URL(string: urlString) else { return }
            downloadImageFrom(url: url, imageMode: imageMode)
        }
    
        func downloadImageFrom(url: URL, imageMode: UIViewContentMode) {
            contentMode = imageMode
            if let cachedImage = imageCache.object(forKey: url.absoluteString as NSString) as? UIImage {
                self.image = cachedImage
            } else {
                URLSession.shared.dataTask(with: url) { data, response, error in
                    guard let data = data, error == nil else { return }
                    DispatchQueue.main.async {
                        let imageToCache = UIImage(data: data)
                        self.imageCache.setObject(imageToCache!, forKey: url.absoluteString as NSString)
                        self.image = imageToCache
                    }
                }.resume()
            }
        }
    }
    

    Additionally, here's a useful resource: https://www.hackingwithswift.com/example-code/system/how-to-cache-data-using-nscache

    0 讨论(0)
提交回复
热议问题