2023-01-16

Document is not being marked dirty when element is being changed programmatically

What is wrong with this SwiftUI code?

When the button "Reset Text" is pressed the document data is changed (as seen in the debugger), but the view is not updated.

If the document is then saved, the modified data is not saved. The method "func fileWrapper" is not called because it does not think that the field "text" has changed.

If characters are typed in the TextEditor so the field is dirty and then saved, the data is saved.


If characters are types in the TextEditor so the field is dirty, then the "Reset Text" button pressed, the TextEditor view is not updated, but the "Reset Text" is saved.

If I wait a while 1 to 3 minutes, the TextEditor will sometimes update to the new text.

    import SwiftUI
    import UniformTypeIdentifiers
    
    @main
    struct DocumentAppApp: App {
        var body: some Scene {
            DocumentGroup(newDocument: DocumentAppDocument()) { file in
            ContentView(document: file.$document)
            }
        }
    }

    extension UTType {
        static var exampleText: UTType {
            UTType(importedAs: "com.example.plain-text")
        }
    }
    
    class DocumentAppDocument: ObservableObject, FileDocument {
        @Published var text: String
    
        init(text: String = "This is some text") {
            self.text = text
        }
    
        static var readableContentTypes: [UTType] { [.exampleText] }
        
        required 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 = text.data(using: .utf8)!
            return .init(regularFileWithContents: data)
        }
    }

    struct ContentView: View {
        @Binding var document: DocumentAppDocument
    
        var body: some View {
            VStack {
                TextEditor(text: $document.text)
                Button(action: {
    //              document.objectWillChange.send()
                    document.text = "Reset Text"
                }, label: { Text("Reset Text") })
                .frame(width: 200, height: 50)
                .border(Color.black)
                .padding()
            }
        }
    }


I tried added a call to document.objectWillChange.send() and it made no difference.

I tried as both a struct and a class

I've tried various combinations of @State, @StateObject and @Binding.

It looks like I could call .save on the DocumentGroup, but I couldn't figure out how to get ahold of it.

Test case

Create a new document.

Type in some text -> test is there

Save document.

Open document -> text is there.

Press "Reset Text" Button. Nothing happens.

Save.

Open -> previous text is there.

Type some text, press button -> nothing happens.

Save.

Open -> "Reset Text: text is present.



No comments:

Post a Comment