SettingsAccess

repository·main·Indexed 41 days ago

https://github.com/orchetect/settingsaccess

A SwiftUI library for programmatically opening the macOS Settings scene. It provides the `openSettingsLegacy()` method to overcome limitations in macOS 14 and a custom `SettingsLink` initializer for use in `MenuBarExtra` menus, allowing for pre- and post-action execution.

Tokens
1.5K
Snippets
4
Records
5
Agent score
50%

What's inside SettingsAccess

  1. Limitations of SettingsAccess

    main

    Users should be aware of the following constraints:

    • SwiftUI Context Required: SettingsAccess only works within a SwiftUI context. It requires at least one SwiftUI view to be instantiated and its body to be invoked. It cannot be used globally.
    • MenuBarExtra Restriction: openSettingsLegacy() is not usable within a menu-based MenuBarExtra due to SwiftUI limitations. In these cases, use the custom SettingsLink initializer with preAction and postAction closures instead.
  2. Install SettingsAccess via Swift Package Manager

    main

    Add SettingsAccess as a dependency to your Xcode project or Swift Package.

    In an Xcode project:

    1. Select File → Swift Packages → Add Package Dependency...
    2. Enter the URL: https://github.com/orchetect/SettingsAccess

    In a Package.swift file: Add the following to your dependencies array:

    .package(url: "https://github.com/orchetect/SettingsAccess", from: "2.1.0")
  3. Open the Settings scene programmatically using `openSettingsLegacy()`

    main

    To call openSettingsLegacy() from any subview, you must first attach the .openSettingsAccess() view modifier to a base view in your view hierarchy (typically the root view of your WindowGroup).

    Step 1: Attach the modifier to your App's root view

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .openSettingsAccess()
            }
            
            Settings { SettingsView() }
        }
    }

    Step 2: Use the environment method in a subview

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

    Note on Xcode 16+: Xcode 16 makes Apple's openSettings environment method public. For projects targeting macOS 14+, you may use the native openSettings. Use openSettingsLegacy() for compatibility with older macOS versions.

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .openSettingsAccess()
            }
            
            Settings { SettingsView() }
        }
    }
    
    struct ContentView: View {
        @Environment(\.openSettingsLegacy) private var openSettingsLegacy
      
        var body: some View {
            Button("Open Settings") { try? openSettingsLegacy() }
        }
    }
  4. Wire up an 'About' menu item to open a specific Settings tab

    main

    You can replace the standard 'About' menu item to open the Settings window at a specific tab by using SettingsLink with a preAction that updates a shared state (like @AppStorage).

    Example Implementation:

    1. Define a shared state (e.g., selectedSettingsPage) using @AppStorage.
    2. In your CommandGroup, use SettingsLink to set that state in the preAction closure.
    3. In your Settings view, use a TabView bound to that same state.
    @main
    struct MyApp: App {
        @AppStorage("selectedSettingsPage") private var selectedSettingsPage: SettingsPage = .general
        
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
            .commands {
                CommandGroup(replacing: .appInfo) {
                    SettingsLink {
                        Text("About...")
                    } preAction: {
                        selectedSettingsPage = .about
                    } postAction: {
                        // none
                    }
                }
            }
            
            Settings { SettingsView() }
        }
    }
    
    enum SettingsPage: String {
        case general
        case about
    }
    
    struct SettingsView: View {
        @AppStorage("selectedSettingsPage") private var selectedSettingsPage: SettingsPage = .general
        
        var body: some View {
            TabView(selection: $selectedSettingsPage) {
                GeneralView()
                    .tabItem { Label("General", systemImage: "gear") }
                    .tag(SettingsPage.general)
                    
                AboutView()
                    .tabItem { Label("About", systemImage: "info.bubble.fill") }
                    .tag(SettingsPage.about)
            }
        }
    }
  5. Use custom `SettingsLink` in a `MenuBarExtra` menu

    main

    Due to SwiftUI limitations, openSettingsLegacy() cannot be used within a menu-based MenuBarExtra. Instead, use the custom SettingsLink initializer, which allows you to execute code before or after the Settings scene opens.

    struct AppMenuView: View {
        var body: some View {
            SettingsLink {
                Text("Settings...")
            } preAction: {
                // code to run before Settings opens
            } postAction: {
                // code to run after Settings opens
            }
            
            Button("Quit") { NSApp.terminate(nil) }
        }
    }