FunClosureFunClosure

Cache Image

Swift 5, iOS 13, Xcode 11

extension FileManager {
    
    // MARK: - Image
    func saveImage(_ image: UIImage, bucketName: String, pathComponent: String, for directory: SearchPathDirectory = .documentDirectory) {
    
        // prepare file path
        let pathURL = getPathURL(bucketName: bucketName,pathComponent: pathComponent, for: directory)
        let directoryURL = URL(fileURLWithPath: pathURL.path).deletingLastPathComponent()
        
        // prepare image data
        guard let imageData = image.jpegData(compressionQuality: 1.0) else {return}
                
        // make sure the folder is there
        self.createDirectoryIfNotExist(path: directoryURL.path)

        // save the image
        do {
            try imageData.write(to: pathURL, options: .atomic)
        } catch {
            // handle error
            print(#function, error.localizedDescription)
        }

    }

    
    
    func getImage(bucketName: String, pathComponent: String, for directory: SearchPathDirectory = .documentDirectory) -> UIImage? {
    
        // prepare file path
        let pathURL = getPathURL(bucketName: bucketName, pathComponent: pathComponent, for: directory)
        
        // get image
        if self.fileExists(atPath: pathURL.path){
            return UIImage(contentsOfFile: pathURL.path)
        }else{
            return nil
        }
    }
}

Nowadays, most of the images in the app are from the cloud.

In order to make the content dynamic and smaller the size of the app, probably every app in the AppStore need to show some images that are stored locally when the app is downloaded.

Most of the developer can just go for the third-party library, like Kingfisher, that helps you to download, cache, and show the image asynchronous.

But sometimes, you might just want to do it yourself. Maybe it's because you're using AWS S3, that need some cache function, or you just want to keep things simple.

The code above is the simple setter and getter of downloaded image. Additional, in the example, you can also specify the bucketName to group the images.

To make the example above work, you will also need to implement the file path helper methods as below:

// MARK: - Path helper
extension FileManager {
    
    func getPathURL(bucketName: String, pathComponent: String, for directory: SearchPathDirectory = .documentDirectory) -> URL {
        let pathURL = getDirectoryPath(for: directory).appendingPathComponent(bucketName, isDirectory: true).appendingPathComponent(pathComponent)

        if !self.fileExists(atPath: pathURL.deletingLastPathComponent().path) {
            self.createDirectoryIfNotExist(path: pathURL.deletingLastPathComponent().path)
        }

        return pathURL
    }

    func getDirectoryPath(for directory: SearchPathDirectory = .documentDirectory) -> URL {
        let url = self.urls(for: directory, in: .userDomainMask).first!
        return url
    }

    func createDirectoryIfNotExist(path: String){
        if !self.fileExists(atPath: path){
            try! self.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: [:])
        }else{
            print("Directory has been created.")
        }
    }

    func deleteDirectory(path: String){
        if self.fileExists(atPath: path){
            try! self.removeItem(atPath: path)
        }else{
            print("Something wrong.")
        }
    }
}

Useful links:

Tagged with: