How it works
ReadableDocument is the protocol that defines how a document type loads its contents when SwiftUI hands it the bytes of a file. It is the read half of SwiftUI's document model: by conforming, you declare which content types your document understands and supply the initializer that turns a file's data into your in-memory value. Reach for it whenever a document-based app — built with DocumentGroup or driven by the file importer — needs to open existing files rather than only create new ones. In practice you adopt it through FileDocument, which refines ReadableDocument and pairs the reading contract with the writing side.
Conform to gain readability
Adopting ReadableDocument (here via the FileDocument refinement) is what makes a type loadable by SwiftUI's document infrastructure. The nested
TextDocconforms toFileDocument, which is enough to let DocumentGroup and the system open panel construct one from a file on disk.Declare openable types with readableContentTypes
ReadableDocument requires the static
readableContentTypesproperty, a list of UTType values describing the file formats the document can read. The example returns[.plainText], so SwiftUI offers only plain-text files when opening and refuses formats outside that set.Decode in init(configuration:)
The protocol's core requirement is the throwing
init(configuration:), which receives aReadConfigurationcarrying the file's bytes. Here the initializer reachesconfiguration.file.regularFileContents, decodes them withString(data:encoding:), and assigns the result totext— falling back to an empty string when the file has no readable contents.Signal failure by throwing
Because the initializer is marked
throws, ReadableDocument lets you reject input that cannot be parsed; SwiftUI surfaces the error to the user instead of producing a malformed document. The example always succeeds, but thethrowscontract is where you would raise a CocoaError for corrupt or unsupported data.Pair reading with writing
FileDocument completes ReadableDocument with the symmetric write side,
fileWrapper(configuration:), so the same type that reads a file can also serialize back to one. The example returns aFileWrapper(regularFileWithContents:)built fromData(text.utf8), keeping the round trip from disk to value and back consistent.
readableContentTypes from [.plainText] to [.json] and watch SwiftUI stop offering plain-text files when opening, while init(configuration:) still receives whatever bytes the chosen file holds.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct ReadableDocumentDemo: View {
struct TextDoc: FileDocument {
static var readableContentTypes: [UTType] { [.plainText] }
var text: String
init(text: String) { self.text = text }
init(configuration: ReadConfiguration) throws {
if let data = configuration.file.regularFileContents,
let s = String(data: data, encoding: .utf8) {
text = s
} else { text = "" }
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
FileWrapper(regularFileWithContents: Data(text.utf8))
}
}
@State private var doc = TextDoc(text: "Hello, readable file!")
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text("FileDocument").font(.headline)
Text(doc.text)
.padding(8)
.background(Color(.secondarySystemBackground))
.cornerRadius(6)
Text("Reads .plainText files")
.font(.caption)
.foregroundColor(.secondary)
}
.padding()
}
}