A macOS TCA dependency for managing launch-at-login registration.
LaunchAtLoginClient provides a testable interface for registering and
unregistering the app as a login item via SMAppService.mainApp. Includes
a ready-made SwiftUI Toggle view for settings screens.
Based on LaunchAtLogin-Modern
by Sindre Sorhus. Includes error logging via os.Logger.
@Reducer
struct SettingsFeature {
@ObservableState
struct State: Equatable {
var launchAtLogin: Bool = false
}
enum Action: BindableAction {
case binding(BindingAction<State>)
case onAppear
case toggleLaunchAtLogin
}
@Dependency(\.launchAtLogin) var launchAtLogin
var body: some ReducerOf<Self> {
BindingReducer()
Reduce { state, action in
switch action {
case .onAppear:
state.launchAtLogin = launchAtLogin.isEnabled()
return .none
case .toggleLaunchAtLogin:
state.launchAtLogin.toggle()
try? launchAtLogin.setEnabled(state.launchAtLogin)
return .none
case .binding:
return .none
}
}
}
}For simple settings screens, use the built-in toggle view that manages state automatically:
// With default "Launch at login" label:
LaunchAtLoginClient.Toggle()
// With custom label:
LaunchAtLoginClient.Toggle {
Text("Open at login")
}The toggle reads the current registration state on appear and calls
setEnabled on every change.
Determine if the current launch was triggered by the login item mechanism
(must be checked in NSApplicationDelegate.applicationDidFinishLaunching):
@Dependency(\.launchAtLogin) var launchAtLogin
func applicationDidFinishLaunching(_ notification: Notification) {
if launchAtLogin.wasLaunchedAtLogin() {
// App was opened automatically at login
}
}let store = TestStore(initialState: SettingsFeature.State()) {
SettingsFeature()
} withDependencies: {
$0.launchAtLogin = .noop
}Or with a custom mock:
$0.launchAtLogin = LaunchAtLoginClient(
isEnabled: { true },
setEnabled: { _ in },
wasLaunchedAtLogin: { false }
)| Property | Type | Description |
|---|---|---|
isEnabled |
@Sendable () -> Bool |
Whether the app is registered to launch at login |
setEnabled |
@Sendable (Bool) throws -> Void |
Enables or disables launch-at-login registration |
wasLaunchedAtLogin |
@Sendable () -> Bool |
Whether the current launch was triggered by the login item |
| View | Description |
|---|---|
LaunchAtLoginClient.Toggle |
A toggle that manages launch-at-login state automatically |
LaunchAtLoginClient.Toggle(_:) |
Convenience initializer with LocalizedStringKey label (defaults to "Launch at login") |
| Platform | Support |
|---|---|
| macOS | Full support via SMAppService.mainApp |
| Mac Catalyst | Full support via SMAppService.mainApp |
| iOS | Not available (module does not compile) |
| tvOS | Not available (module does not compile) |
| watchOS | Not available (module does not compile) |
- Backend: Uses
SMAppService.mainApp(ServiceManagement framework) for registration. The live implementation unregisters before re-registering to avoid stale state. - wasLaunchedAtLogin: Must be checked during
applicationDidFinishLaunching. UsesNSAppleEventManagerto inspect the launch Apple Event forkeyAELaunchedAsLogInItem. - Sandboxing:
SMAppServiceworks in both sandboxed and non-sandboxed apps. No entitlements required.