I have a number of changes to a document-based application which are not undoable (conceptually, think quiz progress) but which should dirty the document so the state is saved when the user quits or closes the document.
Apple's documentation for ReferenceFileDocument states Changes to the document are observed and on change dirty the document state for resaving. but that does not happen for me. (I have tried both changes to a DocumentData class held by my document and to a variable directly within my ReferenceFileDocument subclass, but nothing happens; the document stays clean and does not autosave.)
NSDocument has an updateChangeCount
function which is all I want at this point.
All references to dirtying documents I have found involve an undo manager, so in my document class I have implemented
var isDirty: Bool = false
func dirtyDocument(undoManager: UndoManager?){
undoManager?.registerUndo(withTarget: self) { isDirty in
undoManager?.removeAllActions(withTarget: self)
}
if isDirty == false {
isDirty = true
}
}
and any View that makes a noteworthy change contains
@Environment(\.undoManager) var undoManager
and calls document.dirtyDocument(undoManager: undoManager)
This works in that it dirties the document, but leaves me with an enabled 'Undo' action which does nothing BUT which marks the document as 'not dirty' if the user invokes it, which is why I added undoManager?.removeAllActions(withTarget: self)
I now have the functionality I want, apart from not finding a way to disable the undo menu item for this 'undo action' but it still feels hackish. Is there a better way to emulate updateChangeCount
without invoking the UndoManager and and immediately discarding its changes?
I've come back to this issue because while the above works well enough, I wasn't satisfied.
Turns out that behind the scenes, both FileDocument and ReferenceFileDocument still use NSDocumentController (I have not found this in the documentation, it was just mentioned in passing in a discussion of FileDocument). Here I've used a button to dirty a document that otherwise does nothing.
struct ContentView: View {
@Binding var document: DocumentAppFromTemplateDocument
let documentController: NSDocumentController = .shared
var body: some View {
VStack{
//TextEditor(text: $document.text)
Text("App content goes here")
Button("Dirty the Doc"){
if let document = documentController.currentDocument {
document.updateChangeCount(.changeDone)
}
}
}
}
}
so we can proceed as before.
(MacOS 14.5, Xcode 16b)