The current version of Xcode (version 12.5.1) provides a template for a Document Based App for macOS providing the following document model:
struct MyDocument: FileDocument {
var text: String
init(text: String = "Hello, world!") {
self.text = text
}
static var readableContentTypes: [UTType] { [.exampleText] }
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)
}
}
I want to add a method to this struct that passes my document to an external program, also saving the document before doing so:
func passMyDocumentToProgram() {
// Save document
// Pass document to external program
}
The problem is I don't know how to save a document like this.
The resulting app (built from the template) provides functionality (in the menu bar) to save a document, so I should be calling this existing functionality somehow.
From my understanding, the fileWrapper
method in MyDocument
returns a FileWrapper
that has a write()
method that can be used to save the document; however, the fileWrapper
method requires a WriteConfiguration
, and I don't how to create this. The documentation for WriteConfiguration
is quite sparse and I have not been able to find anything fruitful online.
Update. The better question is how do I trigger my Document App to auto-save?
I figured out I can save my document with something like FileWrapper(regularFileWithContents: data).write( ... )
, but this is a bad idea because your app will give you an error saying an external program modified the file.
SwiftUI Document Apps written with the FileDocument
protocol auto-save their documents on certain events, like (un)focusing a window, so I'd like to know if there is a way I can trigger such an auto-save programatically.
Following a similar procedure to https://stackoverflow.com/a/68331797/16524160, we can try to get the implementation used by the Save...
menu entry. Looking at
let menu = NSApp.mainMenu!.items.first(where: { $0.title == "File" })!
let submenu = menu.submenu!.items.first(where: { $0.title == "Save…" })!
submenu.target // nil
submenu.action // saveDocument:
I came up with the following method for MyDocument
:
func save() {
NSApp.sendAction(#selector(NSDocument.save(_:)), to: nil, from: nil)
}
Note saveDocument
has been renamed to save
. From my understanding, this tries to send the save()
selector to the first object in the key window which can react to it. In my case, the key window will contain the document user is editing, so this will save the document.