The title of this post is a bit misleading, because you should not store images in the user's defaults database. That is not what it was designed for. With this post, I hope I can convince other developers to use a different solution for storing images in the application's sandbox.

What Is the Problem?

The defaults system is designed for storing small pieces of data, such as settings and user preferences. It is not designed for storing large blobs of data, such as images.

Is It Possible?

Is it possible to store an image in the user's defaults database? It is, but it isn't possible to store an image as is in the user's defaults database. The defaults system only supports strings, numbers, Date objects, and Data objects. This means that you need to convert the image to a Data object before you can store it in the user's defaults database. But as I mentioned earlier, I strongly discourage you from taking this approach.

Other Options

A much better solution is storing the image in the application's sandbox and only storing the location of the image in the user's defaults database. Let me show you how this works. In this example, we work with a UIImage instance. This also works with other types, such as NSImage.

We add an import statement for the UIKit framework and load an image with name landscape. This is just an example to illustrate the concept.

import UIKit

// Load Image
let image = UIImage(named: "landscape")

We then convert the UIImage instance to a Data object by invoking the pngData() method on the UIImage instance. Because this operation can fail, we safely unwrap the result of the pngData() method using an if statement.

import UIKit

// Load Image
let image = UIImage(named: "landscape")

// Convert to Data
if let data = image?.pngData() {

}

The next step is deciding where the image data will be written to. In this example, we write the image data to the Documents directory in the application's sandbox. We ask the FileManager class for the URL of the Documents directory and append the name of the file, landscape.png, to the URL.

import UIKit

// Load Image
let image = UIImage(named: "landscape")

// Convert to Data
if let data = image?.pngData() {
    // Create URL
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let url = documents.appendingPathComponent("landscape.png")
}

Writing a Data object to disk is a throwing operation so we wrap it in a do-catch statement. If the operation is successful, we store the URL in the user's defaults database.

import UIKit

// Load Image
let image = UIImage(named: "landscape")

// Convert to Data
if let data = image?.pngData() {
    // Create URL
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let url = documents.appendingPathComponent("landscape.png")

    do {
        // Write to Disk
        try data.write(to: url)

        // Store URL in User Defaults
        UserDefaults.standard.set(url, forKey: "background")

    } catch {
        print("Unable to Write Data to Disk (\(error))")
    }
}

Should You Use User Defaults?

In this example, you could argue that it isn't necessary to store the URL of the image data in the user's defaults database and that is a valid point. If you're storing the user's profile image in the Documents directory and you always name it profile.png, then there's no need to store the URL of the image data in the user's defaults database.

What I want you to remember is that you should not use the user's defaults database for storing large blobs of data, including image data. I understand that it's convenient, but it may impact the performance of your application.

On tvOS, the system terminates your application if the size of the user's defaults database exceeds 1MB and your application is notified when it exceeds 512kB. This clearly indicates that Apple doesn't want you to misuse the user's defaults database for storing big chunks of data.

Don't use the defaults system for storing image data.