TechnologiesSwiftUI

ReferenceFileDocument protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

A type that you use to serialize reference type documents to and from file.

How it works

ReferenceFileDocument is the protocol you adopt to back a document-based app with a reference type, typically an ObservableObject class whose properties drive your views directly. Use it instead of the value-type FileDocument when your model is naturally a class, when you need the document instance to be observed and mutated in place, or when saving large files should not block editing. Because saving happens on a separate snapshot rather than the live object, SwiftUI can serialize a document while the user keeps working. Reach for it whenever your editable content lives in a shared, mutable object that several views read and write.

  1. Adopt the protocol on a reference type

    Conform a class to ReferenceFileDocument so the same instance can be shared and observed across your UI. Here TextDocument is a final class whose @Published var text is the editable state; marking it published lets SwiftUI re-render whenever the document's contents change.

  2. Declare the formats with readableContentTypes

    The static readableContentTypes requirement lists the UTType values your document can open, telling the system which files to offer and how to tag them. TextDocument returns [.plainText], declaring it reads and writes plain-text files (the UTType API comes from import UniformTypeIdentifiers).

  3. Read existing files with init(configuration:)

    The throwing init(configuration:) builds your document from a file the user opens, receiving a ReadConfiguration whose file.regularFileContents holds the bytes. TextDocument decodes that Data as UTF-8 into text, falling back to an empty string when there are no contents.

  4. Capture a snapshot before writing

    Unlike FileDocument, saving is split in two. snapshot(contentType:) is called first, on the main actor, to grab an immutable copy of whatever the document needs to persist; TextDocument simply returns its current text. SwiftUI then hands that snapshot to the writer so editing can continue uninterrupted while the save proceeds.

  5. Serialize the snapshot with fileWrapper(snapshot:configuration:)

    fileWrapper(snapshot:configuration:) runs off the main actor and turns the captured snapshot into a FileWrapper for disk, guided by a WriteConfiguration. TextDocument wraps Data(snapshot.utf8) in a FileWrapper(regularFileWithContents:), and because it works from the snapshot rather than the live object the write is safe to perform in the background.

Try it — Change the snapshot(contentType:) body from text to text.uppercased() and observe that saved files are upper-cased while the live $document.text in the TextEditor stays exactly as typed.

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.

ReferenceFileDocument.swift
import UniformTypeIdentifiers

final class TextDocument: ReferenceFileDocument {
    static var readableContentTypes: [UTType] { [.plainText] }

    @Published var text: String

    init(text: String = "Hello, document!") {
        self.text = text
    }

    init(configuration: ReadConfiguration) throws {
        if let data = configuration.file.regularFileContents {
            text = String(decoding: data, as: UTF8.self)
        } else {
            text = ""
        }
    }

    func snapshot(contentType: UTType) throws -> String { text }

    func fileWrapper(snapshot: String, configuration: WriteConfiguration) throws -> FileWrapper {
        FileWrapper(regularFileWithContents: Data(snapshot.utf8))
    }
}

struct ReferenceFileDocumentDemo: View {
    @StateObject private var document = TextDocument()

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("ReferenceFileDocument")
                .font(.headline)
            TextEditor(text: $document.text)
                .frame(height: 120)
                .border(.secondary)
            Text("Edited as a reference document")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
ReferenceFileDocument Edited as a reference document
ReferenceFileDocument Edited as a reference document
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →