Use a document picker to access the content of a directory outside your app’s container.
Framework
- UIKit
Overview
In iOS 12 and earlier, users could open and interact with files outside the app’s container. The UIDocument
and UIDocument
provided access to files stored in the system’s local file provider, in iCloud, or in third-party services that use a File Provider extension. Users could select multiple files at a time—but they needed to select each file individually.
With iOS 13, users can select a directory from any of the available file providers using a UIDocument
. The document picker returns a security-scoped URL for the directory. Security-scoped URLs permit your app to access content outside its container. In this case, the URL lets your app recursively access the directory and all of its contents. This includes accessing any new items added to the directory in the future. Your app can even save a bookmark for this URL, letting it access the directory the next time it launches.
Ask the User to Select a Directory
To prompt the user to select a directory, create a document picker for an UIDocument
operation, setting the document type to k
. Then set the document picker’s delegate and present it.
// Create a document picker for directories.
let documentPicker =
UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String],
in: .open)
documentPicker.delegate = self
// Set the initial directory.
documentPicker.directoryURL = startingDirectory
// Present the document picker.
present(documentPicker, animated: true, completion: nil)
As soon as you call the present
method, the system displays the document picker to the user. If you specify the directory
property, the document picker starts with the selected directory. Otherwise, it starts with the last directory chosen by the user.

After the user taps Done, the system calls your delegate’s document
method, passing an array of security-scoped URLs for the directories chosen by the user. Use a security-scoped URL to enumerate the content of the directory and any of its subdirectories, or to add, remove, or modify any files. For more information, see Access the Directory’s Content.
If the user taps Cancel, the system calls document
instead.
Note
The UIDocument
doesn’t support the k
document type. To provide access to directories, you use the UIDocument
instead.
Access the Directory’s Content
When the user selects a directory in the document picker, your system gives your app permission to access that directory and all of its contents. The document picker returns a security-scoped URL for the directory. When you use one of these URLs to enumerate the directory’s content, the resulting URLs are also security-scoped. Finally, you can save a security-scoped URL as a bookmark, then later resolve it back into a security-scoped URL.
To access the content of a security-scoped URL, you must do the following:
Before accessing the URL, call
start
.Accessing Security Scoped Resource Use a file coordinator to perform read or write operations on the URL’s contents.
After you’re done accessing the URL, call
stop
.Accessing Security Scoped Resource
// Start accessing a security-scoped resource.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
// Make sure you release the security-scoped resource when you are done.
defer { url.stopAccessingSecurityScopedResource() }
// Use file coordination for reading and writing any of the URL’s content.
var error: NSError? = nil
NSFileCoordinator().coordinate(readingItemAt: url, error: &error) { (url) in
let keys : [URLResourceKey] = [.nameKey, .isDirectoryKey]
// Get an enumerator for the directory's content.
guard let fileList =
FileManager.default.enumerator(at: url, includingPropertiesForKeys: keys) else {
output.append("*** Unable to access the contents of \(url.path) ***\n")
return
}
for case let file as URL in fileList {
// Also start accessing the content's security-scoped URL.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
continue
}
// Make sure you release the security-scoped resource when you are done.
defer { url.stopAccessingSecurityScopedResource() }
// Do something with the file here.
}
}
Save the URL as a Bookmark
To access the URL in the future, save the URL as a NSURLBookmark
, using its bookmark
method.
do {
// Start accessing a security-scoped resource.
guard url.startAccessingSecurityScopedResource() else {
// Handle the failure here.
return
}
// Make sure you release the security-scoped resource when you are done.
defer { url.stopAccessingSecurityScopedResource() }
let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
try bookmarkData.write(to: getMyURLForBookmark())
}
catch let error {
// Handle the error here.
}
You can then read the bookmark, and resolve it to a security-scoped URL again.
do {
let bookmarkData = try Data(contentsOf: getMyURLForBookmark())
var isStale = false
let url = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
guard !isStale else {
// Handle stale data here.
return
}
// Use the URL here.
}
catch let error {
// Handle the error here.
}
Respond to Permission Changes
Users always have complete control over the apps that can access directories. After selecting a directory from the document picker, your app appears in Settings > Privacy > Files and Folders. This page lists all the apps that have permission to access shared directories, and users can revoke or restore permission for each app at any time.
This means your app must be ready to handle failures when accessing a directory’s content. Calls to the start
method can fail, as well as any attempts to read or write to the URL. This is especially true when saving and resolving bookmarks to security-scoped URLs, because using saved bookmarks can greatly increase the amount of time users have to possibly change your app’s permissions.