swiftuiimagensurlcache

URLCache (CS193P Assignment 6)


I'm now on Stanford iOS Swift Assignment 6 where one of the required tasks is to use URLCache to cache the image on the local disk. After days of googling, I still couldn't figure out how to use it. It'd be helpful if anyone could point me to a good guide!

My code is like this now after trying to understand the official documentation. It doesn't help that the official doc doesn't have sample codes I could refer to :(

let urlCache = URLCache.shared

The required task is to set a cache and specify the size limit. I tried initialising URLCache and pass the size in the parameters. It works but storing and getting the cache doesn't seem to work. If URLCache is initialised every time the app (or view controller) is launched, wouldn't it ignore the previous cache that was created and stored?

I think there's no issue with this code? reading data from cache

if let cachedURLResponse = urlCache.cachedResponse(for: URLRequest(url: url)) {
    if let fetchedImage = UIImage(data: cachedURLResponse.data) {
        image = fetchedImage
    }
}

I'm lost on writing data to cache

urlCache.storeCachedResponse(CachedURLResponse(response: URLResponse(), data: imageData), for: URLRequest(url: url))

How to initialise URLResponse properly? I looked at the init method and it also requires url to be passed in as parameter. Found this strange since url is in URLRequest() too. Am I doing it wrong?

Helpful advice much appreciated!


Solution

  • You can use URLCache by making a request for the image data with URLSession then using the data and response available in its completion handler, for example:

    import UIKit
    
    class GalleryCollectionViewController: UICollectionViewController, UICollectionViewDragDelegate, UICollectionViewDropDelegate, UICollectionViewDelegateFlowLayout {
    
     // MARK: - Model
    
     var gallery: Gallery?
    
     // MARK: - Properties
    
     private var cache = URLCache.shared
     private var session = URLSession(configuration: .default)
    
     override func viewDidLoad() {
        super.viewDidLoad()
        cache = URLCache(memoryCapacity: 100, diskCapacity: 100, diskPath: nil) // replace capacities with your own values
     }
    
     override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GalleryCell", for: indexPath)
        if let galleryCell = cell as? GalleryCollectionViewCell {
            galleryCell.image = nil
            galleryCell.imageView.isHidden = true
            if let url = gallery?.images[indexPath.item].url {
                let request = URLRequest(url: url.imageURL) // imageURL from Utilities.swift of Stanford iOS course
                if let cachedResponse = cache.cachedResponse(for: request), let image = UIImage(data: cachedResponse.data) {
                    galleryCell.image = image
                    galleryCell.imageView.isHidden = false
                } else {
                    DispatchQueue.global(qos: .userInitiated).async { [weak self, weak galleryCell] in
                        let task = self?.session.dataTask(with: request) { (urlData, urlResponse, urlError) in
                            DispatchQueue.main.async {
                                if urlError != nil { print("Data request failed with error \(urlError!)") }
                                if let data = urlData, let image = UIImage(data: data) {
                                    if let response = urlResponse {
                                        self?.cache.storeCachedResponse(CachedURLResponse(response: response, data: data), for: request)
                                    }
                                    galleryCell?.image = image
                                } else {
                                    galleryCell?.image = UIImage(named: "placeholder")
                                }
                                galleryCell?.imageView.isHidden = false
                            }
                        }
                        task?.resume()
                    }
                }
            }
        }
        return cell
     }
    }