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.
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
Notestruct conforms toWritableDocumentand carries a singletextproperty as its content.Declare the formats with writableContentTypes
The static
writableContentTypesrequirement lists everyUTTypeyour document can be written as, driving the format choices the system offers when exporting.Notereturns[.plainText], marking it as a plain-text document; the example surfaces that choice by readingNote.writableContentTypes.first?.preferredFilenameExtensionto show the on-disk extension.Serialize through fileWrapper(configuration:)
The
fileWrapper(configuration:)method is where the protocol asks you to encode your current state into aFileWrapper, throwing if the value can't be represented.Notepacks itstextinto UTF-8 bytes withData(text.utf8)and hands back aFileWrapper(regularFileWithContents:)holding those contents.Use the WriteConfiguration parameter
The
WriteConfigurationpassed intofileWrapper(configuration:)carries the target content type and any existing file the system is overwriting, letting one document branch its output by format. TheNoteexample writes plain text unconditionally, but more elaborate documents inspectconfigurationto choose an encoding.Drive it from your view's state
A
WritableDocumentis an ordinary value type, so you can hold it in@Stateand edit it live before saving. The demo binds$note.textto aTextField, mutating the samenotevalue whosefileWrapper(configuration:)would later produce the file.
.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.
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()
}
}