TechnologiesSwiftUIDocuments

DocumentWriter protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

Implements logic of writing documents to disk.

How it works

DocumentWriter is the protocol that describes how a type serializes a document's contents so SwiftUI can persist them to disk. When you build a document-based app, the framework needs a single, well-defined point at which your in-memory model becomes bytes in a file; DocumentWriter is that contract. You adopt it indirectly through the document protocols, such as FileDocument, and supply the write logic SwiftUI calls whenever the document is saved, exported, or autosaved. Reach for it whenever a view's editable state must round-trip to a file the user owns, rather than living only in memory.

  1. Adopt a document protocol that conforms to DocumentWriter

    You rarely conform to DocumentWriter by hand; you adopt one of SwiftUI's document protocols that builds on it. Here TextDoc conforms to FileDocument, which makes the type a writer SwiftUI can drive through the document infrastructure while you focus on the model property text.

  2. Declare the content types you can write

    A writer must advertise which uniform type identifiers it produces so the system can present the correct save and export options. The readableContentTypes requirement returns the supported UTType values — here a single-element array [.plainText] — anchoring the document to plain text.

  3. Implement the write entry point

    The core of the writing contract is the method SwiftUI calls to capture your model as a file. fileWrapper(configuration:) receives a WriteConfiguration and returns a FileWrapper; in the example it wraps Data(text.utf8), turning the current text into UTF-8 bytes that become the file's contents.

  4. Use the WriteConfiguration to honor the save context

    The WriteConfiguration parameter carries the content type being written and any existing file data, letting you adapt output to how the save was initiated. Even when, as here, you ignore it and always emit fresh bytes, it is the channel through which DocumentWriter communicates the save's intent.

  5. Bind editable state to the document

    Because the writer reads its model whenever a save occurs, you keep that model in view state and edit it normally. The @State value doc is bound through $doc.text to a TextField, so every keystroke updates the same text that fileWrapper(configuration:) will later serialize.

Try it — Change the body written in fileWrapper(configuration:) from Data(text.utf8) to Data(text.uppercased().utf8) and observe that the saved file stores the uppercased contents while the on-screen TextField keeps showing what you 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.

DocumentWriter.swift
struct DocumentWriterDemo: View {
    struct TextDoc: FileDocument {
        static var readableContentTypes: [UTType] { [.plainText] }
        var text: String
        init(text: String = "Hello, document!") { self.text = text }
        init(configuration: ReadConfiguration) throws {
            self.text = ""
        }
        func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
            FileWrapper(regularFileWithContents: Data(text.utf8))
        }
    }

    @State private var doc = TextDoc()

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("FileDocument")
                .font(.headline)
            TextField("Contents", text: $doc.text)
                .textFieldStyle(.roundedBorder)
            Text("\(doc.text.utf8.count) bytes will be written")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
FileDocument Contents {doc.text.utf8.count} bytes will be written
FileDocument Contents {doc.text.utf8.count} bytes will be written
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →