add launch-at-login setting via SMAppService

Checkbox registers/unregisters SMAppService.mainApp (macOS 13+ API, no
helper app needed). Deliberately not mirrored in UserDefaults: the
service status is the single source of truth, and syncUI reads it live
so changes made in System Settings > Login Items stay in sync. Failures
revert the checkbox and surface the error in an alert.

Note: registration points at the app bundle's current path, so it only
behaves properly once BrewBar runs from a stable location like
/Applications — debug builds may register the build folder copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
maxsoch 2026-07-06 22:05:14 +02:00
parent 98d8cbe2a9
commit 617e07d53d

View file

@ -1,4 +1,5 @@
import Cocoa import Cocoa
import ServiceManagement
class SettingsWindowController: NSWindowController { class SettingsWindowController: NSWindowController {
weak var appDelegate: AppDelegate? weak var appDelegate: AppDelegate?
@ -13,6 +14,7 @@ class SettingsWindowController: NSWindowController {
var intervalPopup: NSPopUpButton! var intervalPopup: NSPopUpButton!
var customMinutesField: NSTextField! var customMinutesField: NSTextField!
var launchAtLoginCheckbox: NSButton!
convenience init(appDelegate: AppDelegate) { convenience init(appDelegate: AppDelegate) {
let window = NSWindow( let window = NSWindow(
@ -66,9 +68,17 @@ class SettingsWindowController: NSWindowController {
]) ])
customRow.orientation = .horizontal customRow.orientation = .horizontal
launchAtLoginCheckbox = NSButton(
checkboxWithTitle: "Launch BrewBar at login",
target: self,
action: #selector(toggleLaunchAtLogin(_:))
)
let stack = NSStackView(views: [ let stack = NSStackView(views: [
frequencyRow, frequencyRow,
customRow, customRow,
separator(),
launchAtLoginCheckbox,
]) ])
stack.orientation = .vertical stack.orientation = .vertical
stack.alignment = .leading stack.alignment = .leading
@ -99,6 +109,16 @@ class SettingsWindowController: NSWindowController {
intervalPopup.selectItem(at: presets.count) // Custom intervalPopup.selectItem(at: presets.count) // Custom
} }
customMinutesField.stringValue = "\(Int(interval / 60))" customMinutesField.stringValue = "\(Int(interval / 60))"
// SMAppService is the source of truth the user may have changed
// this in System Settings > Login Items while we weren't looking
launchAtLoginCheckbox.state = SMAppService.mainApp.status == .enabled ? .on : .off
}
func separator() -> NSBox {
let box = NSBox()
box.boxType = .separator
return box
} }
// MARK: - Actions // MARK: - Actions
@ -124,4 +144,22 @@ class SettingsWindowController: NSWindowController {
appDelegate?.restartTimer() appDelegate?.restartTimer()
syncUI() syncUI()
} }
@objc func toggleLaunchAtLogin(_ sender: NSButton) {
do {
if sender.state == .on {
try SMAppService.mainApp.register()
} else {
try SMAppService.mainApp.unregister()
}
} catch {
sender.state = sender.state == .on ? .off : .on // revert on failure
let alert = NSAlert()
alert.alertStyle = .warning
alert.messageText = "Could not change login item"
alert.informativeText = error.localizedDescription
alert.runModal()
}
}
} }