UIDocumentPickerViewController (DPVC) is Apple’s drop‑in UI for letting users import, open, move, or export files in the system‑wide Files environment. It works on iOS, iPadOS and macOS (Catalyst) and interoperates automatically with third‑party File Provider extensions.
Think of it as a scoped portal into user‑selected files. Outside of the delegate callbacks you have no access to the user’s filesystem.
UIDocumentPickerMode
).import
– copy a document into your sandbox..open
– open in‑place; returns a security‑scoped URL..exportToService
/.moveToService
– send or move files to another provider.Apple deprecated the string‑based constructor and introduced UTType‑driven APIs:
import UniformTypeIdentifiers
// Import or open
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .jpeg, .plainText],
asCopy: false)
Companion initializers exist for forExporting and
forOpeningContentTypes. The older
init(documentTypes:in:)
remains available but is
deprecated on iOS 14+ .
Always import UniformTypeIdentifiers
before referencing
UTType
symbols .
Task | Initializer | Key Parameters |
---|---|---|
Import / Open | forOpeningContentTypes |
asCopy (Bool) → copy or open‑in‑place |
Export | forExporting |
asCopy (Bool) → move vs copy only |
Legacy (pre iOS 14) | init(documentTypes:in:) |
UIDocumentPickerMode |
let utTypes: [UTType] = [.pdf, .png, .jpeg, .plainText]
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: utTypes, asCopy: true)
Set allowsMultipleSelection = true on iOS 11+ to let users
pick more than one file. For legacy code you must guard with
if #available(iOS 11, *)
.
Use .formSheet
or .pageSheet
on iPad for a nicer
Files‑style modal, or embed inside a navigation stack if you need continuous
browsing.
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]) {
// 1 ‑ start access for security‑scoped files
for url in urls where url.startAccessingSecurityScopedResource() {
defer { url.stopAccessingSecurityScopedResource() }
// 2 ‑ process or copy the file
}
}
func documentPickerWasCancelled(
_ controller: UIDocumentPickerViewController) {
// Dismiss UI / reset state
}
}
Always wrap sandboxed URLs with
startAccessingSecurityScopedResource /
stopAccessingSecurityScopedResource when
asCopy
is false; otherwise the file becomes unreadable
outside the callback scope.
If you need to keep long‑term access to an open‑in‑place file, turn the URL into a bookmark (bookmarkData), store it in Keychain / Core Data, then resolve via URL(resolvingBookmarkData:) when reopening the app. Never cache plain URLs – they go stale after a reboot.
@State private var docs: [URL] = []
.fileImporter(isPresented: $showing,
allowedContentTypes: [.item],
allowsMultipleSelection: false) { result in
docs = (try? result.get()) ?? []
}
For exporting (sharing) use fileExporter instead – it wraps the forExporting initializer under the hood.
allowsMultipleSelection = true
; usually fixed by a reboot
or simulator reset .UniformTypeIdentifiers
; otherwise UTType
will be missing .
Work‑arounds typically involve wrapping your dismiss logic in
DispatchQueue.main.async
to avoid presenting while de‑presenting.
import UIKit
import UniformTypeIdentifiers
final class ImportViewController: UIViewController, UIDocumentPickerDelegate {
@IBAction func chooseFile(_ sender: Any) {
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText], asCopy: true)
picker.allowsMultipleSelection = false
picker.delegate = self
present(picker, animated: true)
}
func documentPicker(_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else { return }
// Use url directly – already copied into sandbox
}
}
asCopy = false
) to
avoid duplicate storage.Task
and present a
UIActivityIndicatorView
or SwiftUI
ProgressView
.Info.plist
so your app
appears as a destination in other apps’ Share Sheets.fileImporter
& fileExporter
.