<!--
{
  "documentType" : "article",
  "framework" : "SwiftUI",
  "identifier" : "/documentation/SwiftUI/Updating-your-document-based-app",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Updating your document-based app"
}
-->

# Updating your document-based app

Migrate an existing app to adopt URL-based document reading and writing with Swift concurrency.

## Discussion

If you have an existing document-based app, you can adopt the <doc://com.apple.documentation/documentation/SwiftUI/Document> protocol to take advantage of direct URL access, Swift concurrency integration, and modern observation. The <doc://com.apple.documentation/documentation/SwiftUI/Document> protocol separates reading and writing into dedicated types, which gives you more control over file I/O and enables partial reads and writes for complex document formats.

In releases before iOS 27, iPadOS 27, macOS 27, and visionOS 27, you create a document type by conforming to either `FileDocument` or `ReferenceFileDocument`. In these and later releases, you can either conform to the `Document` protocol or to the `ReadableDocument` and `WritableDocument` protocols, depending on what your app does. Although `FileDocument` and `ReferenceFileDocument` remain available, they’re no longer supported for new document types.

The following table highlights the differences between these three protocols to help you choose the right migration path:

|                             |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument>                            |<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument>                                     |<doc://com.apple.documentation/documentation/SwiftUI/Document>                                                                                                                                  |
|-----------------------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|Type                         |Value (`struct`)                                                                              |Reference (`class`)                                                                                             |Reference (`class`)                                                                                                                                                                             |
|Reading                      |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument/init(configuration:)>       |<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument/init(configuration:)>                |<doc://com.apple.documentation/documentation/SwiftUI/ReadableDocument/reader(configuration:)> + <doc://com.apple.documentation/documentation/SwiftUI/ReadableDocument/apply(snapshot:previous:)>|
|Writing                      |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument/fileWrapper(configuration:)>|<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument/fileWrapper(snapshot:configuration:)>|<doc://com.apple.documentation/documentation/SwiftUI/WritableDocument/writer(configuration:)> + <doc://com.apple.documentation/documentation/SwiftUI/WritableDocument/snapshot(contentType:)>   |
|Reading and writing execution|Synchronous                                                                                   |Synchronous                                                                                                     |`async`/`sending` with explicit actor boundaries                                                                                                                                                |
|File access                  |[`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) only        |[`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) only                          |URL or [`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper)                                                                                                        |
|Undo                         |Automatic (value semantics)                                                                   |Manual ([`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager))                      |Manual ([`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager))                                                                                                      |
|Observation                  |N/A (value type)                                                                              |[`ObservableObject`](https://developer.apple.com/documentation/combine/observableobject)                        |[`@Observable`](https://developer.apple.com/documentation/observation/observable)                                                                                                               |

## Update your app

Depending on which deprecated protocol your app uses, select the appropriate tab and follow the checklist to update your app:

**FileDocument:**

1. **Convert from a structure to an [`@Observable`](https://developer.apple.com/documentation/observation/observable) class.** A document type that conforms to <doc://com.apple.documentation/documentation/SwiftUI/FileDocument> is typically a value type. The <doc://com.apple.documentation/documentation/SwiftUI/Document> protocol requires a reference type. Replace your structure with a `final class` annotated with [`@Observable`](https://developer.apple.com/documentation/observation/observable).
2. **Separate reading logic into a <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader>.** For reading, use <doc://com.apple.documentation/documentation/SwiftUI/FileWrapperDocumentReader> and provide a closure that converts a [`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) into a snapshot value.
3. **Implement <doc://com.apple.documentation/documentation/SwiftUI/ReadableDocument/apply(snapshot:previous:)>.** Use this method to update your document’s properties when a new snapshot arrives from the reader.
4. **Separate writing logic into a <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter>.** For writing, use <doc://com.apple.documentation/documentation/SwiftUI/FileWrapperDocumentWriter> and provide a closure that converts a snapshot into a [`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper).
5. **Implement <doc://com.apple.documentation/documentation/SwiftUI/WritableDocument/snapshot(contentType:)>.** Add this method to capture your document’s current state on the main actor. Mark it `async throws` and return a `sending` value.
6. **Add undo registration.** With <doc://com.apple.documentation/documentation/SwiftUI/FileDocument>, SwiftUI manages undo automatically through value semantics and [`Binding`](https://developer.apple.com/documentation/swiftui/binding). With the <doc://com.apple.documentation/documentation/SwiftUI/Document> protocol, you register undo actions yourself using an [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager). The undo manager from the environment in your content view is already connected to the document.
7. **Update your <doc://com.apple.documentation/documentation/SwiftUI/DocumentGroup> initializer.** Replace `DocumentGroup(newDocument:)` with the closure-based initializer that receives <doc://com.apple.documentation/documentation/SwiftUI/URLDocumentConfiguration> and <doc://com.apple.documentation/documentation/SwiftUI/DocumentCreationContext>.
8. **Update your content view.** Replace `@Binding var document: MyDocument` with a direct reference to your observable class, and use [`@Bindable`](https://developer.apple.com/documentation/swiftui/bindable) for creating bindings.

**ReferenceFileDocument:**

1. **Mark your document [`@Observable`](https://developer.apple.com/documentation/observation/observable).** The <doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument> protocol predates the Observation framework. Add the [`@Observable`](https://developer.apple.com/documentation/observation/observable) macro and remove any [`ObservableObject`](https://developer.apple.com/documentation/combine/observableobject) conformance and [`@Published`](https://developer.apple.com/documentation/combine/published) property wrappers.
2. **Separate reading logic into a <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader>.** For reading, use <doc://com.apple.documentation/documentation/SwiftUI/FileWrapperDocumentReader> and provide a closure that converts a [`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) into a snapshot value.
3. **Implement <doc://com.apple.documentation/documentation/SwiftUI/ReadableDocument/apply(snapshot:previous:)>.** Use this method to update your document’s properties when a new snapshot arrives from the reader.
4. **Separate writing logic into a <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter>.** For writing, use <doc://com.apple.documentation/documentation/SwiftUI/FileWrapperDocumentWriter> and provide a closure that converts a snapshot into a [`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper).
5. **Implement <doc://com.apple.documentation/documentation/SwiftUI/WritableDocument/snapshot(contentType:)>.** Update the method to add `async throws` and `sending` to the return type to enable safe transfer across concurrency boundaries.
6. **Update your <doc://com.apple.documentation/documentation/SwiftUI/DocumentGroup> initializer.** Replace the type-based initializer with the closure-based one that receives <doc://com.apple.documentation/documentation/SwiftUI/URLDocumentConfiguration> and <doc://com.apple.documentation/documentation/SwiftUI/DocumentCreationContext>.
7. **Audit undo registration.** Undo registration is conceptually the same, but verify that your undo actions work correctly after the changes.

If your existing document-based app uses <doc://com.apple.documentation/documentation/SwiftUI/FileDocument> or <doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument>, the following table shows how concepts map to the <doc://com.apple.documentation/documentation/SwiftUI/Document> protocol:

**FileDocument:**|Before                                                                                        |After                                                                                                           |
        |----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
        |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument>                            |<doc://com.apple.documentation/documentation/SwiftUI/Document>                                                  |
        |`struct` (value type)                                                                         |[`@Observable`](https://developer.apple.com/documentation/observation/observable) `final class` (reference type)|
        |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument/init(configuration:)>       |Separate <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader>                                   |
        |<doc://com.apple.documentation/documentation/SwiftUI/FileDocument/fileWrapper(configuration:)>|Separate <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter>                                   |
        |Implicit snapshot (value semantics)                                                           |Explicit <doc://com.apple.documentation/documentation/SwiftUI/WritableDocument/snapshot(contentType:)> method   |
        |Automatic undo via [`Binding`](https://developer.apple.com/documentation/swiftui/binding)     |Manual undo registration with [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager) |
        |`DocumentGroup(newDocument:)`                                                                 |<doc://com.apple.documentation/documentation/SwiftUI/DocumentGroup> initializer                                 |

**ReferenceFileDocument:**|Before                                                                                                          |After                                                                                                                                                                                                                                                 |
        |----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
        |<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument>                                     |<doc://com.apple.documentation/documentation/SwiftUI/Document>                                                                                                                                                                                        |
        |<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument/init(configuration:)>                |Separate <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader>                                                                                                                                                                         |
        |<doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument/fileWrapper(snapshot:configuration:)>|Separate <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter>                                                                                                                                                                         |
        |[`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) only                          |[`FileWrapper`](https://developer.apple.com/documentation/foundation/filewrapper) and URL access through <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader> and <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter>|
        |Single `Snapshot` type on <doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument>           |Separate `Snapshot` types on <doc://com.apple.documentation/documentation/SwiftUI/DocumentWriter> and <doc://com.apple.documentation/documentation/SwiftUI/DocumentReader>                                                                            |

The following example shows a complete text document before and after migrating from <doc://com.apple.documentation/documentation/SwiftUI/FileDocument>:

**Before:**

```swift
struct TextDocument: FileDocument {
    static let readableContentTypes: [UTType] = [.plainText]

    var text: String

    init(configuration: ReadConfiguration) throws {
        guard let data = configuration.file.regularFileContents,
              let string = String(data: data, encoding: .utf8)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }
        text = string
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let data = Data(text.utf8)
        return FileWrapper(regularFileWithContents: data)
    }
}

struct TextDocumentApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: TextDocument()) { file in
            TextEditor(text: file.$document.text)
        }
    }
}
```

**After:**

```swift
@Observable
final class TextDocument: Document {
    static let readableContentTypes: [UTType] = [.plainText]

    var text: String

    init(text: String = "") {
        self.text = text
    }

    func reader(configuration: sending ReadConfiguration) -> sending FileWrapperDocumentReader<String> {
        FileWrapperDocumentReader(configuration) { fileWrapper in
            guard let data = fileWrapper.regularFileContents else {
                throw CocoaError(.fileReadCorruptFile)
            }
            return String(decoding: data, as: UTF8.self)
        }
    }

    func writer(configuration: sending WriteConfiguration) -> sending FileWrapperDocumentWriter<String> {
        FileWrapperDocumentWriter(configuration) { snapshot in
            FileWrapper(
                regularFileWithContents: Data(snapshot.utf8)
            )
        }
    }

    @MainActor
    func snapshot(contentType: UTType) async throws -> sending String {
        text
    }

    @MainActor
    func apply(snapshot: sending String, previous: sending String?) async throws {
        text = snapshot
    }
}

struct TextDocumentApp: App {
    var body: some Scene {
        DocumentGroup { document in
            TextDocumentView(document: document)
        } makeDocument: { _, _ in
            TextDocument()
        }
    }
}

struct TextDocumentView: View {
    @Bindable var document: TextDocument
    @Environment(\.undoManager) private var undoManager

    var body: some View {
        TextEditor(text: $document.text)
            .onChange(of: document.text) { oldValue, _ in
                undoManager?.registerUndo(
                    withTarget: document
                ) { document in
                    document.text = oldValue
                }
            }
    }
}
```

The key structural change is the shift from value to reference semantics. With <doc://com.apple.documentation/documentation/SwiftUI/FileDocument>, SwiftUI tracks mutations through the [`Binding`](https://developer.apple.com/documentation/swiftui/binding) to the structure and manages undo automatically. With <doc://com.apple.documentation/documentation/SwiftUI/Document>, you use [`@Observable`](https://developer.apple.com/documentation/observation/observable) for change tracking and register undo actions explicitly — but gain async I/O, URL-based file access, and clear separation between state capture and serialization.

The following example shows a complete text document before and after migrating from <doc://com.apple.documentation/documentation/SwiftUI/ReferenceFileDocument>:

**Before:**

```swift
final class OldTextDocument: ReferenceFileDocument {
    typealias Snapshot = String

    static let readableContentTypes = [UTType.utf8PlainText]

    @Published var text: String

    init() {
        text = ""
    }

    required init(configuration: ReadConfiguration) throws {
        if let data = configuration.file.regularFileContents {
            text = String(data: data, encoding: .utf8) ?? ""
        } else {
            text = ""
        }
    }

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

    func fileWrapper(
        snapshot: String, configuration: WriteConfiguration
    ) throws -> FileWrapper {
        let data = snapshot.data(using: .utf8) ?? Data()
        return FileWrapper(regularFileWithContents: data)
    }
}
```

**After:**

```swift
@Observable
final class TextDocument: Document {
    static let readableContentTypes = [UTType.utf8PlainText]

    var text: String

    init() {
        text = ""
    }

    func reader(configuration: sending ReadConfiguration) -> sending FileWrapperDocumentReader<String> {
        FileWrapperDocumentReader(configuration) { fileWrapper in
            if let data = fileWrapper.regularFileContents,
               let text = String(data: data, encoding: .utf8) {
                return text
            }
            return ""
        }
    }

    @MainActor
    func apply(snapshot: sending String, previous: sending String?) async throws {
        text = snapshot
    }

    func writer(configuration: sending WriteConfiguration) -> sending FileWrapperDocumentWriter<String> {
        FileWrapperDocumentWriter(configuration) { snapshot, previous in
            let data = snapshot.data(using: .utf8) ?? Data()
            return FileWrapper(regularFileWithContents: data)
        }
    }

    @MainActor
    func snapshot(contentType: UTType) async throws -> sending String {
        text
    }
}

struct TextDocumentView: View {
    @Bindable var document: TextDocument
    @Environment(\.undoManager) private var undoManager

    var body: some View {
        TextEditor(text: $document.text)
            .onChange(of: document.text) { oldValue, _ in
                undoManager?.registerUndo(
                    withTarget: document
                ) { document in
                    document.text = oldValue
                }
            }
    }
}
```

The migrated document cleanly separates concerns, supports URL access for advanced use cases, adopts the Observation framework, and integrates Swift concurrency.

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)