How to Get Documents Directory in Swift

How to Get Documents Directory in Swift

Almost every application needs to persist data in the application's sandbox. One option is to store data in the Documents directory of the application container. The question is How do you get the location of the Documents directory in Swift? This is easier than you might think.

Because applications are sandboxed on Apple's platforms, you need to be careful where you store data. To the get location of the application's sandbox on the user's device, you invoke the NSHomeDirectory() function, defined in the Foundation framework. The location this function returns differs from platform to platform. Let's take a look at the location of the application's sandbox on iOS and tvOS.

I have created a blank Xcode project using the Single View App template. We invoke the NSHomeDirectory() function in the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class and print the result to Xcode's console.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    print(NSHomeDirectory())

    return true
}

The output looks something like this.

/Users/Bart/Library/Developer/CoreSimulator/Devices/853BED98-B81B-48CB-9158-6E4BACE17E7C/data/Containers/Data/Application/3EF124CF-6B25-4093-A49C-BC5EF91A9D8C

We don't want to store data at the root of the application container, though. The Documents directory is a better place to store data. We could append Documents to the path returned by the NSHomeDirectory() function, but it's a better idea to ask the FileManager class for the location of the Documents directory. This is slightly more verbose.

We ask the default file manager instance for the location of the Documents directory by invoking the urls(for:in:) method. This method accepts two arguments, the directory we are searching for and a domain mask. This may sound complicated, but it's straightforward. We pass documentDirectory as the first parameter because we want to obtain the location of the Documents directory. We pass userDomainMask as the second parameter, which means we search for the Documents directory in the user's home directory.

let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

The file manager returns an array of URL objects. We know that there is only one Documents directory in the home directory of the application container. With that in mind, we can ask the array of URL objects for the item at index 0.

let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = urls[0]

Let's print the value to Xcode's console and run the application in the simulator. The output confirms that we successfully obtained the URL pointing to the Documents directory. The examples are written in Swift 5, but they also apply to earlier Swift versions.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    print(NSHomeDirectory())

    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = urls[0]

    print(documentsDirectory)

    return true
}

Adding a Bit of Convenience

If you need to access the Documents directory in several locations of your project, then it makes sense to create a convenience method for easier access. It's up to you what that convenience method looks like. In the following example, I define a static method on the URL struct using an extension. The static method with name documents provides easy access to the Documents directory in Swift.

extension URL {

    static var documents: URL {
        return FileManager
            .default
            .urls(for: .documentDirectory, in: .userDomainMask)[0]
    }
}

Storing an Image in the Documents Directory

This makes it trivial to access the Documents directory and write, for example, the data of an image to disk. We create an image by loading an asset from the asset catalog. We then create the URL object, using the static method we defined in the extension for the URL struct, appending the file name we choose for the image. We convert the UIImage instance to a Data object by invoking the pngData() method on the UIImage instance and write the image data to the Documents directory.

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

// Create URL
let url = URL.documents.appendingPathComponent("image.png")

// Convert to Data
if let data = image?.pngData() {
    do {
        try data.write(to: url)
    } catch {
        print("Unable to Write Image Data to Disk")
    }
}

The write(to:) method is a throwing method, which means we need to wrap it in a do-catch statement. Add this snippet to the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class and run the application in the simulator. I use SimPholders to easily access the sandbox of the application. This is what that looks like in Finder.

How to Get Documents Directory in Swift