Better SwiftUI Settings Scene Access on macOS

SettingsAccess

Why

As of macOS 14 Sonoma:

  • Apple completely removed the ability to open the SwiftUI Settings scene using legacy NSApp.sendAction() method using the showSettingsWindow: (macOS 13) or showPreferencesWindow: (macOS 12 and earlier) selectors. The only available method of opening the Settings scene apart from the App menu → Settings menu item is to use the new SettingsLink view.

  • This presents two major restrictions:

    1. SettingsLink is a view that wraps a standard SwiftUI Button. There is no way to detect when the user has clicked this button if additional code is desired to run in addition to the intrinsic behavior of opening the Settings scene. Due to how SwiftUI works, it is impossible to attach a simultaneous gesture to attempt to detect a button press.
    2. There is no way to programmatically open the Settings scene.
  • These restrictons become problematic in many scenarios. Some examples that are currently impossible without SettingsAccess:

    • You are building a window-based MenuBarExtra and want to have a button that opens Settings and also dismisses the window.
    • You want to open the Settings scene in reponse to a user action in your application that requires the user manipulate a setting that may be invalid.

Solution

  1. SettingsAccess provides a custom button style that can be applied directly to SettingsLink in order to execute code before and/or after the button press action occurs.
  2. SettingsAccess provides an environment method called openSettings that can be called anywhere in the view hierarchy to programmatically open the Settings scene.

In addition, SettingsAccess is backwards compatible from macOS 11 Big Sur and later. Calling openSettings() will use the correct method to open the Settings scene for each operating system automatically.

Using the Package

Swift Package Manager (SPM)

Add SettingsAccess as a dependency using Swift Package Manager.

  • In an app project or framework, in Xcode:

    Select the menu: File → Swift Packages → Add Package Dependency…

    Enter this URL: https://github.com/orchetect/SettingsAccess

  • In a Swift Package, add it to the Package.swift dependencies:

    .package(url: "https://github.com/orchetect/SettingsAccess", from: "1.0.0")

Getting Started

  1. Import the library.

    import SettingsAccess
  2. Attach the openSettingsAccess view modifier to the base view which needs access to the openSettings method.

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .openSettingsAccess()
            }
        }
    }
  3. In any subview where needed, add the environment method declaration. Then the Settings scene may be opened programmatically by calling this method.

    struct ContentView: View {
        @Environment(\.openSettings) private var openSettings
      
        var body: some View {
            Button("Open Settings") { openSettings() }
        }
    }

Example Code

Try the Demo example project to see the library in action.

Requirements

Supports macOS 11.0+.

Author

Coded by a bunch of ? hamsters in a trenchcoat that calls itself @orchetect.

License

Licensed under the MIT license. See LICENSE for details.

Sponsoring

If you enjoy using SettingsAccess and want to contribute to open-source financially, GitHub sponsorship is much appreciated. Feedback and code contributions are also welcome.

Contributions

Contributions are welcome. Posting in Discussions first prior to new submitting PRs for features or modifications is encouraged.

GitHub

View Github