Markus Moenig
Markus Moenig

Reputation: 1904

Document based Apps, how to get access to the Document from within a menu?

I am using the new MultiPlatform SwiftUI Document template in Xcode 12 and I don't understand how to get access to the current FileDocument from within a menu item.

My app code looks like this (straight from the template).

@main
struct MyApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { file in
            ContentView(document: file.$document)
        }
        .commands {
            CommandMenu("Utilities") {
                Button(action: {
                    // How to get access to the current FileDocument ?
                }) {
                    Text("Test")
                }
            }
        }
    }
}

Upvotes: 4

Views: 1992

Answers (3)

Gareth Redman
Gareth Redman

Reputation: 66

You can create a FocusedValueKey, publish a binding to the document from your content view, and then observe it in the menu using @FocusedBinding. There is a great explanation here.

Upvotes: 5

Markus Moenig
Markus Moenig

Reputation: 1904

To answer my own question, this is how I solved it. You can just send a signal from the .commands section via Combine.

@main
struct MyApp: App {

    private let exportAsImage = PassthroughSubject<Void, Never>()

    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { file in
            ContentView(document: file.$document)
                .onReceive(exportAsImage) { _ in
                    file.document.exportAsImage()
                }
        }
        .commands {
            CommandGroup(replacing: .importExport) {
                Button(action: {
                    exportAsImage.send()
                }) {
                    Text("Export as Image...")
                }
            }
        }
    }
}

Upvotes: -1

Asperi
Asperi

Reputation: 258365

Here is a demo of possible approach. The idea is to use app state object to store current document in it and access from any place needed.

Tested with Xcode 12 / iOS 14

@main
struct MyApp: App {
    @StateObject var appState = AppState()

    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { file in
            createView(for: file)
        }
        .commands {
            CommandMenu("Utilities") {
                Button(action: {
                    if let doc = appState.currentDocument {
                        // do something
                    }
                }) {
                    Text("Test")
                }
            }
        }
    }

    private func createView(for file: FileDocumentConfiguration<MyDocument>) -> some View {
        appState.currentDocument = file.document
        return ContentView(document: file.document)
    }
}

class AppState: ObservableObject {
    @Published var currentDocument: MyDocument?
}

Upvotes: -1

Related Questions