This post presents an example implementation to download a file or image from a remote URL and save the data to a file cache in Swift. If the file is already downloaded, this example implementation will read the file or image directly from a saved cache.

  1. Download An Image or File From A URL
  2. Cache File or Image To Disk
  3. Read File or Image From Cache
  4. Load File or Image From A URL

Download An Image or File From A URL

This post will use URLSession.downloadTask(with:) to download a remote file from a url:

let task = URLSession.shared.downloadTask(with: url) {
    (tempURL, response, error) in
    // Handle response, the download file is
    // at tempURL
}

// Start the download
task.resume()

Cache File or Image To Disk

A file path is needed to be able to cache a downloaded file or image. The function download(url:, toFile:, completion:) builds off of URLSession.downloadTask(with:), taking in a toFile file path and handles the download response callback to cache downloaded data.

The first parameter of the downloadTask(with:) callback is a temporary URL tempURL to the downloaded file. This temporary file will be deleted when the callback completes, so it is important to copy the data at tempURL to a new location before the callback finishes.

func download(url: URL, toFile file: URL, completion: @escaping (Error?) -> Void) {
    // Download the remote URL to a file
    let task = URLSession.shared.downloadTask(with: url) {
        (tempURL, response, error) in
        // Early exit on error
        guard let tempURL = tempURL else {
            completion(error)
            return
        }

        do {
            // Remove any existing document at file
            if FileManager.default.fileExists(atPath: file.path) {
                try FileManager.default.removeItem(at: file)
            }

            // Copy the tempURL to file
            try FileManager.default.copyItem(
                at: tempURL,
                to: file
            )

            completion(nil)
        }

        // Handle potential file system errors
        catch let fileError {
            completion(error)
        }
    }

    // Start the download
    task.resume()
}

Read File or Image From Cache

With download(url:toFile:completion:) implemented, the next step is to create an interface that:

  1. Loads a file or image from the cache if the data exists in the cache
  2. Otherwise downloads the file or image to the cache

An important consideration is where to cache images. One option is the temporary directory (read more). On Apple platforms like iOS, the temporary directory will automatically be cleared in low-memory situations.

One way to obtain a path in the temporary directory is:

// Given a url
let url = // URL

// Compute a path to this URL in the cache
let fileCachePath = FileManager.default.temporaryDirectory
    .appendingPathComponent(
        url.lastPathComponent,
        isDirectory: false
    )

Load File or Image From A URL

The function loadData(url:, completion:) uses download(url:, toFile:, completion:) and FileManager to complete the implementation for downloading, caching, and reading files and images from a URL:

func loadData(url: URL, completion: @escaping (Data?, Error?) -> Void) {
    // Compute a path to the URL in the cache
    let fileCachePath = FileManager.default.temporaryDirectory
        .appendingPathComponent(
            url.lastPathComponent,
            isDirectory: false
        )
    
    // If the image exists in the cache, 
    // load the image from the cache and exit
    if let data = Data(contentsOfFile: cachedFile.path) {
        completion(data, nil)
        return
    }
    
    // If the image does not exist in the cache, 
    // download the image to the cache
    download(url: url, toFile: cachedFile) { (error) in
        let data = Data(contentsOfFile: cachedFile.path)
        completion(data, error)
    }
}

Downloading and Caching Images in Swift

That’s it! Using URLSession and FileManager you can work with remote files and images in Swift without any external dependencies.

// Example Usage
let url = URL(string: "https://domain.com/logo.png")!

loadData(url: url) { (data, error) in
    // Handle the loaded file data

    // If the data is an image, use UIImage(data: data) to
    // load the image
}

Why not use SDWebImage and Cocoapods to download and cache files and images?

Many apps do not require the features of third-party frameworks to support efficient file and image downloading. By implementing their own image download and cache interface a developer can:

  • simplify the codebase by removing third-party dependencies like SDWebImage
  • reduce the binary size of the application
  • reduce risk associated with relying on third-party dependencies