Apply FocusedValue for Active macOS Scenes Only in SwiftUI

Apply FocusedValue for Active macOS Scenes Only in SwiftUI

FocusedValue, focusedSceneValue and commands can be used to improve user experiences like printing.

·

2 min read

You have just created the world's best Lyric writing app for macOS.

You get your first feature request; printing. Easy enough because you've heard you can add commands to your Scene:

struct ExampleApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: AppDocument()) { file in
            ContentView(document: file.$document)
        }
        .commands {
            CommandGroup(replacing: .printItem) {
                Button("Print...") {
                    // ...
                }
                .keyboardShortcut("p", modifiers: [.command])
            }
        }
    }
}

You even throw in a cheeky shortcut because you care about making the lives of your users easier.

But wait a second, how does the app know which document to print given a user can have multiple documents opened at the same time.

Please welcome focusedSceneValue(...) to the stage 👏

Let's get started:

  1. Create a Focused Value
extension FocusedValues {
    enum PrintValueKey: FocusedValueKey {
        typealias Value = () -> Void
    }

    var print: PrintValueKey.Value? {
        get { self[PrintValueKey.self] }
        set { self[PrintValueKey.self] = newValue }
    }
}
  1. Add the Focused Value to your App
struct ExampleApp: App {

    @FocusedValue(\.print) // <--- New
    private var print

    var body: some Scene {
        DocumentGroup(newDocument: AppDocument()) { file in
            ContentView(document: file.$document)
        }
        .commands {
            CommandGroup(replacing: .printItem) {
                Button("Print...") {
                    print?() // <--- New
                }
                .keyboardShortcut("p", modifiers: [.command])
                .disabled(print == nil) // <--- If no Scene is active, print is disabled.
            }
        }
    }
}
  1. Update your ContentView to print the document.
struct ContentView: View {

    @Binding
    var document: AppDocument

    var body: some View {
        TextEditor(text: $document.text)
            .focusedSceneValue(\.print) {
                print(document)
            }
    }
}

Try it out and you too can offer an incredible printing experience like Lyrcs ❤️