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.
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
TextDocconforms toFileDocument, which makes the type a writer SwiftUI can drive through the document infrastructure while you focus on the model propertytext.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
readableContentTypesrequirement returns the supportedUTTypevalues — here a single-element array[.plainText]— anchoring the document to plain text.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 aWriteConfigurationand returns aFileWrapper; in the example it wrapsData(text.utf8), turning the currenttextinto UTF-8 bytes that become the file's contents.Use the WriteConfiguration to honor the save context
The
WriteConfigurationparameter 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.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
@Statevaluedocis bound through$doc.textto aTextField, so every keystroke updates the sametextthatfileWrapper(configuration:)will later serialize.
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.
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()
}
}