How vorssaint-utils Handles macOS Accessibility Permissions: Inside the TCC Singleton
vorssaint-utils centralizes all macOS Transparency, Consent, and Control (TCC) permission handling in a single Permissions singleton that live-polls system state, prompts users once per TCC reset, and automatically updates SwiftUI views via @Published properties.
Managing macOS Accessibility permissions in a Swift macOS application requires careful coordination between system APIs, user interface state, and background service availability. The vorssaint-utils repository solves this challenge through a centralized permission architecture that abstracts the complexity of macOS TCC into a reactive, observable singleton. This approach ensures that features requiring Accessibility access—such as scroll inversion and window activation—remain synchronized with the system's current permission grant status.
Centralized Permission Management in Permissions.swift
The ObservableObject Pattern
At the core of vorssaint-utils lies the Permissions class in Sources/Vorssaint/Core/Permissions.swift, which conforms to ObservableObject to provide reactive state management. The singleton exposes published properties including accessibility and screenRecording that automatically propagate changes to SwiftUI views throughout the application. This design eliminates the need for scattered permission checks across view models and service layers.
Live Polling Architecture
When active features require Accessibility access, the permissions system enters a polling state via scheduleActivePermissionPolling(). This method creates a Timer whose interval is dynamically computed from desiredPollInterval based on currently required features. The timer fires only when permission-aware features become active, minimizing system overhead while ensuring real-time accuracy.
Fast Permission Checks and System Integration
Low-Overhead State Verification
The refreshActivePermissions() method performs lightweight permission validation using native macOS APIs. For Accessibility status, the implementation calls AXIsProcessTrusted(), while screen recording checks use CGPreflightScreenCaptureAccess(). These calls execute on the main queue and update the published properties, triggering immediate UI updates when permission states change.
User-Initiated Permission Requests
When users enable features requiring Accessibility access through the UI, the system invokes requestAccessibility(). This method constructs a dictionary containing kAXTrustedCheckOptionPrompt and passes it to AXIsProcessTrustedWithOptions(), displaying the system prompt once per TCC reset. If the user denies access, the system presents PermissionGuideOverlay to provide contextual guidance.
System Settings Integration and Feature Gating
Deep-Linking to Privacy Preferences
The openAccessibilitySettings() method streamlines manual permission granting by constructing a URL with the scheme x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility and opening it via NSWorkspace. This deep-link navigates users directly to the Accessibility pane in Privacy & Security settings, reducing friction in the permission workflow.
Service-Level Feature Gating
Throughout the codebase, services guard their functionality using Permissions.shared.accessibility. For example, ScrollInverter.swift and WindowActivator.swift check this property before executing Accessibility-dependent operations. In SettingsView.swift, UI toggles for brightness and OSD features automatically trigger requestAccessibility() when users attempt to enable them, ensuring the system prompt appears at the point of need.
Implementation Example
// Check current permission status
if Permissions.shared.accessibility {
// Execute Accessibility-dependent logic
scrollInverter.enable()
}
// Request permission with system prompt
Permissions.shared.requestAccessibility()
// Open System Settings if previously denied
Permissions.shared.openAccessibilitySettings()
These patterns appear throughout the codebase, particularly in SettingsView.swift where brightness toggles invoke permission requests, and in service files that guard core logic with Permissions.shared.accessibility checks.
Summary
- vorssaint-utils consolidates macOS TCC management in the
Permissionssingleton located atSources/Vorssaint/Core/Permissions.swift - The system uses
AXIsProcessTrusted()for fast checks andAXIsProcessTrustedWithOptions()for user prompts, updating@Publishedproperties reactively - Live polling via
scheduleActivePermissionPolling()ensures real-time state synchronization without excessive resource consumption - Deep-link integration via
openAccessibilitySettings()directs users straight to Privacy & Security preferences - Services like
ScrollInverterandWindowActivatorgate functionality usingPermissions.shared.accessibilitychecks
Frequently Asked Questions
How does vorssaint-utils check if Accessibility permissions are granted?
The system calls AXIsProcessTrusted() in the refreshActivePermissions() method to perform lightweight, non-blocking permission checks. This updates the @Published properties, triggering reactive UI updates across the application.
What happens when a user denies the Accessibility permission prompt?
If requestAccessibility() detects an ungranted state after prompting, it displays PermissionGuideOverlay to guide users toward manually enabling the permission. Users can then invoke openAccessibilitySettings(), which launches System Settings directly to the Accessibility pane.
How does the permission system handle app lifecycle events?
The Permissions initializer registers for NSApplication.didBecomeActiveNotification and UserDefaults.didChangeNotification, triggering refresh() when the app returns from background or when relevant preferences change. This ensures UI state remains synchronized with system permissions when users return from manually adjusting settings.
Which vorssaint-utils features require macOS Accessibility permissions?
Features including the scroll inverter (ScrollInverter.swift), window activation (WindowActivator.swift), and brightness/OSD controls all depend on Accessibility access. These components check Permissions.shared.accessibility before executing and automatically request permission when users enable related toggles in SettingsView.swift.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →