In a SwiftUI Document App, how to save a document from within a function
Asked Answered
S

1

8

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.

Sirmons answered 25/7, 2021 at 21:5 Comment(0)
S
3

Following a similar procedure to https://mcmap.net/q/324853/-toggle-sidebar-in-swiftui-navigationview-on-macos, 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.

Sirmons answered 28/7, 2021 at 15:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.