TechnologiesSwiftUIDocuments

WritableDocument protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

A type that you use to write documents to file.

How it works

WritableDocument is the protocol you adopt to describe a model that SwiftUI knows how to serialize and write to a file or data destination. It is the output-only counterpart to the document model used by DocumentGroup and the fileExporter modifiers: by conforming, your value type tells the system which content types it can produce and how to turn its in-memory state into bytes on disk. Reach for it when you have data the user should be able to save or export, but you don't need SwiftUI to read documents back in for you.

  1. Conform a value type to WritableDocument

    Adopt the protocol on a struct that holds your document's state, so SwiftUI can treat it as a saveable unit. Here the nested Note struct conforms to WritableDocument and carries a single text property as its content.

  2. Declare the formats with writableContentTypes

    The static writableContentTypes requirement lists every UTType your document can be written as, driving the format choices the system offers when exporting. Note returns [.plainText], marking it as a plain-text document; the example surfaces that choice by reading Note.writableContentTypes.first?.preferredFilenameExtension to show the on-disk extension.

  3. Serialize through fileWrapper(configuration:)

    The fileWrapper(configuration:) method is where the protocol asks you to encode your current state into a FileWrapper, throwing if the value can't be represented. Note packs its text into UTF-8 bytes with Data(text.utf8) and hands back a FileWrapper(regularFileWithContents:) holding those contents.

  4. Use the WriteConfiguration parameter

    The WriteConfiguration passed into fileWrapper(configuration:) carries the target content type and any existing file the system is overwriting, letting one document branch its output by format. The Note example writes plain text unconditionally, but more elaborate documents inspect configuration to choose an encoding.

  5. Drive it from your view's state

    A WritableDocument is an ordinary value type, so you can hold it in @State and edit it live before saving. The demo binds $note.text to a TextField, mutating the same note value whose fileWrapper(configuration:) would later produce the file.

Try it — Add .json to the array returned by writableContentTypes and watch the "Saves as" line change to reflect the first declared UTType's extension.

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.

WritableDocument.swift
struct WritableDocumentDemo: View {
    struct Note: WritableDocument {
        static var writableContentTypes: [UTType] { [.plainText] }
        var text: String
        init(text: String) { self.text = text }
        func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
            FileWrapper(regularFileWithContents: Data(text.utf8))
        }
    }

    @State private var note = Note(text: "Hello, world!")

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("WritableDocument")
                .font(.headline)
            TextField("Contents", text: $note.text)
                .textFieldStyle(.roundedBorder)
            Text("Saves as: \(Note.writableContentTypes.first?.preferredFilenameExtension ?? "txt")")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
WritableDocument Hello, world! Saves as: {Note.writableContentTypes.first?.prefe… ?? "txt"}
WritableDocument Hello, world! Saves as: {Note.writableContentTypes.first?.prefe… ?? "txt"}
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →